use of org.robolectric.res.android.ResourceTypes.ResTable_map in project robolectric by robolectric.
the class CppAssetManager2 method GetBag.
// Retrieves the best matching bag/map resource with ID `resid`.
// This method will resolve all parent references for this bag and merge keys with the child.
// To iterate over the keys, use the following idiom:
//
// final ResolvedBag* bag = asset_manager.GetBag(id);
// if (bag != null) {
// for (auto iter = begin(bag); iter != end(bag); ++iter) {
// ...
// }
// }
ResolvedBag GetBag(int resid, List<Integer> child_resids) {
// ATRACE_NAME("AssetManager::GetBag");
ResolvedBag cached_iter = cached_bags_.get(resid);
if (cached_iter != null) {
return cached_iter;
}
final Ref<FindEntryResult> entryRef = new Ref<>(null);
ApkAssetsCookie cookie = FindEntry(resid, (short) 0, /* density_override */
false, /* stop_at_first_match */
entryRef);
if (cookie.intValue() == kInvalidCookie) {
return null;
}
FindEntryResult entry = entryRef.get();
// was intended to be a map.
if (dtohs(entry.entry.size) < ResTable_map_entry.BASE_SIZEOF || (dtohs(entry.entry.flags) & ResourceTypes.ResTable_entry.FLAG_COMPLEX) == 0) {
// Not a bag, nothing to do.
return null;
}
// final ResTable_map_entry map = reinterpret_cast<final ResTable_map_entry*>(entry.entry);
// final ResTable_map map_entry =
// reinterpret_cast<final ResTable_map*>(reinterpret_cast<final byte*>(map) + map.size);
// final ResTable_map map_entry_end = map_entry + dtohl(map.count);
final ResTable_map_entry map = new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset());
int curOffset = map.myOffset() + map.size;
// = new ResTable_map(map.myBuf(), curOffset);
ResTable_map map_entry = null;
final int map_entry_end = curOffset + dtohl(map.count) * ResTable_map.SIZEOF;
if (curOffset < map_entry_end) {
map_entry = new ResTable_map(map.myBuf(), curOffset);
}
// Keep track of ids that have already been seen to prevent infinite loops caused by circular
// dependencies between bags
child_resids.add(resid);
final Ref<Integer> parent_resid = new Ref<>(dtohl(map.parent.ident));
if (parent_resid.get() == 0 || child_resids.contains(parent_resid.get())) {
// There is no parent or that a circular dependency exist, meaning there is nothing to
// inherit and we can do a simple copy of the entries in the map.
final int entry_count = (map_entry_end - curOffset) / ResTable_map.SIZEOF;
// util.unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
// malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag.Entry))))};
ResolvedBag new_bag = new ResolvedBag();
ResolvedBag.Entry[] new_entry = new_bag.entries = new Entry[entry_count];
int i = 0;
while (curOffset < map_entry_end) {
map_entry = new ResTable_map(map_entry.myBuf(), curOffset);
final Ref<Integer> new_key = new Ref<>(dtohl(map_entry.name.ident));
if (!is_internal_resid(new_key.get())) {
// other data, which would be wrong to change via a lookup.
if (entry.dynamic_ref_table.lookupResourceId(new_key) != NO_ERROR) {
System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", new_key.get(), resid));
return null;
}
}
Entry new_entry_ = new_entry[i] = new Entry();
new_entry_.cookie = cookie;
new_entry_.key = new_key.get();
new_entry_.key_pool = null;
new_entry_.type_pool = null;
new_entry_.style = resid;
new_entry_.value = map_entry.value.copy();
final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
new_entry_.value = valueRef.get();
if (err != NO_ERROR) {
System.err.println(String.format("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType, new_entry_.value.data, new_key.get()));
return null;
}
// ++new_entry;
++i;
final int size = dtohs(map_entry.value.size);
// curOffset += size + sizeof(*map)-sizeof(map->value);
curOffset += size + ResTable_map.SIZEOF - Res_value.SIZEOF;
}
new_bag.type_spec_flags = entry.type_flags;
new_bag.entry_count = entry_count;
ResolvedBag result = new_bag;
cached_bags_.put(resid, new_bag);
return result;
}
// In case the parent is a dynamic reference, resolve it.
entry.dynamic_ref_table.lookupResourceId(parent_resid);
// Get the parent and do a merge of the keys.
final ResolvedBag parent_bag = GetBag(parent_resid.get(), child_resids);
if (parent_bag == null) {
// Failed to get the parent that should exist.
System.err.println(String.format("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid.get(), resid));
return null;
}
// Create the max possible entries we can make. Once we construct the bag,
// we will realloc to fit to size.
final int max_count = parent_bag.entry_count + dtohl(map.count);
// util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
// malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
ResolvedBag new_bag = new ResolvedBag();
new_bag.entries = new Entry[max_count];
final ResolvedBag.Entry[] new_entry = new_bag.entries;
int newEntryIndex = 0;
// const ResolvedBag::Entry* parent_entry = parent_bag->entries;
int parentEntryIndex = 0;
// final ResolvedBag.Entry parent_entry_end = parent_entry + parent_bag.entry_count;
final int parentEntryCount = parent_bag.entry_count;
// The keys are expected to be in sorted order. Merge the two bags.
while (map_entry != null && curOffset != map_entry_end && parentEntryIndex != parentEntryCount) {
map_entry = new ResTable_map(map_entry.myBuf(), curOffset);
final Ref<Integer> child_keyRef = new Ref<>(dtohl(map_entry.name.ident));
if (!is_internal_resid(child_keyRef.get())) {
if (entry.dynamic_ref_table.lookupResourceId(child_keyRef) != NO_ERROR) {
System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", child_keyRef.get(), resid));
return null;
}
}
int child_key = child_keyRef.get();
Entry parent_entry = parent_bag.entries[parentEntryIndex];
if (parent_entry == null) {
parent_entry = new Entry();
}
if (child_key <= parent_entry.key) {
// Use the child key if it comes before the parent
// or is equal to the parent (overrides).
Entry new_entry_ = new_entry[newEntryIndex] = new Entry();
new_entry_.cookie = cookie;
new_entry_.key = child_key;
new_entry_.key_pool = null;
new_entry_.type_pool = null;
new_entry_.value = map_entry.value.copy();
new_entry_.style = resid;
final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
new_entry_.value = valueRef.get();
if (err != NO_ERROR) {
System.err.println(String.format("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType, new_entry_.value.data, child_key));
return null;
}
// ++map_entry;
curOffset += map_entry.value.size + ResTable_map.SIZEOF - Res_value.SIZEOF;
} else {
// Take the parent entry as-is.
// memcpy(new_entry, parent_entry, sizeof(*new_entry));
new_entry[newEntryIndex] = parent_entry.copy();
}
if (child_key >= parent_entry.key) {
// Move to the next parent entry if we used it or it was overridden.
// ++parent_entry;
++parentEntryIndex;
// parent_entry = parent_bag.entries[parentEntryIndex];
}
// Increment to the next entry to fill.
// ++new_entry;
++newEntryIndex;
}
// Finish the child entries if they exist.
while (map_entry != null && curOffset != map_entry_end) {
map_entry = new ResTable_map(map_entry.myBuf(), curOffset);
final Ref<Integer> new_key = new Ref<>(map_entry.name.ident);
if (!is_internal_resid(new_key.get())) {
if (entry.dynamic_ref_table.lookupResourceId(new_key) != NO_ERROR) {
System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", new_key.get(), resid));
return null;
}
}
Entry new_entry_ = new_entry[newEntryIndex] = new Entry();
new_entry_.cookie = cookie;
new_entry_.key = new_key.get();
new_entry_.key_pool = null;
new_entry_.type_pool = null;
new_entry_.value = map_entry.value.copy();
new_entry_.style = resid;
final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
new_entry_.value = valueRef.get();
if (err != NO_ERROR) {
System.err.println(String.format("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType, new_entry_.value.data, new_key.get()));
return null;
}
// ++map_entry;
curOffset += map_entry.value.size + ResTable_map.SIZEOF - Res_value.SIZEOF;
// ++new_entry;
++newEntryIndex;
}
// Finish the parent entries if they exist.
while (parentEntryIndex != parent_bag.entry_count) {
// Take the rest of the parent entries as-is.
// final int num_entries_to_copy = parent_entry_end - parent_entry;
// final int num_entries_to_copy = parent_bag.entry_count - parentEntryIndex;
// memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
Entry parentEntry = parent_bag.entries[parentEntryIndex];
new_entry[newEntryIndex] = parentEntry == null ? new Entry() : parentEntry.copy();
// new_entry += num_entries_to_copy;
++newEntryIndex;
++parentEntryIndex;
}
// Resize the resulting array to fit.
// final int actual_count = new_entry - new_bag.entries;
final int actual_count = newEntryIndex;
if (actual_count != max_count) {
// new_bag.reset(reinterpret_cast<ResolvedBag*>(realloc(
// new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
Entry[] resizedEntries = new Entry[actual_count];
System.arraycopy(new_bag.entries, 0, resizedEntries, 0, actual_count);
new_bag.entries = resizedEntries;
}
// Combine flags from the parent and our own bag.
new_bag.type_spec_flags = entry.type_flags | parent_bag.type_spec_flags;
new_bag.entry_count = actual_count;
ResolvedBag result2 = new_bag;
// cached_bags_[resid] = std::move(new_bag);
cached_bags_.put(resid, new_bag);
return result2;
}
use of org.robolectric.res.android.ResourceTypes.ResTable_map in project robolectric by robolectric.
the class ResTable method getBagLocked.
public int getBagLocked(int resID, Ref<bag_entry[]> outBag, Ref<Integer> outTypeSpecFlags) {
if (mError != NO_ERROR) {
return mError;
}
final int p = getResourcePackageIndex(resID);
final int t = Res_GETTYPE(resID);
final int e = Res_GETENTRY(resID);
if (p < 0) {
ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
if (t < 0) {
ALOGW("No type identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
// printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t);
PackageGroup grp = mPackageGroups.get(p);
if (grp == NULL) {
ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
final List<Type> typeConfigs = getOrDefault(grp.types, t, Collections.emptyList());
if (typeConfigs.isEmpty()) {
ALOGW("Type identifier 0x%x does not exist.", t + 1);
return BAD_INDEX;
}
final int NENTRY = typeConfigs.get(0).entryCount;
if (e >= (int) NENTRY) {
ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", e, (int) typeConfigs.get(0).entryCount);
return BAD_INDEX;
}
// First see if we've already computed this bag...
TypeCacheEntry cacheEntry = grp.typeCacheEntries.editItemAt(t);
bag_set[] typeSet = cacheEntry.cachedBags;
// Bag not found, we need to compute it!
if (!isTruthy(typeSet)) {
// (bag_set**)calloc(NENTRY, sizeof(bag_set*));
typeSet = new bag_set[NENTRY];
// cacheEntry.cachedBags = typeSet;
}
if (kDebugTableNoisy) {
ALOGI("Building bag: %x\n", resID);
}
// Now collect all bag attributes
Entry entry = new Entry();
int err = getEntry(grp, t, e, mParams, entry);
if (err != NO_ERROR) {
return err;
}
final short entrySize = dtohs(entry.entry.size);
// const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
// ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
// const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
// ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
ResTable_map_entry mapEntry = entrySize >= ResTable_map_entry.BASE_SIZEOF ? new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset()) : null;
final int parent = mapEntry != null ? dtohl(mapEntry.parent.ident) : 0;
final int count = mapEntry != null ? dtohl(mapEntry.count) : 0;
int N = count;
if (kDebugTableNoisy) {
ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count);
// If this map inherits from another, we need to start
// with its parent's values. Otherwise start out empty.
ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent);
}
// This is what we are building.
bag_set set;
if (isTruthy(parent)) {
final Ref<Integer> resolvedParent = new Ref<>(parent);
// Bags encode a parent reference without using the standard
// Res_value structure. That means we must always try to
// resolve a parent reference in case it is actually a
// TYPE_DYNAMIC_REFERENCE.
err = grp.dynamicRefTable.lookupResourceId(resolvedParent);
if (err != NO_ERROR) {
ALOGE("Failed resolving bag parent id 0x%08x", parent);
return UNKNOWN_ERROR;
}
final Ref<bag_entry[]> parentBag = new Ref<>(null);
final Ref<Integer> parentTypeSpecFlags = new Ref<>(0);
final int NP = getBagLocked(resolvedParent.get(), parentBag, parentTypeSpecFlags);
final int NT = ((NP >= 0) ? NP : 0) + N;
set = new bag_set(NT);
if (NP > 0) {
set.copyFrom(parentBag.get(), NP);
set.numAttrs = NP;
if (kDebugTableNoisy) {
ALOGI("Initialized new bag with %d inherited attributes.\n", NP);
}
} else {
if (kDebugTableNoisy) {
ALOGI("Initialized new bag with no inherited attributes.\n");
}
set.numAttrs = 0;
}
set.availAttrs = NT;
set.typeSpecFlags = parentTypeSpecFlags.get();
} else {
set = new bag_set(N);
set.numAttrs = 0;
set.availAttrs = N;
set.typeSpecFlags = 0;
}
set.typeSpecFlags |= entry.specFlags;
// Now merge in the new attributes...
// int curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
// + dtohs(entry.entry.size);
int curOff = entry.entry.myOffset() - entry.type.myOffset() + entry.entry.size;
ResTable_map map;
// bag_entry* entries = (bag_entry*)(set+1);
bag_entry[] entries = set.bag_entries;
int curEntry = 0;
int pos = 0;
if (kDebugTableNoisy) {
ALOGI("Starting with set %s, entries=%s, avail=0x%x\n", set, entries, set.availAttrs);
}
while (pos < count) {
if (kDebugTableNoisy) {
// ALOGI("Now at %s\n", curOff);
ALOGI("Now at %s\n", curEntry);
}
if (curOff > (dtohl(entry.type.header.size) - ResTable_map.SIZEOF)) {
ALOGW("ResTable_map at %d is beyond type chunk data %d", (int) curOff, dtohl(entry.type.header.size));
return BAD_TYPE;
}
// map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
map = new ResTable_map(entry.type.myBuf(), entry.type.myOffset() + curOff);
N++;
final Ref<Integer> newName = new Ref<>(htodl(map.name.ident));
if (!Res_INTERNALID(newName.get())) {
// other data, which would be wrong to change via a lookup.
if (grp.dynamicRefTable.lookupResourceId(newName) != NO_ERROR) {
ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x", (int) curEntry, (int) newName.get());
return UNKNOWN_ERROR;
}
}
boolean isInside;
int oldName = 0;
while ((isInside = (curEntry < set.numAttrs)) && (oldName = entries[curEntry].map.name.ident) < newName.get()) {
if (kDebugTableNoisy) {
ALOGI("#0x%x: Keeping existing attribute: 0x%08x\n", curEntry, entries[curEntry].map.name.ident);
}
curEntry++;
}
if ((!isInside) || oldName != newName.get()) {
// This is a new attribute... figure out what to do with it.
if (set.numAttrs >= set.availAttrs) {
// Need to alloc more memory...
final int newAvail = set.availAttrs + N;
// set = (bag_set[])realloc(set,
// sizeof(bag_set)
// + sizeof(bag_entry)*newAvail);
set.resizeBagEntries(newAvail);
set.availAttrs = newAvail;
// entries = (bag_entry*)(set+1);
entries = set.bag_entries;
if (kDebugTableNoisy) {
ALOGI("Reallocated set %s, entries=%s, avail=0x%x\n", set, entries, set.availAttrs);
}
}
if (isInside) {
// Going in the middle, need to make space.
// memmove(entries+curEntry+1, entries+curEntry,
// sizeof(bag_entry)*(set.numAttrs-curEntry));
System.arraycopy(entries, curEntry, entries, curEntry + 1, set.numAttrs - curEntry);
entries[curEntry] = null;
set.numAttrs++;
}
if (kDebugTableNoisy) {
ALOGI("#0x%x: Inserting new attribute: 0x%08x\n", curEntry, newName.get());
}
} else {
if (kDebugTableNoisy) {
ALOGI("#0x%x: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
}
}
bag_entry cur = entries[curEntry];
if (cur == null) {
cur = entries[curEntry] = new bag_entry();
}
cur.stringBlock = entry._package_.header.index;
cur.map.name.ident = newName.get();
// cur->map.value.copyFrom_dtoh(map->value);
cur.map.value = map.value;
final Ref<Res_value> valueRef = new Ref<>(cur.map.value);
err = grp.dynamicRefTable.lookupResourceValue(valueRef);
cur.map.value = map.value = valueRef.get();
if (err != NO_ERROR) {
ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur.map.value.data);
return UNKNOWN_ERROR;
}
if (kDebugTableNoisy) {
ALOGI("Setting entry #0x%x %s: block=%d, name=0x%08d, type=%d, data=0x%08x\n", curEntry, cur, cur.stringBlock, cur.map.name.ident, cur.map.value.dataType, cur.map.value.data);
}
// On to the next!
curEntry++;
pos++;
final int size = dtohs(map.value.size);
// curOff += size + sizeof(*map)-sizeof(map->value);
curOff += size + ResTable_map.SIZEOF - Res_value.SIZEOF;
}
;
if (curEntry > set.numAttrs) {
set.numAttrs = curEntry;
}
// And this is it...
typeSet[e] = set;
if (isTruthy(set)) {
if (outTypeSpecFlags != NULL) {
outTypeSpecFlags.set(set.typeSpecFlags);
}
outBag.set(set.bag_entries);
if (kDebugTableNoisy) {
ALOGI("Returning 0x%x attrs\n", set.numAttrs);
}
return set.numAttrs;
}
return BAD_INDEX;
}
Aggregations