Loading libs/androidfw/ResourceTypes.cpp +58 −27 Original line number Diff line number Diff line Loading @@ -1877,19 +1877,27 @@ void ResTable_config::swapHtoD() { return (l.locale > r.locale) ? 1 : -1; } // The language & region are equal, so compare the scripts and variants. // The language & region are equal, so compare the scripts, variants and // numbering systms in this order. Comparison of variants and numbering // systems should happen very infrequently (if at all.) // The comparison code relies on memcmp low-level optimizations that make it // more efficient than strncmp. const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'}; const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript; const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript; int script = memcmp(lScript, rScript, sizeof(l.localeScript)); if (script) { return script; } // The language, region and script are equal, so compare variants. // // This should happen very infrequently (if at all.) return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); int variant = memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); if (variant) { return variant; } return memcmp(l.localeNumberingSystem, r.localeNumberingSystem, sizeof(l.localeNumberingSystem)); } int ResTable_config::compare(const ResTable_config& o) const { Loading Loading @@ -2030,6 +2038,22 @@ int ResTable_config::diff(const ResTable_config& o) const { return diffs; } // There isn't a well specified "importance" order between variants and // scripts. We can't easily tell whether, say "en-Latn-US" is more or less // specific than "en-US-POSIX". // // We therefore arbitrarily decide to give priority to variants over // scripts since it seems more useful to do so. We will consider // "en-US-POSIX" to be more specific than "en-Latn-US". // // Unicode extension keywords are considered to be less important than // scripts and variants. inline int ResTable_config::getImportanceScoreOfLocale() const { return (localeVariant[0] ? 4 : 0) + (localeScript[0] && !localeScriptWasComputed ? 2: 0) + (localeNumberingSystem[0] ? 1: 0); } int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { if (locale || o.locale) { if (language[0] != o.language[0]) { Loading @@ -2043,21 +2067,7 @@ int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { } } // There isn't a well specified "importance" order between variants and // scripts. We can't easily tell whether, say "en-Latn-US" is more or less // specific than "en-US-POSIX". // // We therefore arbitrarily decide to give priority to variants over // scripts since it seems more useful to do so. We will consider // "en-US-POSIX" to be more specific than "en-Latn-US". const int score = ((localeScript[0] != '\0' && !localeScriptWasComputed) ? 1 : 0) + ((localeVariant[0] != '\0') ? 2 : 0); const int oScore = (o.localeScript[0] != '\0' && !o.localeScriptWasComputed ? 1 : 0) + ((o.localeVariant[0] != '\0') ? 2 : 0); return score - oScore; return getImportanceScoreOfLocale() - o.getImportanceScoreOfLocale(); } bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { Loading Loading @@ -2314,6 +2324,17 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, return localeMatches; } // The variants are the same, try numbering system. const bool localeNumsysMatches = strncmp(localeNumberingSystem, requested->localeNumberingSystem, sizeof(localeNumberingSystem)) == 0; const bool otherNumsysMatches = strncmp(o.localeNumberingSystem, requested->localeNumberingSystem, sizeof(localeNumberingSystem)) == 0; if (localeNumsysMatches != otherNumsysMatches) { return localeNumsysMatches; } // Finally, the languages, although equivalent, may still be different // (like for Tagalog and Filipino). Identical is better than just // equivalent. Loading Loading @@ -2781,7 +2802,7 @@ void ResTable_config::appendDirLocale(String8& out) const { return; } const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed; if (!scriptWasProvided && !localeVariant[0]) { if (!scriptWasProvided && !localeVariant[0] && !localeNumberingSystem[0]) { // Legacy format. if (out.size() > 0) { out.append("-"); Loading Loading @@ -2826,6 +2847,12 @@ void ResTable_config::appendDirLocale(String8& out) const { out.append("+"); out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant))); } if (localeNumberingSystem[0]) { out.append("+u+nu+"); out.append(localeNumberingSystem, strnlen(localeNumberingSystem, sizeof(localeNumberingSystem))); } } void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const { Loading Loading @@ -2868,10 +2895,17 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can str[charsWritten++] = '-'; } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); charsWritten += strnlen(str + charsWritten, sizeof(localeVariant)); } /* TODO: Add BCP47 extension. It requires RESTABLE_MAX_LOCALE_LEN * increase from 28 to 42 bytes (-u-nu-xxxxxxxx) */ // Add Unicode extension only if at least one other locale component is present if (localeNumberingSystem[0] != '\0' && charsWritten > 0) { static constexpr char NU_PREFIX[] = "-u-nu-"; static constexpr size_t NU_PREFIX_LEN = sizeof(NU_PREFIX) - 1; memcpy(str + charsWritten, NU_PREFIX, NU_PREFIX_LEN); charsWritten += NU_PREFIX_LEN; memcpy(str + charsWritten, localeNumberingSystem, sizeof(localeNumberingSystem)); } } struct LocaleParserState { Loading Loading @@ -3004,10 +3038,7 @@ struct LocaleParserState { } void ResTable_config::setBcp47Locale(const char* in) { locale = 0; memset(localeScript, 0, sizeof(localeScript)); memset(localeVariant, 0, sizeof(localeVariant)); memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem)); clearLocale(); const char* start = in; LocaleParserState state; Loading libs/androidfw/include/androidfw/ResourceTypes.h +7 −3 Original line number Diff line number Diff line Loading @@ -894,9 +894,10 @@ struct ResTable_package // - a 8 char variant code prefixed by a 'v' // // each separated by a single char separator, which sums up to a total of 24 // chars, (25 include the string terminator) rounded up to 28 to be 4 byte // aligned. #define RESTABLE_MAX_LOCALE_LEN 28 // chars, (25 include the string terminator). Numbering system specificator, // if present, can add up to 14 bytes (-u-nu-xxxxxxxx), giving 39 bytes, // or 40 bytes to make it 4 bytes aligned. #define RESTABLE_MAX_LOCALE_LEN 40 /** Loading Loading @@ -1303,6 +1304,9 @@ struct ResTable_config // and 0 if they're equally specific. int isLocaleMoreSpecificThan(const ResTable_config &o) const; // Returns an integer representng the imporance score of the configuration locale. int getImportanceScoreOfLocale() const; // Return true if 'this' is a better locale match than 'o' for the // 'requested' configuration. Similar to isBetterThan(), this assumes that // match() has already been used to remove any configurations that don't Loading libs/androidfw/tests/ConfigLocale_test.cpp +53 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,18 @@ TEST(ConfigLocaleTest, IsMoreSpecificThan) { fillIn("en", "US", NULL, "POSIX", &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("ar", "EG", NULL, NULL, &l); fillIn("ar", "EG", NULL, NULL, &r); memcpy(&r.localeNumberingSystem, "latn", 4); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", NULL, NULL, &l); fillIn("es", "ES", NULL, NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_FALSE(r.isMoreSpecificThan(l)); } TEST(ConfigLocaleTest, setLocale) { Loading Loading @@ -321,6 +333,22 @@ TEST(ConfigLocaleTest, getBcp47Locale_script) { EXPECT_EQ(0, strcmp("en", out)); } TEST(ConfigLocaleTest, getBcp47Locale_numberingSystem) { ResTable_config config; fillIn("en", NULL, NULL, NULL, &config); char out[RESTABLE_MAX_LOCALE_LEN]; memcpy(&config.localeNumberingSystem, "latn", 4); config.getBcp47Locale(out); EXPECT_EQ(0, strcmp("en-u-nu-latn", out)); fillIn("sr", "SR", "Latn", NULL, &config); memcpy(&config.localeNumberingSystem, "latn", 4); config.getBcp47Locale(out); EXPECT_EQ(0, strcmp("sr-Latn-SR-u-nu-latn", out)); } TEST(ConfigLocaleTest, getBcp47Locale_canonicalize) { ResTable_config config; char out[RESTABLE_MAX_LOCALE_LEN]; Loading Loading @@ -433,6 +461,11 @@ TEST(ConfigLocaleTest, match) { fillIn("ar", "XB", NULL, NULL, &requested); // Even if they are pseudo-locales, exactly equal locales match. EXPECT_TRUE(supported.match(requested)); fillIn("ar", "EG", NULL, NULL, &supported); fillIn("ar", "TN", NULL, NULL, &requested); memcpy(&supported.localeNumberingSystem, "latn", 4); EXPECT_TRUE(supported.match(requested)); } TEST(ConfigLocaleTest, match_emptyScript) { Loading Loading @@ -758,6 +791,26 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) { EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); } TEST(ConfigLocaleTest, isLocaleBetterThan_numberingSystem) { ResTable_config config1, config2, request; fillIn("ar", "EG", NULL, NULL, &request); memcpy(&request.localeNumberingSystem, "latn", 4); fillIn("ar", NULL, NULL, NULL, &config1); memcpy(&config1.localeNumberingSystem, "latn", 4); fillIn("ar", NULL, NULL, NULL, &config2); EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request)); EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); fillIn("ar", "EG", NULL, NULL, &request); memcpy(&request.localeNumberingSystem, "latn", 4); fillIn("ar", "TN", NULL, NULL, &config1); memcpy(&config1.localeNumberingSystem, "latn", 4); fillIn("ar", NULL, NULL, NULL, &config2); EXPECT_TRUE(config2.isLocaleBetterThan(config1, &request)); EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request)); } // Default resources are considered better matches for US English // and US-like English locales than International English locales TEST(ConfigLocaleTest, isLocaleBetterThan_UsEnglishIsSpecial) { Loading Loading
libs/androidfw/ResourceTypes.cpp +58 −27 Original line number Diff line number Diff line Loading @@ -1877,19 +1877,27 @@ void ResTable_config::swapHtoD() { return (l.locale > r.locale) ? 1 : -1; } // The language & region are equal, so compare the scripts and variants. // The language & region are equal, so compare the scripts, variants and // numbering systms in this order. Comparison of variants and numbering // systems should happen very infrequently (if at all.) // The comparison code relies on memcmp low-level optimizations that make it // more efficient than strncmp. const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'}; const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript; const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript; int script = memcmp(lScript, rScript, sizeof(l.localeScript)); if (script) { return script; } // The language, region and script are equal, so compare variants. // // This should happen very infrequently (if at all.) return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); int variant = memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); if (variant) { return variant; } return memcmp(l.localeNumberingSystem, r.localeNumberingSystem, sizeof(l.localeNumberingSystem)); } int ResTable_config::compare(const ResTable_config& o) const { Loading Loading @@ -2030,6 +2038,22 @@ int ResTable_config::diff(const ResTable_config& o) const { return diffs; } // There isn't a well specified "importance" order between variants and // scripts. We can't easily tell whether, say "en-Latn-US" is more or less // specific than "en-US-POSIX". // // We therefore arbitrarily decide to give priority to variants over // scripts since it seems more useful to do so. We will consider // "en-US-POSIX" to be more specific than "en-Latn-US". // // Unicode extension keywords are considered to be less important than // scripts and variants. inline int ResTable_config::getImportanceScoreOfLocale() const { return (localeVariant[0] ? 4 : 0) + (localeScript[0] && !localeScriptWasComputed ? 2: 0) + (localeNumberingSystem[0] ? 1: 0); } int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { if (locale || o.locale) { if (language[0] != o.language[0]) { Loading @@ -2043,21 +2067,7 @@ int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { } } // There isn't a well specified "importance" order between variants and // scripts. We can't easily tell whether, say "en-Latn-US" is more or less // specific than "en-US-POSIX". // // We therefore arbitrarily decide to give priority to variants over // scripts since it seems more useful to do so. We will consider // "en-US-POSIX" to be more specific than "en-Latn-US". const int score = ((localeScript[0] != '\0' && !localeScriptWasComputed) ? 1 : 0) + ((localeVariant[0] != '\0') ? 2 : 0); const int oScore = (o.localeScript[0] != '\0' && !o.localeScriptWasComputed ? 1 : 0) + ((o.localeVariant[0] != '\0') ? 2 : 0); return score - oScore; return getImportanceScoreOfLocale() - o.getImportanceScoreOfLocale(); } bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { Loading Loading @@ -2314,6 +2324,17 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, return localeMatches; } // The variants are the same, try numbering system. const bool localeNumsysMatches = strncmp(localeNumberingSystem, requested->localeNumberingSystem, sizeof(localeNumberingSystem)) == 0; const bool otherNumsysMatches = strncmp(o.localeNumberingSystem, requested->localeNumberingSystem, sizeof(localeNumberingSystem)) == 0; if (localeNumsysMatches != otherNumsysMatches) { return localeNumsysMatches; } // Finally, the languages, although equivalent, may still be different // (like for Tagalog and Filipino). Identical is better than just // equivalent. Loading Loading @@ -2781,7 +2802,7 @@ void ResTable_config::appendDirLocale(String8& out) const { return; } const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed; if (!scriptWasProvided && !localeVariant[0]) { if (!scriptWasProvided && !localeVariant[0] && !localeNumberingSystem[0]) { // Legacy format. if (out.size() > 0) { out.append("-"); Loading Loading @@ -2826,6 +2847,12 @@ void ResTable_config::appendDirLocale(String8& out) const { out.append("+"); out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant))); } if (localeNumberingSystem[0]) { out.append("+u+nu+"); out.append(localeNumberingSystem, strnlen(localeNumberingSystem, sizeof(localeNumberingSystem))); } } void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const { Loading Loading @@ -2868,10 +2895,17 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can str[charsWritten++] = '-'; } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); charsWritten += strnlen(str + charsWritten, sizeof(localeVariant)); } /* TODO: Add BCP47 extension. It requires RESTABLE_MAX_LOCALE_LEN * increase from 28 to 42 bytes (-u-nu-xxxxxxxx) */ // Add Unicode extension only if at least one other locale component is present if (localeNumberingSystem[0] != '\0' && charsWritten > 0) { static constexpr char NU_PREFIX[] = "-u-nu-"; static constexpr size_t NU_PREFIX_LEN = sizeof(NU_PREFIX) - 1; memcpy(str + charsWritten, NU_PREFIX, NU_PREFIX_LEN); charsWritten += NU_PREFIX_LEN; memcpy(str + charsWritten, localeNumberingSystem, sizeof(localeNumberingSystem)); } } struct LocaleParserState { Loading Loading @@ -3004,10 +3038,7 @@ struct LocaleParserState { } void ResTable_config::setBcp47Locale(const char* in) { locale = 0; memset(localeScript, 0, sizeof(localeScript)); memset(localeVariant, 0, sizeof(localeVariant)); memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem)); clearLocale(); const char* start = in; LocaleParserState state; Loading
libs/androidfw/include/androidfw/ResourceTypes.h +7 −3 Original line number Diff line number Diff line Loading @@ -894,9 +894,10 @@ struct ResTable_package // - a 8 char variant code prefixed by a 'v' // // each separated by a single char separator, which sums up to a total of 24 // chars, (25 include the string terminator) rounded up to 28 to be 4 byte // aligned. #define RESTABLE_MAX_LOCALE_LEN 28 // chars, (25 include the string terminator). Numbering system specificator, // if present, can add up to 14 bytes (-u-nu-xxxxxxxx), giving 39 bytes, // or 40 bytes to make it 4 bytes aligned. #define RESTABLE_MAX_LOCALE_LEN 40 /** Loading Loading @@ -1303,6 +1304,9 @@ struct ResTable_config // and 0 if they're equally specific. int isLocaleMoreSpecificThan(const ResTable_config &o) const; // Returns an integer representng the imporance score of the configuration locale. int getImportanceScoreOfLocale() const; // Return true if 'this' is a better locale match than 'o' for the // 'requested' configuration. Similar to isBetterThan(), this assumes that // match() has already been used to remove any configurations that don't Loading
libs/androidfw/tests/ConfigLocale_test.cpp +53 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,18 @@ TEST(ConfigLocaleTest, IsMoreSpecificThan) { fillIn("en", "US", NULL, "POSIX", &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("ar", "EG", NULL, NULL, &l); fillIn("ar", "EG", NULL, NULL, &r); memcpy(&r.localeNumberingSystem, "latn", 4); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", NULL, NULL, &l); fillIn("es", "ES", NULL, NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_FALSE(r.isMoreSpecificThan(l)); } TEST(ConfigLocaleTest, setLocale) { Loading Loading @@ -321,6 +333,22 @@ TEST(ConfigLocaleTest, getBcp47Locale_script) { EXPECT_EQ(0, strcmp("en", out)); } TEST(ConfigLocaleTest, getBcp47Locale_numberingSystem) { ResTable_config config; fillIn("en", NULL, NULL, NULL, &config); char out[RESTABLE_MAX_LOCALE_LEN]; memcpy(&config.localeNumberingSystem, "latn", 4); config.getBcp47Locale(out); EXPECT_EQ(0, strcmp("en-u-nu-latn", out)); fillIn("sr", "SR", "Latn", NULL, &config); memcpy(&config.localeNumberingSystem, "latn", 4); config.getBcp47Locale(out); EXPECT_EQ(0, strcmp("sr-Latn-SR-u-nu-latn", out)); } TEST(ConfigLocaleTest, getBcp47Locale_canonicalize) { ResTable_config config; char out[RESTABLE_MAX_LOCALE_LEN]; Loading Loading @@ -433,6 +461,11 @@ TEST(ConfigLocaleTest, match) { fillIn("ar", "XB", NULL, NULL, &requested); // Even if they are pseudo-locales, exactly equal locales match. EXPECT_TRUE(supported.match(requested)); fillIn("ar", "EG", NULL, NULL, &supported); fillIn("ar", "TN", NULL, NULL, &requested); memcpy(&supported.localeNumberingSystem, "latn", 4); EXPECT_TRUE(supported.match(requested)); } TEST(ConfigLocaleTest, match_emptyScript) { Loading Loading @@ -758,6 +791,26 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) { EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); } TEST(ConfigLocaleTest, isLocaleBetterThan_numberingSystem) { ResTable_config config1, config2, request; fillIn("ar", "EG", NULL, NULL, &request); memcpy(&request.localeNumberingSystem, "latn", 4); fillIn("ar", NULL, NULL, NULL, &config1); memcpy(&config1.localeNumberingSystem, "latn", 4); fillIn("ar", NULL, NULL, NULL, &config2); EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request)); EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); fillIn("ar", "EG", NULL, NULL, &request); memcpy(&request.localeNumberingSystem, "latn", 4); fillIn("ar", "TN", NULL, NULL, &config1); memcpy(&config1.localeNumberingSystem, "latn", 4); fillIn("ar", NULL, NULL, NULL, &config2); EXPECT_TRUE(config2.isLocaleBetterThan(config1, &request)); EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request)); } // Default resources are considered better matches for US English // and US-like English locales than International English locales TEST(ConfigLocaleTest, isLocaleBetterThan_UsEnglishIsSpecial) { Loading