use of org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry in project robolectric by robolectric.
the class AttributeResolution10 method ResolveAttrs.
// These are all variations of the same method. They each perform the exact same operation,
// but on various data sources. I *think* they are re-written to avoid an extra branch
// in the inner loop, but after one branch miss (some pointer != null), the branch predictor should
// predict the rest of the iterations' branch correctly.
// TODO(adamlesinski): Run performance tests against these methods and a new, single method
// that uses all the sources and branches to the right ones within the inner loop.
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
public static boolean ResolveAttrs(Theme theme, int def_style_attr, int def_style_res, int[] src_values, int src_values_length, int[] attrs, int attrs_length, int[] out_values, int[] out_indices) {
if (kDebugStyles) {
ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res);
}
CppAssetManager2 assetmanager = theme.GetAssetManager();
ResTable_config config = new ResTable_config();
Res_value value;
int indicesIdx = 0;
// Load default style from attribute, if specified...
final Ref<Integer> def_style_flags = new Ref<>(0);
if (def_style_attr != 0) {
final Ref<Res_value> valueRef = new Ref<>(null);
if (theme.GetAttribute(def_style_attr, valueRef, def_style_flags).intValue() != kInvalidCookie) {
value = valueRef.get();
if (value.dataType == Res_value.TYPE_REFERENCE) {
def_style_res = value.data;
}
}
}
// Retrieve the default style bag, if requested.
ResolvedBag default_style_bag = null;
if (def_style_res != 0) {
default_style_bag = assetmanager.GetBag(def_style_res);
if (default_style_bag != null) {
def_style_flags.set(def_style_flags.get() | default_style_bag.type_spec_flags);
}
}
BagAttributeFinder def_style_attr_finder = new BagAttributeFinder(default_style_bag);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
int destOffset = 0;
for (int ii = 0; ii < attrs_length; ii++) {
final int cur_ident = attrs[ii];
if (kDebugStyles) {
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
ApkAssetsCookie cookie = K_INVALID_COOKIE;
int type_set_flags = 0;
value = Res_value.NULL_VALUE;
config.density = 0;
// Retrieve the current input value if available.
if (src_values_length > 0 && src_values[ii] != 0) {
value = new Res_value((byte) Res_value.TYPE_ATTRIBUTE, src_values[ii]);
if (kDebugStyles) {
ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
}
} else {
final Entry entry = def_style_attr_finder.Find(cur_ident);
if (entry != null) {
cookie = entry.cookie;
type_set_flags = def_style_flags.get();
value = entry.value;
if (kDebugStyles) {
ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
int resid = 0;
final Ref<Res_value> valueRef = new Ref<>(value);
final Ref<Integer> residRef = new Ref<>(resid);
final Ref<Integer> type_set_flagsRef = new Ref<>(type_set_flags);
final Ref<ResTable_config> configRef = new Ref<>(config);
if (value.dataType != Res_value.TYPE_NULL) {
// Take care of resolving the found resource to its final value.
ApkAssetsCookie new_cookie = theme.ResolveAttributeReference(cookie, valueRef, configRef, type_set_flagsRef, residRef);
if (new_cookie.intValue() != kInvalidCookie) {
cookie = new_cookie;
}
if (kDebugStyles) {
ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
}
} else if (value.data != Res_value.DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
ApkAssetsCookie new_cookie = theme.GetAttribute(cur_ident, valueRef, type_set_flagsRef);
if (new_cookie.intValue() != kInvalidCookie) {
if (kDebugStyles) {
ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
new_cookie = assetmanager.ResolveReference(new_cookie, valueRef, configRef, type_set_flagsRef, residRef);
if (new_cookie.intValue() != kInvalidCookie) {
cookie = new_cookie;
}
if (kDebugStyles) {
ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
value = valueRef.get();
resid = residRef.get();
type_set_flags = type_set_flagsRef.get();
config = configRef.get();
// Deal with the special @null value -- it turns back to TYPE_NULL.
if (value.dataType == Res_value.TYPE_REFERENCE && value.data == 0) {
if (kDebugStyles) {
ALOGI("-> Setting to @null!");
}
value = Res_value.NULL_VALUE;
cookie = K_INVALID_COOKIE;
}
if (kDebugStyles) {
ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
}
// Write the final value back to Java.
out_values[destOffset + STYLE_TYPE] = value.dataType;
out_values[destOffset + STYLE_DATA] = value.data;
out_values[destOffset + STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
out_values[destOffset + STYLE_RESOURCE_ID] = resid;
out_values[destOffset + STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[destOffset + STYLE_DENSITY] = config.density;
if (out_indices != null && value.dataType != Res_value.TYPE_NULL) {
indicesIdx++;
out_indices[indicesIdx] = ii;
}
destOffset += STYLE_NUM_ENTRIES;
}
if (out_indices != null) {
out_indices[0] = indicesIdx;
}
return true;
}
use of org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry in project robolectric by robolectric.
the class ShadowArscAssetManager9 method nativeGetResourceArray.
// static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
// jintArray out_data) {
@Implementation(minSdk = P)
protected static int nativeGetResourceArray(long ptr, @ArrayRes int resid, @NonNull int[] out_data) {
CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
ResolvedBag bag = assetmanager.GetBag(resid);
if (bag == null) {
return -1;
}
int out_data_length = out_data.length;
if ((int) (bag.entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
throw new IllegalArgumentException("Input array is not large enough");
}
int[] buffer = // reinterpret_cast<int*>(env.GetPrimitiveArrayCritical(out_data, null));
out_data;
if (buffer == null) {
return -1;
}
int[] cursor = buffer;
for (int i = 0; i < bag.entry_count; i++) {
ResolvedBag.Entry entry = bag.entries[i];
final Ref<Res_value> value = new Ref<>(entry.value);
final Ref<ResTable_config> selected_config = new Ref<>(new ResTable_config());
selected_config.get().density = 0;
final Ref<Integer> flags = new Ref<>(bag.type_spec_flags);
final Ref<Integer> ref = new Ref<>(0);
ApkAssetsCookie cookie = assetmanager.ResolveReference(entry.cookie, value, selected_config, flags, ref);
if (cookie.intValue() == kInvalidCookie) {
// env.ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
return -1;
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
if (value.get().dataType == Res_value.TYPE_REFERENCE && value.get().data == 0) {
value.set(Res_value.NULL_VALUE);
}
int offset = i * STYLE_NUM_ENTRIES;
cursor[offset + STYLE_TYPE] = (int) (value.get().dataType);
cursor[offset + STYLE_DATA] = (int) (value.get().data);
cursor[offset + STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
cursor[offset + STYLE_RESOURCE_ID] = (int) (ref.get());
cursor[offset + STYLE_CHANGING_CONFIGURATIONS] = (int) (flags.get());
cursor[offset + STYLE_DENSITY] = (int) (selected_config.get().density);
// cursor += STYLE_NUM_ENTRIES;
}
// env.ReleasePrimitiveArrayCritical(out_data, buffer, 0);
return (int) (bag.entry_count);
}
use of org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry 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.CppAssetManager2.ResolvedBag.Entry in project robolectric by robolectric.
the class ShadowArscAssetManager10 method nativeGetResourceArray.
// static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
// jintArray out_data) {
@Implementation(minSdk = P)
protected static int nativeGetResourceArray(long ptr, @ArrayRes int resid, @NonNull int[] out_data) {
CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
ResolvedBag bag = assetmanager.GetBag(resid);
if (bag == null) {
return -1;
}
int out_data_length = out_data.length;
if ((int) (bag.entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
throw new IllegalArgumentException("Input array is not large enough");
}
int[] buffer = // reinterpret_cast<int*>(env.GetPrimitiveArrayCritical(out_data, null));
out_data;
if (buffer == null) {
return -1;
}
int[] cursor = buffer;
for (int i = 0; i < bag.entry_count; i++) {
ResolvedBag.Entry entry = bag.entries[i];
final Ref<Res_value> value = new Ref<>(entry.value);
final Ref<ResTable_config> selected_config = new Ref<>(new ResTable_config());
selected_config.get().density = 0;
final Ref<Integer> flags = new Ref<>(bag.type_spec_flags);
final Ref<Integer> ref = new Ref<>(0);
ApkAssetsCookie cookie = assetmanager.ResolveReference(entry.cookie, value, selected_config, flags, ref);
if (cookie.intValue() == kInvalidCookie) {
// env.ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
return -1;
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
if (value.get().dataType == Res_value.TYPE_REFERENCE && value.get().data == 0) {
value.set(Res_value.NULL_VALUE);
}
int offset = i * STYLE_NUM_ENTRIES;
cursor[offset + STYLE_TYPE] = (int) (value.get().dataType);
cursor[offset + STYLE_DATA] = (int) (value.get().data);
cursor[offset + STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
cursor[offset + STYLE_RESOURCE_ID] = (int) (ref.get());
cursor[offset + STYLE_CHANGING_CONFIGURATIONS] = (int) (flags.get());
cursor[offset + STYLE_DENSITY] = (int) (selected_config.get().density);
// cursor += STYLE_NUM_ENTRIES;
}
// env.ReleasePrimitiveArrayCritical(out_data, buffer, 0);
return (int) (bag.entry_count);
}
use of org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry in project robolectric by robolectric.
the class ShadowArscAssetManager10 method nativeGetResourceStringArrayInfo.
// static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
// jint resid) {
@Implementation(minSdk = P)
@Nullable
protected static int[] nativeGetResourceStringArrayInfo(long ptr, @ArrayRes int resid) {
CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
ResolvedBag bag = assetmanager.GetBag(resid);
if (bag == null) {
return null;
}
int[] array = new int[bag.entry_count * 2];
// if (array == null) {
// return null;
// }
// reinterpret_cast<int*>(env.GetPrimitiveArrayCritical(array, null));
int[] buffer = array;
for (int i = 0; i < bag.entry_count; i++) {
ResolvedBag.Entry entry = bag.entries[i];
final Ref<Res_value> value = new Ref<>(entry.value);
final Ref<ResTable_config> selected_config = new Ref<>(null);
final Ref<Integer> flags = new Ref<>(0);
final Ref<Integer> ref = new Ref<>(0);
ApkAssetsCookie cookie = assetmanager.ResolveReference(entry.cookie, value, selected_config, flags, ref);
if (cookie.intValue() == kInvalidCookie) {
// env.ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
return null;
}
int string_index = -1;
if (value.get().dataType == Res_value.TYPE_STRING) {
string_index = (int) (value.get().data);
}
buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
buffer[(i * 2) + 1] = string_index;
}
// env.ReleasePrimitiveArrayCritical(array, buffer, 0);
return array;
}
Aggregations