public int getBagLocked()

in resources/src/main/java/org/robolectric/res/android/ResTable.java [2715:2992]


  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;
    // todo cache
//    if (isTruthy(typeSet)) {
//      bag_set set = typeSet[e];
//      if (isTruthy(set)) {
//        if (set != (bag_set) 0xFFFFFFFF){
//        if (set != SENTINEL_BAG_SET){
//          if (outTypeSpecFlags != NULL) {
//                    outTypeSpecFlags.set(set.typeSpecFlags);
//          }
//          outBag.set((bag_entry *) (set + 1);
//          if (kDebugTableSuperNoisy) {
//            ALOGI("Found existing bag for: 0x%x\n", resID);
//          }
//          return set.numAttrs;
//        }
//        ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
//            resID);
//        return BAD_INDEX;
//      }
//    }
//
    // Bag not found, we need to compute it!
    if (!isTruthy(typeSet)) {
      typeSet = new bag_set[NENTRY]; // (bag_set**)calloc(NENTRY, sizeof(bag_set*));
      //cacheEntry.cachedBags = typeSet;
    }
//
//    // Mark that we are currently working on this one.
//    typeSet[e] = (bag_set*)0xFFFFFFFF;
//    typeSet[e] = SENTINEL_BAG_SET;

    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())) {
        // Attributes don't have a resource id as the name. They specify
        // 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;
  }