in resources/src/main/java/org/robolectric/res/android/CppAssetManager2.java [943:1183]
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();
// Check that the size of the entry header is at least as big as
// the desired ResTable_map_entry. Also verify that the entry
// 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;
ResTable_map map_entry = null; // = new ResTable_map(map.myBuf(), curOffset);
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;
for (; 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())) {
// Attributes, arrays, etc don't have a resource id as the name. They specify
// 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_.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 && map_entry.myOffset() != map_entry_end && parentEntryIndex != parentEntryCount) {
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();
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;
map_entry = new ResTable_map(map_entry.myBuf(), map_entry.myOffset() + 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 && map_entry.myOffset() != map_entry_end) {
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();
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;
map_entry = new ResTable_map(map_entry.myBuf(), map_entry.myOffset() + 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;
}