am 6c997a9e: aapt now sorts the strings in the resource string pool.

* commit '6c997a9e880e08c354ffd809bd62df9e25e9c4d4':
  aapt now sorts the strings in the resource string pool.
This commit is contained in:
Dianne Hackborn
2012-01-31 17:24:50 -08:00
committed by Android Git Automerger
11 changed files with 1362 additions and 899 deletions

View File

@@ -347,6 +347,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
sb.append(" (no locale)");
}
switch (textLayoutDirection) {
case LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE: /* ltr not interesting */ break;
case LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
}

View File

@@ -444,23 +444,31 @@ public:
void uninit();
// Return string entry as UTF16; if the pool is UTF8, the string will
// be converted before returning.
inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
return stringAt(ref.index, outLen);
}
const char16_t* stringAt(size_t idx, size_t* outLen) const;
// Note: returns null if the string pool is not UTF8.
const char* string8At(size_t idx, size_t* outLen) const;
// Return string whether the pool is UTF8 or UTF16. Does not allow you
// to distinguish null.
const String8 string8ObjectAt(size_t idx) const;
const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const;
const ResStringPool_span* styleAt(size_t idx) const;
ssize_t indexOfString(const char16_t* str, size_t strLen) const;
size_t size() const;
size_t styleCount() const;
size_t bytes() const;
#ifndef HAVE_ANDROID_OS
bool isSorted() const;
bool isUTF8() const;
#endif
private:
status_t mError;
@@ -746,7 +754,9 @@ private:
/**
* Header for a resource table. Its data contains a series of
* additional chunks:
* * A ResStringPool_header containing all table values.
* * A ResStringPool_header containing all table values. This string pool
* contains all of the string values in the entire resource table (not
* the names of entries or type identifiers however).
* * One or more ResTable_package chunks.
*
* Specific entries within a resource table can be uniquely identified
@@ -984,68 +994,15 @@ struct ResTable_config
uint32_t screenSizeDp;
};
inline void copyFromDeviceNoSwap(const ResTable_config& o) {
const size_t size = dtohl(o.size);
if (size >= sizeof(ResTable_config)) {
*this = o;
} else {
memcpy(this, &o, size);
memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size);
}
}
void copyFromDeviceNoSwap(const ResTable_config& o);
inline void copyFromDtoH(const ResTable_config& o) {
copyFromDeviceNoSwap(o);
size = sizeof(ResTable_config);
mcc = dtohs(mcc);
mnc = dtohs(mnc);
density = dtohs(density);
screenWidth = dtohs(screenWidth);
screenHeight = dtohs(screenHeight);
sdkVersion = dtohs(sdkVersion);
minorVersion = dtohs(minorVersion);
smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
screenWidthDp = dtohs(screenWidthDp);
screenHeightDp = dtohs(screenHeightDp);
}
inline void swapHtoD() {
size = htodl(size);
mcc = htods(mcc);
mnc = htods(mnc);
density = htods(density);
screenWidth = htods(screenWidth);
screenHeight = htods(screenHeight);
sdkVersion = htods(sdkVersion);
minorVersion = htods(minorVersion);
smallestScreenWidthDp = htods(smallestScreenWidthDp);
screenWidthDp = htods(screenWidthDp);
screenHeightDp = htods(screenHeightDp);
}
inline int compare(const ResTable_config& o) const {
int32_t diff = (int32_t)(imsi - o.imsi);
if (diff != 0) return diff;
diff = (int32_t)(locale - o.locale);
if (diff != 0) return diff;
diff = (int32_t)(screenType - o.screenType);
if (diff != 0) return diff;
diff = (int32_t)(input - o.input);
if (diff != 0) return diff;
diff = (int32_t)(screenSize - o.screenSize);
if (diff != 0) return diff;
diff = (int32_t)(version - o.version);
if (diff != 0) return diff;
diff = (int32_t)(screenLayout - o.screenLayout);
if (diff != 0) return diff;
diff = (int32_t)(uiMode - o.uiMode);
if (diff != 0) return diff;
diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
if (diff != 0) return diff;
diff = (int32_t)(screenSizeDp - o.screenSizeDp);
return (int)diff;
}
void copyFromDtoH(const ResTable_config& o);
void swapHtoD();
int compare(const ResTable_config& o) const;
int compareLogical(const ResTable_config& o) const;
// Flags indicating a set of config values. These flag constants must
// match the corresponding ones in android.content.pm.ActivityInfo and
// attrs_manifest.xml.
@@ -1068,158 +1025,10 @@ struct ResTable_config
// Compare two configuration, returning CONFIG_* flags set for each value
// that is different.
inline int diff(const ResTable_config& o) const {
int diffs = 0;
if (mcc != o.mcc) diffs |= CONFIG_MCC;
if (mnc != o.mnc) diffs |= CONFIG_MNC;
if (locale != o.locale) diffs |= CONFIG_LOCALE;
if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION;
if (density != o.density) diffs |= CONFIG_DENSITY;
if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN;
if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0)
diffs |= CONFIG_KEYBOARD_HIDDEN;
if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD;
if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
if (version != o.version) diffs |= CONFIG_VERSION;
if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT;
if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
return diffs;
}
int diff(const ResTable_config& o) const;
// Return true if 'this' is more specific than 'o'.
inline bool
isMoreSpecificThan(const ResTable_config& o) const {
// The order of the following tests defines the importance of one
// configuration parameter over another. Those tests first are more
// important, trumping any values in those following them.
if (imsi || o.imsi) {
if (mcc != o.mcc) {
if (!mcc) return false;
if (!o.mcc) return true;
}
if (mnc != o.mnc) {
if (!mnc) return false;
if (!o.mnc) return true;
}
}
if (locale || o.locale) {
if (language[0] != o.language[0]) {
if (!language[0]) return false;
if (!o.language[0]) return true;
}
if (country[0] != o.country[0]) {
if (!country[0]) return false;
if (!o.country[0]) return true;
}
}
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
if (!smallestScreenWidthDp) return false;
if (!o.smallestScreenWidthDp) return true;
}
}
if (screenSizeDp || o.screenSizeDp) {
if (screenWidthDp != o.screenWidthDp) {
if (!screenWidthDp) return false;
if (!o.screenWidthDp) return true;
}
if (screenHeightDp != o.screenHeightDp) {
if (!screenHeightDp) return false;
if (!o.screenHeightDp) return true;
}
}
if (screenLayout || o.screenLayout) {
if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) {
if (!(screenLayout & MASK_SCREENSIZE)) return false;
if (!(o.screenLayout & MASK_SCREENSIZE)) return true;
}
if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) {
if (!(screenLayout & MASK_SCREENLONG)) return false;
if (!(o.screenLayout & MASK_SCREENLONG)) return true;
}
}
if (orientation != o.orientation) {
if (!orientation) return false;
if (!o.orientation) return true;
}
if (uiMode || o.uiMode) {
if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) {
if (!(uiMode & MASK_UI_MODE_TYPE)) return false;
if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true;
}
if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) {
if (!(uiMode & MASK_UI_MODE_NIGHT)) return false;
if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true;
}
}
// density is never 'more specific'
// as the default just equals 160
if (touchscreen != o.touchscreen) {
if (!touchscreen) return false;
if (!o.touchscreen) return true;
}
if (input || o.input) {
if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) {
if (!(inputFlags & MASK_KEYSHIDDEN)) return false;
if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true;
}
if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) {
if (!(inputFlags & MASK_NAVHIDDEN)) return false;
if (!(o.inputFlags & MASK_NAVHIDDEN)) return true;
}
if (keyboard != o.keyboard) {
if (!keyboard) return false;
if (!o.keyboard) return true;
}
if (navigation != o.navigation) {
if (!navigation) return false;
if (!o.navigation) return true;
}
}
if (screenSize || o.screenSize) {
if (screenWidth != o.screenWidth) {
if (!screenWidth) return false;
if (!o.screenWidth) return true;
}
if (screenHeight != o.screenHeight) {
if (!screenHeight) return false;
if (!o.screenHeight) return true;
}
}
if (version || o.version) {
if (sdkVersion != o.sdkVersion) {
if (!sdkVersion) return false;
if (!o.sdkVersion) return true;
}
if (minorVersion != o.minorVersion) {
if (!minorVersion) return false;
if (!o.minorVersion) return true;
}
}
return false;
}
bool isMoreSpecificThan(const ResTable_config& o) const;
// Return true if 'this' is a better match than 'o' for the 'requested'
// configuration. This assumes that match() has already been used to
@@ -1231,222 +1040,7 @@ struct ResTable_config
// they are not equal then one must be generic because only generic and
// '==requested' will pass the match() call. So if this is not generic,
// it wins. If this IS generic, o wins (return false).
inline bool
isBetterThan(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested) {
if (imsi || o.imsi) {
if ((mcc != o.mcc) && requested->mcc) {
return (mcc);
}
if ((mnc != o.mnc) && requested->mnc) {
return (mnc);
}
}
if (locale || o.locale) {
if ((language[0] != o.language[0]) && requested->language[0]) {
return (language[0]);
}
if ((country[0] != o.country[0]) && requested->country[0]) {
return (country[0]);
}
}
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
// The configuration closest to the actual size is best.
// We assume that larger configs have already been filtered
// out at this point. That means we just want the largest one.
return smallestScreenWidthDp >= o.smallestScreenWidthDp;
}
if (screenSizeDp || o.screenSizeDp) {
// "Better" is based on the sum of the difference between both
// width and height from the requested dimensions. We are
// assuming the invalid configs (with smaller dimens) have
// already been filtered. Note that if a particular dimension
// is unspecified, we will end up with a large value (the
// difference between 0 and the requested dimension), which is
// good since we will prefer a config that has specified a
// dimension value.
int myDelta = 0, otherDelta = 0;
if (requested->screenWidthDp) {
myDelta += requested->screenWidthDp - screenWidthDp;
otherDelta += requested->screenWidthDp - o.screenWidthDp;
}
if (requested->screenHeightDp) {
myDelta += requested->screenHeightDp - screenHeightDp;
otherDelta += requested->screenHeightDp - o.screenHeightDp;
}
//ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d",
// screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp,
// requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta);
return (myDelta <= otherDelta);
}
if (screenLayout || o.screenLayout) {
if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0
&& (requested->screenLayout & MASK_SCREENSIZE)) {
// A little backwards compatibility here: undefined is
// considered equivalent to normal. But only if the
// requested size is at least normal; otherwise, small
// is better than the default.
int mySL = (screenLayout & MASK_SCREENSIZE);
int oSL = (o.screenLayout & MASK_SCREENSIZE);
int fixedMySL = mySL;
int fixedOSL = oSL;
if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) {
if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL;
if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL;
}
// For screen size, the best match is the one that is
// closest to the requested screen size, but not over
// (the not over part is dealt with in match() below).
if (fixedMySL == fixedOSL) {
// If the two are the same, but 'this' is actually
// undefined, then the other is really a better match.
if (mySL == 0) return false;
return true;
}
return fixedMySL >= fixedOSL;
}
if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0
&& (requested->screenLayout & MASK_SCREENLONG)) {
return (screenLayout & MASK_SCREENLONG);
}
}
if ((orientation != o.orientation) && requested->orientation) {
return (orientation);
}
if (uiMode || o.uiMode) {
if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0
&& (requested->uiMode & MASK_UI_MODE_TYPE)) {
return (uiMode & MASK_UI_MODE_TYPE);
}
if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0
&& (requested->uiMode & MASK_UI_MODE_NIGHT)) {
return (uiMode & MASK_UI_MODE_NIGHT);
}
}
if (screenType || o.screenType) {
if (density != o.density) {
// density is tough. Any density is potentially useful
// because the system will scale it. Scaling down
// is generally better than scaling up.
// Default density counts as 160dpi (the system default)
// TODO - remove 160 constants
int h = (density?density:160);
int l = (o.density?o.density:160);
bool bImBigger = true;
if (l > h) {
int t = h;
h = l;
l = t;
bImBigger = false;
}
int reqValue = (requested->density?requested->density:160);
if (reqValue >= h) {
// requested value higher than both l and h, give h
return bImBigger;
}
if (l >= reqValue) {
// requested value lower than both l and h, give l
return !bImBigger;
}
// saying that scaling down is 2x better than up
if (((2 * l) - reqValue) * h > reqValue * reqValue) {
return !bImBigger;
} else {
return bImBigger;
}
}
if ((touchscreen != o.touchscreen) && requested->touchscreen) {
return (touchscreen);
}
}
if (input || o.input) {
const int keysHidden = inputFlags & MASK_KEYSHIDDEN;
const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN;
if (keysHidden != oKeysHidden) {
const int reqKeysHidden =
requested->inputFlags & MASK_KEYSHIDDEN;
if (reqKeysHidden) {
if (!keysHidden) return false;
if (!oKeysHidden) return true;
// For compatibility, we count KEYSHIDDEN_NO as being
// the same as KEYSHIDDEN_SOFT. Here we disambiguate
// these by making an exact match more specific.
if (reqKeysHidden == keysHidden) return true;
if (reqKeysHidden == oKeysHidden) return false;
}
}
const int navHidden = inputFlags & MASK_NAVHIDDEN;
const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN;
if (navHidden != oNavHidden) {
const int reqNavHidden =
requested->inputFlags & MASK_NAVHIDDEN;
if (reqNavHidden) {
if (!navHidden) return false;
if (!oNavHidden) return true;
}
}
if ((keyboard != o.keyboard) && requested->keyboard) {
return (keyboard);
}
if ((navigation != o.navigation) && requested->navigation) {
return (navigation);
}
}
if (screenSize || o.screenSize) {
// "Better" is based on the sum of the difference between both
// width and height from the requested dimensions. We are
// assuming the invalid configs (with smaller sizes) have
// already been filtered. Note that if a particular dimension
// is unspecified, we will end up with a large value (the
// difference between 0 and the requested dimension), which is
// good since we will prefer a config that has specified a
// size value.
int myDelta = 0, otherDelta = 0;
if (requested->screenWidth) {
myDelta += requested->screenWidth - screenWidth;
otherDelta += requested->screenWidth - o.screenWidth;
}
if (requested->screenHeight) {
myDelta += requested->screenHeight - screenHeight;
otherDelta += requested->screenHeight - o.screenHeight;
}
return (myDelta <= otherDelta);
}
if (version || o.version) {
if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
return (sdkVersion > o.sdkVersion);
}
if ((minorVersion != o.minorVersion) &&
requested->minorVersion) {
return (minorVersion);
}
}
return false;
}
return isMoreSpecificThan(o);
}
bool isBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
// Return true if 'this' can be considered a match for the parameters in
// 'settings'.
@@ -1454,150 +1048,11 @@ struct ResTable_config
// but a request for the default should not match odd specifics
// (ie, request with no mcc should not match a particular mcc's data)
// settings is the requested settings
inline bool match(const ResTable_config& settings) const {
if (imsi != 0) {
if (mcc != 0 && mcc != settings.mcc) {
return false;
}
if (mnc != 0 && mnc != settings.mnc) {
return false;
}
}
if (locale != 0) {
if (language[0] != 0
&& (language[0] != settings.language[0]
|| language[1] != settings.language[1])) {
return false;
}
if (country[0] != 0
&& (country[0] != settings.country[0]
|| country[1] != settings.country[1])) {
return false;
}
}
if (screenConfig != 0) {
const int screenSize = screenLayout&MASK_SCREENSIZE;
const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
// Any screen sizes for larger screens than the setting do not
// match.
if (screenSize != 0 && screenSize > setScreenSize) {
return false;
}
const int screenLong = screenLayout&MASK_SCREENLONG;
const int setScreenLong = settings.screenLayout&MASK_SCREENLONG;
if (screenLong != 0 && screenLong != setScreenLong) {
return false;
}
bool match(const ResTable_config& settings) const;
const int uiModeType = uiMode&MASK_UI_MODE_TYPE;
const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE;
if (uiModeType != 0 && uiModeType != setUiModeType) {
return false;
}
void getLocale(char str[6]) const;
const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT;
const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT;
if (uiModeNight != 0 && uiModeNight != setUiModeNight) {
return false;
}
if (smallestScreenWidthDp != 0
&& smallestScreenWidthDp > settings.smallestScreenWidthDp) {
return false;
}
}
if (screenSizeDp != 0) {
if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
//ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp);
return false;
}
if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) {
//ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp);
return false;
}
}
if (screenType != 0) {
if (orientation != 0 && orientation != settings.orientation) {
return false;
}
// density always matches - we can scale it. See isBetterThan
if (touchscreen != 0 && touchscreen != settings.touchscreen) {
return false;
}
}
if (input != 0) {
const int keysHidden = inputFlags&MASK_KEYSHIDDEN;
const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN;
if (keysHidden != 0 && keysHidden != setKeysHidden) {
// For compatibility, we count a request for KEYSHIDDEN_NO as also
// matching the more recent KEYSHIDDEN_SOFT. Basically
// KEYSHIDDEN_NO means there is some kind of keyboard available.
//ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden);
if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) {
//ALOGI("No match!");
return false;
}
}
const int navHidden = inputFlags&MASK_NAVHIDDEN;
const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN;
if (navHidden != 0 && navHidden != setNavHidden) {
return false;
}
if (keyboard != 0 && keyboard != settings.keyboard) {
return false;
}
if (navigation != 0 && navigation != settings.navigation) {
return false;
}
}
if (screenSize != 0) {
if (screenWidth != 0 && screenWidth > settings.screenWidth) {
return false;
}
if (screenHeight != 0 && screenHeight > settings.screenHeight) {
return false;
}
}
if (version != 0) {
if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) {
return false;
}
if (minorVersion != 0 && minorVersion != settings.minorVersion) {
return false;
}
}
return true;
}
void getLocale(char str[6]) const {
memset(str, 0, 6);
if (language[0]) {
str[0] = language[0];
str[1] = language[1];
if (country[0]) {
str[2] = '_';
str[3] = country[0];
str[4] = country[1];
}
}
}
String8 toString() const {
char buf[200];
sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d "
"kbd=%d nav=%d input=%d ssz=%dx%d sw%ddp w%ddp h%ddp sz=%d long=%d "
"ui=%d night=%d vers=%d.%d",
mcc, mnc,
language[0] ? language[0] : '-', language[1] ? language[1] : '-',
country[0] ? country[0] : '-', country[1] ? country[1] : '-',
orientation, touchscreen, density, keyboard, navigation, inputFlags,
screenWidth, screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG,
uiMode&MASK_UI_MODE_TYPE, uiMode&MASK_UI_MODE_NIGHT,
sdkVersion, minorVersion);
return String8(buf);
}
String8 toString() const;
};
/**
@@ -2056,8 +1511,14 @@ public:
const char16_t* getBasePackageName(size_t idx) const;
uint32_t getBasePackageId(size_t idx) const;
// Return the number of resource tables that the object contains.
size_t getTableCount() const;
// Return the values string pool for the resource table at the given
// index. This string pool contains all of the strings for values
// contained in the resource table -- that is the item values themselves,
// but not the names their entries or types.
const ResStringPool* getTableStringBlock(size_t index) const;
// Return unique cookie identifier for the given resource table.
void* getTableCookie(size_t index) const;
// Return the configurations (ResTable_config) that we know about

File diff suppressed because it is too large Load Diff

View File

@@ -53,17 +53,6 @@ enum {
AXIS_END = AXIS_VERSION,
};
enum {
SDK_CUPCAKE = 3,
SDK_DONUT = 4,
SDK_ECLAIR = 5,
SDK_ECLAIR_0_1 = 6,
SDK_MR1 = 7,
SDK_FROYO = 8,
SDK_HONEYCOMB_MR2 = 13,
SDK_ICE_CREAM_SANDWICH = 14,
};
/**
* This structure contains a specific variation of a single file out
* of all the variations it can have that we can have.

View File

@@ -14,6 +14,18 @@
#include <utils/String8.h>
#include <utils/Vector.h>
enum {
SDK_CUPCAKE = 3,
SDK_DONUT = 4,
SDK_ECLAIR = 5,
SDK_ECLAIR_0_1 = 6,
SDK_MR1 = 7,
SDK_FROYO = 8,
SDK_HONEYCOMB_MR2 = 13,
SDK_ICE_CREAM_SANDWICH = 14,
SDK_ICE_CREAM_SANDWICH_MR1 = 15,
};
/*
* Things we can do.
*/
@@ -82,7 +94,6 @@ public:
void setRequireLocalization(bool val) { mRequireLocalization = val; }
bool getPseudolocalize(void) const { return mPseudolocalize; }
void setPseudolocalize(bool val) { mPseudolocalize = val; }
bool getWantUTF16(void) const { return mWantUTF16; }
void setWantUTF16(bool val) { mWantUTF16 = val; }
bool getValues(void) const { return mValues; }
void setValues(bool val) { mValues = val; }
@@ -103,6 +114,10 @@ public:
bool getGenDependencies() { return mGenDependencies; }
void setGenDependencies(bool val) { mGenDependencies = val; }
bool getUTF16StringsOption() {
return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
}
/*
* Input options.
*/

View File

@@ -479,6 +479,11 @@ int doDump(Bundle* bundle)
#ifndef HAVE_ANDROID_OS
res.print(bundle->getValues());
#endif
} else if (strcmp("strings", option) == 0) {
const ResStringPool* pool = res.getTableStringBlock(0);
printStringPool(pool);
} else if (strcmp("xmltree", option) == 0) {
if (bundle->getFileSpecCount() < 3) {
fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
@@ -1382,7 +1387,7 @@ int doDump(Bundle* bundle)
delete dir;
}
} else if (strcmp("badger", option) == 0) {
printf(CONSOLE_DATA);
printf("%s", CONSOLE_DATA);
} else if (strcmp("configurations", option) == 0) {
Vector<ResTable_config> configs;
res.getConfigurations(&configs);

View File

@@ -847,8 +847,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
* request UTF-16 encoding and the parameters of this package
* allow UTF-8 to be used.
*/
if (!bundle->getWantUTF16()
&& bundle->isMinSdkAtLeast(SDK_FROYO)) {
if (!bundle->getUTF16StringsOption()) {
xmlFlags |= XML_COMPILE_UTF8;
}

View File

@@ -2047,7 +2047,8 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
uint32_t attrID,
const Vector<StringPool::entry_style_span>* style,
String16* outStr, void* accessorCookie,
uint32_t attrType)
uint32_t attrType, const String8* configTypeName,
const ConfigDescription* config)
{
String16 finalStr;
@@ -2075,10 +2076,19 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
if (outValue->dataType == outValue->TYPE_STRING) {
// Should do better merging styles.
if (pool) {
if (style != NULL && style->size() > 0) {
outValue->data = pool->add(finalStr, *style);
String8 configStr;
if (config != NULL) {
configStr = config->toString();
} else {
outValue->data = pool->add(finalStr, true);
configStr = "(null)";
}
NOISY(printf("Adding to pool string style #%d config %s: %s\n",
style != NULL ? style->size() : 0,
configStr.string(), String8(finalStr).string()));
if (style != NULL && style->size() > 0) {
outValue->data = pool->add(finalStr, *style, configTypeName, config);
} else {
outValue->data = pool->add(finalStr, true, configTypeName, config);
}
} else {
// Caller will fill this in later.
@@ -2537,16 +2547,19 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
return err;
}
const ConfigDescription nullConfig;
const size_t N = mOrderedPackages.size();
size_t pi;
const static String16 mipmap16("mipmap");
bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
bool useUTF8 = !bundle->getUTF16StringsOption();
// Iterate through all data, collecting all values (strings,
// references, etc).
StringPool valueStrings = StringPool(false, useUTF8);
Vector<sp<Entry> > allEntries;
for (pi=0; pi<N; pi++) {
sp<Package> p = mOrderedPackages.itemAt(pi);
if (p->getTypes().size() == 0) {
@@ -2567,6 +2580,19 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
const String16 typeName(t->getName());
typeStrings.add(typeName, false);
// This is a hack to tweak the sorting order of the final strings,
// to put stuff that is generally not language-specific first.
String8 configTypeName(typeName);
if (configTypeName == "drawable" || configTypeName == "layout"
|| configTypeName == "color" || configTypeName == "anim"
|| configTypeName == "interpolator" || configTypeName == "animator"
|| configTypeName == "xml" || configTypeName == "menu"
|| configTypeName == "mipmap" || configTypeName == "raw") {
configTypeName = "1complex";
} else {
configTypeName = "2value";
}
const bool filterable = (typeName != mipmap16);
const size_t N = t->getOrderedConfigs().size();
@@ -2586,10 +2612,21 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
continue;
}
e->setNameIndex(keyStrings.add(e->getName(), true));
status_t err = e->prepareFlatten(&valueStrings, this);
// If this entry has no values for other configs,
// and is the default config, then it is special. Otherwise
// we want to add it with the config info.
ConfigDescription* valueConfig = NULL;
if (N != 1 || config == nullConfig) {
valueConfig = &config;
}
status_t err = e->prepareFlatten(&valueStrings, this,
&configTypeName, &config);
if (err != NO_ERROR) {
return err;
}
allEntries.add(e);
}
}
}
@@ -2598,6 +2635,17 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
p->setKeyStrings(keyStrings.createStringBlock());
}
if (bundle->getOutputAPKFile() != NULL) {
// Now we want to sort the value strings for better locality. This will
// cause the positions of the strings to change, so we need to go back
// through out resource entries and update them accordingly. Only need
// to do this if actually writing the output file.
valueStrings.sortByConfig();
for (pi=0; pi<allEntries.size(); pi++) {
allEntries[pi]->remapStringValue(&valueStrings);
}
}
ssize_t strAmt = 0;
// Now build the array of package chunks.
@@ -3137,14 +3185,16 @@ status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
}
status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
const String8* configTypeName, const ConfigDescription* config)
{
if (mType == TYPE_ITEM) {
Item& it = mItem;
AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
if (!table->stringToValue(&it.parsedValue, strings,
it.value, false, true, 0,
&it.style, NULL, &ac, mItemFormat)) {
&it.style, NULL, &ac, mItemFormat,
configTypeName, config)) {
return UNKNOWN_ERROR;
}
} else if (mType == TYPE_BAG) {
@@ -3155,7 +3205,8 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable
AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
if (!table->stringToValue(&it.parsedValue, strings,
it.value, false, true, it.bagKeyId,
&it.style, NULL, &ac, it.format)) {
&it.style, NULL, &ac, it.format,
configTypeName, config)) {
return UNKNOWN_ERROR;
}
}
@@ -3167,6 +3218,29 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable
return NO_ERROR;
}
status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
{
if (mType == TYPE_ITEM) {
Item& it = mItem;
if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
}
} else if (mType == TYPE_BAG) {
const size_t N = mBag.size();
for (size_t i=0; i<N; i++) {
Item& it = mBag.editValueAt(i);
if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
}
}
} else {
mPos.error("Error: entry %s is not a single item or a bag.\n",
String8(mName).string());
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
{
size_t amt = 0;

View File

@@ -76,6 +76,37 @@ public:
class Type;
class Entry;
struct ConfigDescription : public ResTable_config {
ConfigDescription() {
memset(this, 0, sizeof(*this));
size = sizeof(ResTable_config);
}
ConfigDescription(const ResTable_config&o) {
*static_cast<ResTable_config*>(this) = o;
size = sizeof(ResTable_config);
}
ConfigDescription(const ConfigDescription&o) {
*static_cast<ResTable_config*>(this) = o;
}
ConfigDescription& operator=(const ResTable_config& o) {
*static_cast<ResTable_config*>(this) = o;
size = sizeof(ResTable_config);
return *this;
}
ConfigDescription& operator=(const ConfigDescription& o) {
*static_cast<ResTable_config*>(this) = o;
return *this;
}
inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
};
ResourceTable(Bundle* bundle, const String16& assetsPackage);
status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
@@ -183,7 +214,9 @@ public:
uint32_t attrID,
const Vector<StringPool::entry_style_span>* style = NULL,
String16* outStr = NULL, void* accessorCookie = NULL,
uint32_t attrType = ResTable_map::TYPE_ANY);
uint32_t attrType = ResTable_map::TYPE_ANY,
const String8* configTypeName = NULL,
const ConfigDescription* config = NULL);
status_t assignResourceIds();
status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
@@ -305,7 +338,10 @@ public:
status_t assignResourceIds(ResourceTable* table,
const String16& package);
status_t prepareFlatten(StringPool* strings, ResourceTable* table);
status_t prepareFlatten(StringPool* strings, ResourceTable* table,
const String8* configTypeName, const ConfigDescription* config);
status_t remapStringValue(StringPool* strings);
ssize_t flatten(Bundle*, const sp<AaptFile>& data, bool isPublic);
@@ -322,37 +358,6 @@ public:
uint32_t mParentId;
SourcePos mPos;
};
struct ConfigDescription : public ResTable_config {
ConfigDescription() {
memset(this, 0, sizeof(*this));
size = sizeof(ResTable_config);
}
ConfigDescription(const ResTable_config&o) {
*static_cast<ResTable_config*>(this) = o;
size = sizeof(ResTable_config);
}
ConfigDescription(const ConfigDescription&o) {
*static_cast<ResTable_config*>(this) = o;
}
ConfigDescription& operator=(const ResTable_config& o) {
*static_cast<ResTable_config*>(this) = o;
size = sizeof(ResTable_config);
return *this;
}
ConfigDescription& operator=(const ConfigDescription& o) {
*static_cast<ResTable_config*>(this) = o;
return *this;
}
inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
};
class ConfigList : public RefBase {
public:

View File

@@ -5,8 +5,10 @@
//
#include "StringPool.h"
#include "ResourceTable.h"
#include <utils/ByteOrder.h>
#include <utils/SortedVector.h>
#if HAVE_PRINTF_ZD
# define ZD "%zd"
@@ -30,31 +32,87 @@ void strcpy16_htod(uint16_t* dst, const uint16_t* src)
void printStringPool(const ResStringPool* pool)
{
SortedVector<const void*> uniqueStrings;
const size_t N = pool->size();
for (size_t i=0; i<N; i++) {
size_t len;
if (pool->isUTF8()) {
uniqueStrings.add(pool->string8At(i, &len));
} else {
uniqueStrings.add(pool->stringAt(i, &len));
}
}
printf("String pool of " ZD " unique %s %s strings, " ZD " entries and "
ZD " styles using " ZD " bytes:\n",
(ZD_TYPE)uniqueStrings.size(), pool->isUTF8() ? "UTF-8" : "UTF-16",
pool->isSorted() ? "sorted" : "non-sorted",
(ZD_TYPE)N, (ZD_TYPE)pool->styleCount(), (ZD_TYPE)pool->bytes());
const size_t NS = pool->size();
for (size_t s=0; s<NS; s++) {
size_t len;
const char *str = (const char*)pool->string8At(s, &len);
if (str == NULL) {
str = String8(pool->stringAt(s, &len)).string();
}
printf("String #" ZD ": %s\n", (ZD_TYPE) s, str);
String8 str = pool->string8ObjectAt(s);
printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string());
}
}
String8 StringPool::entry::makeConfigsString() const {
String8 configStr(configTypeName);
if (configStr.size() > 0) configStr.append(" ");
if (configs.size() > 0) {
for (size_t j=0; j<configs.size(); j++) {
if (j > 0) configStr.append(", ");
configStr.append(configs[j].toString());
}
} else {
configStr = "(none)";
}
return configStr;
}
int StringPool::entry::compare(const entry& o) const {
// Strings with styles go first, to reduce the size of the
// styles array.
if (hasStyles) {
return o.hasStyles ? 0 : -1;
}
if (o.hasStyles) {
return 1;
}
int comp = configTypeName.compare(o.configTypeName);
if (comp != 0) {
return comp;
}
const size_t LHN = configs.size();
const size_t RHN = o.configs.size();
size_t i=0;
while (i < LHN && i < RHN) {
comp = configs[i].compareLogical(o.configs[i]);
if (comp != 0) {
return comp;
}
i++;
}
if (LHN < RHN) return -1;
else if (LHN > RHN) return 1;
return 0;
}
StringPool::StringPool(bool sorted, bool utf8)
: mSorted(sorted), mUTF8(utf8), mValues(-1), mIdents(-1)
{
}
ssize_t StringPool::add(const String16& value, bool mergeDuplicates)
ssize_t StringPool::add(const String16& value, bool mergeDuplicates,
const String8* configTypeName, const ResTable_config* config)
{
return add(String16(), value, mergeDuplicates);
return add(String16(), value, mergeDuplicates, configTypeName, config);
}
ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans)
ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans,
const String8* configTypeName, const ResTable_config* config)
{
ssize_t res = add(String16(), value, false);
ssize_t res = add(String16(), value, false, configTypeName, config);
if (res >= 0) {
addStyleSpans(res, spans);
}
@@ -62,7 +120,7 @@ ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& s
}
ssize_t StringPool::add(const String16& ident, const String16& value,
bool mergeDuplicates)
bool mergeDuplicates, const String8* configTypeName, const ResTable_config* config)
{
if (ident.size() > 0) {
ssize_t idx = mIdents.valueFor(ident);
@@ -84,20 +142,43 @@ ssize_t StringPool::add(const String16& ident, const String16& value,
}
}
if (configTypeName != NULL) {
entry& ent = mEntries.editItemAt(eidx);
NOISY(printf("*** adding config type name %s, was %s\n",
configTypeName->string(), ent.configTypeName.string()));
if (ent.configTypeName.size() <= 0) {
ent.configTypeName = *configTypeName;
} else if (ent.configTypeName != *configTypeName) {
ent.configTypeName = " ";
}
}
if (config != NULL) {
// Add this to the set of configs associated with the string.
entry& ent = mEntries.editItemAt(eidx);
size_t addPos;
for (addPos=0; addPos<ent.configs.size(); addPos++) {
int cmp = ent.configs.itemAt(addPos).compareLogical(*config);
if (cmp >= 0) {
if (cmp > 0) {
NOISY(printf("*** inserting config: %s\n", config->toString().string()));
ent.configs.insertAt(*config, addPos);
}
break;
}
}
if (addPos >= ent.configs.size()) {
NOISY(printf("*** adding config: %s\n", config->toString().string()));
ent.configs.add(*config);
}
}
const bool first = vidx < 0;
if (first || !mergeDuplicates) {
pos = mEntryArray.add(eidx);
if (first) {
vidx = mValues.add(value, pos);
const size_t N = mEntryArrayToValues.size();
for (size_t i=0; i<N; i++) {
size_t& e = mEntryArrayToValues.editItemAt(i);
if ((ssize_t)e >= vidx) {
e++;
}
}
}
mEntryArrayToValues.add(vidx);
if (!mSorted) {
entry& ent = mEntries.editItemAt(eidx);
ent.indices.add(pos);
@@ -147,6 +228,7 @@ status_t StringPool::addStyleSpan(size_t idx, const entry_style_span& span)
entry_style& style = mEntryStyleArray.editItemAt(idx);
style.spans.add(span);
mEntries.editItemAt(mEntryArray[idx]).hasStyles = true;
return NO_ERROR;
}
@@ -169,6 +251,132 @@ size_t StringPool::countIdentifiers() const
return mIdents.size();
}
int StringPool::config_sort(const size_t* lhs, const size_t* rhs, void* state)
{
StringPool* pool = (StringPool*)state;
const entry& lhe = pool->mEntries[pool->mEntryArray[*lhs]];
const entry& rhe = pool->mEntries[pool->mEntryArray[*rhs]];
return lhe.compare(rhe);
}
void StringPool::sortByConfig()
{
LOG_ALWAYS_FATAL_IF(mSorted, "Can't sort string pool containing identifiers.");
LOG_ALWAYS_FATAL_IF(mIdents.size() > 0, "Can't sort string pool containing identifiers.");
LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos.size() > 0, "Can't sort string pool after already sorted.");
const size_t N = mEntryArray.size();
// This is a vector that starts out with a 1:1 mapping to entries
// in the array, which we will sort to come up with the desired order.
// At that point it maps from the new position in the array to the
// original position the entry appeared.
Vector<size_t> newPosToOriginalPos;
for (size_t i=0; i<mEntryArray.size(); i++) {
newPosToOriginalPos.add(i);
}
// Sort the array.
NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n"));
newPosToOriginalPos.sort(config_sort, this);
NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n"));
// Create the reverse mapping from the original position in the array
// to the new position where it appears in the sorted array. This is
// so that clients can re-map any positions they had previously stored.
mOriginalPosToNewPos = newPosToOriginalPos;
for (size_t i=0; i<N; i++) {
mOriginalPosToNewPos.editItemAt(newPosToOriginalPos[i]) = i;
}
#if 0
SortedVector<entry> entries;
for (size_t i=0; i<N; i++) {
printf("#%d was %d: %s\n", i, newPosToOriginalPos[i],
mEntries[mEntryArray[newPosToOriginalPos[i]]].makeConfigsString().string());
entries.add(mEntries[mEntryArray[i]]);
}
for (size_t i=0; i<entries.size(); i++) {
printf("Sorted config #%d: %s\n", i,
entries[i].makeConfigsString().string());
}
#endif
// Now we rebuild the arrays.
Vector<entry> newEntries;
Vector<size_t> newEntryArray;
Vector<entry_style> newEntryStyleArray;
DefaultKeyedVector<size_t, size_t> origOffsetToNewOffset;
for (size_t i=0; i<N; i++) {
// We are filling in new offset 'i'; oldI is where we can find it
// in the original data structure.
size_t oldI = newPosToOriginalPos[i];
// This is the actual entry associated with the old offset.
const entry& oldEnt = mEntries[mEntryArray[oldI]];
// This is the same entry the last time we added it to the
// new entry array, if any.
ssize_t newIndexOfOffset = origOffsetToNewOffset.indexOfKey(oldI);
size_t newOffset;
if (newIndexOfOffset < 0) {
// This is the first time we have seen the entry, so add
// it.
newOffset = newEntries.add(oldEnt);
newEntries.editItemAt(newOffset).indices.clear();
} else {
// We have seen this entry before, use the existing one
// instead of adding it again.
newOffset = origOffsetToNewOffset.valueAt(newIndexOfOffset);
}
// Update the indices to include this new position.
newEntries.editItemAt(newOffset).indices.add(i);
// And add the offset of the entry to the new entry array.
newEntryArray.add(newOffset);
// Add any old style to the new style array.
if (mEntryStyleArray.size() > 0) {
if (oldI < mEntryStyleArray.size()) {
newEntryStyleArray.add(mEntryStyleArray[oldI]);
} else {
newEntryStyleArray.add(entry_style());
}
}
}
// Now trim any entries at the end of the new style array that are
// not needed.
for (ssize_t i=newEntryStyleArray.size()-1; i>=0; i--) {
const entry_style& style = newEntryStyleArray[i];
if (style.spans.size() > 0) {
// That's it.
break;
}
// This one is not needed; remove.
newEntryStyleArray.removeAt(i);
}
// All done, install the new data structures and upate mValues with
// the new positions.
mEntries = newEntries;
mEntryArray = newEntryArray;
mEntryStyleArray = newEntryStyleArray;
mValues.clear();
for (size_t i=0; i<mEntries.size(); i++) {
const entry& ent = mEntries[i];
mValues.add(ent.value, ent.indices[0]);
}
#if 0
printf("FINAL SORTED STRING CONFIGS:\n");
for (size_t i=0; i<mEntries.size(); i++) {
const entry& ent = mEntries[i];
printf("#" ZD " %s: %s\n", (ZD_TYPE)i, ent.makeConfigsString().string(),
String8(ent.value).string());
}
#endif
}
sp<AaptFile> StringPool::createStringBlock()
{
sp<AaptFile> pool = new AaptFile(String8(), AaptGroupEntry(),

View File

@@ -40,12 +40,28 @@ class StringPool
public:
struct entry {
entry() : offset(0) { }
entry(const String16& _value) : value(_value), offset(0) { }
entry(const entry& o) : value(o.value), offset(o.offset), indices(o.indices) { }
entry(const String16& _value) : value(_value), offset(0), hasStyles(false) { }
entry(const entry& o) : value(o.value), offset(o.offset),
hasStyles(o.hasStyles), indices(o.indices),
configTypeName(o.configTypeName), configs(o.configs) { }
String16 value;
size_t offset;
bool hasStyles;
Vector<size_t> indices;
String8 configTypeName;
Vector<ResTable_config> configs;
String8 makeConfigsString() const;
int compare(const entry& o) const;
inline bool operator<(const entry& o) const { return compare(o) < 0; }
inline bool operator<=(const entry& o) const { return compare(o) <= 0; }
inline bool operator==(const entry& o) const { return compare(o) == 0; }
inline bool operator!=(const entry& o) const { return compare(o) != 0; }
inline bool operator>=(const entry& o) const { return compare(o) >= 0; }
inline bool operator>(const entry& o) const { return compare(o) > 0; }
};
struct entry_style_span {
@@ -84,12 +100,15 @@ public:
* if this string pool is sorted, the returned index will not be valid
* when the pool is finally written.
*/
ssize_t add(const String16& value, bool mergeDuplicates = false);
ssize_t add(const String16& value, bool mergeDuplicates = false,
const String8* configTypeName = NULL, const ResTable_config* config = NULL);
ssize_t add(const String16& value, const Vector<entry_style_span>& spans);
ssize_t add(const String16& value, const Vector<entry_style_span>& spans,
const String8* configTypeName = NULL, const ResTable_config* config = NULL);
ssize_t add(const String16& ident, const String16& value,
bool mergeDuplicates = false);
bool mergeDuplicates = false,
const String8* configTypeName = NULL, const ResTable_config* config = NULL);
status_t addStyleSpan(size_t idx, const String16& name,
uint32_t start, uint32_t end);
@@ -102,6 +121,18 @@ public:
size_t countIdentifiers() const;
// Sort the contents of the string block by the configuration associated
// with each item. After doing this you can use mapOriginalPosToNewPos()
// to find out the new position given the position originall returned by
// add().
void sortByConfig();
// For use after sortByConfig() to map from the original position of
// a string to its new sorted position.
size_t mapOriginalPosToNewPos(size_t originalPos) const {
return mOriginalPosToNewPos.itemAt(originalPos);
}
sp<AaptFile> createStringBlock();
status_t writeStringBlock(const sp<AaptFile>& pool);
@@ -125,27 +156,41 @@ public:
const Vector<size_t>* offsetsForString(const String16& val) const;
private:
static int config_sort(const size_t* lhs, const size_t* rhs, void* state);
const bool mSorted;
const bool mUTF8;
// Raw array of unique strings, in some arbitrary order.
// The following data structures represent the actual structures
// that will be generated for the final string pool.
// Raw array of unique strings, in some arbitrary order. This is the
// actual strings that appear in the final string pool, in the order
// that they will be written.
Vector<entry> mEntries;
// Array of indices into mEntries, in the order they were
// added to the pool. This can be different than mEntries
// if the same string was added multiple times (it will appear
// once in mEntries, with multiple occurrences in this array).
// This is the lookup array that will be written for finding
// the string for each offset/position in the string pool.
Vector<size_t> mEntryArray;
// Optional style span information associated with each index of
// mEntryArray.
Vector<entry_style> mEntryStyleArray;
// Mapping from indices in mEntryArray to indices in mValues.
Vector<size_t> mEntryArrayToValues;
// The following data structures are used for book-keeping as the
// string pool is constructed.
// Unique set of all the strings added to the pool, mapped to
// the first index of mEntryArray where the value was added.
DefaultKeyedVector<String16, ssize_t> mValues;
// Unique set of all (optional) identifiers of strings in the
// pool, mapping to indices in mEntries.
DefaultKeyedVector<String16, ssize_t> mIdents;
// This array maps from the original position a string was placed at
// in mEntryArray to its new position after being sorted with sortByConfig().
Vector<size_t> mOriginalPosToNewPos;
};
#endif