Loading tools/aapt2/Resource.h +16 −1 Original line number Diff line number Diff line Loading @@ -19,9 +19,10 @@ #include "ConfigDescription.h" #include "Source.h" #include "util/StringPiece.h" #include <utils/JenkinsHash.h> #include <iomanip> #include <limits> #include <sstream> Loading Loading @@ -353,4 +354,18 @@ inline bool operator==(const SourcedResourceName& lhs, const SourcedResourceName } // namespace aapt namespace std { template <> struct hash<aapt::ResourceName> { size_t operator()(const aapt::ResourceName& name) const { android::hash_t h = 0; h = android::JenkinsHashMix(h, hash<string>()(name.package)); h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type)); h = android::JenkinsHashMix(h, hash<string>()(name.entry)); return static_cast<size_t>(h); } }; } // namespace std #endif // AAPT_RESOURCE_H tools/aapt2/ResourceParser.cpp +11 −15 Original line number Diff line number Diff line Loading @@ -589,17 +589,14 @@ bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* out outResource->name.type = *parsedType; if (Maybe<StringPiece> maybeId = xml::findNonEmptyAttribute(parser, "id")) { android::Res_value val; std::u16string idStr16 = util::utf8ToUtf16(maybeId.value()); bool result = android::ResTable::stringToInt(idStr16.data(), idStr16.size(), &val); ResourceId resourceId(val.data); if (!result || !resourceId.isValid()) { if (Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "id")) { Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeId.value() << "' in <public>"); return false; } outResource->id = resourceId; outResource->id = maybeId.value(); } if (*parsedType == ResourceType::kId) { Loading @@ -626,23 +623,22 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource return false; } Maybe<StringPiece> maybeId = xml::findNonEmptyAttribute(parser, "first-id"); if (!maybeId) { Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "first-id"); if (!maybeIdStr) { mDiag->error(DiagMessage(outResource->source) << "<public-group> must have a 'first-id' attribute"); return false; } android::Res_value val; std::u16string idStr16 = util::utf8ToUtf16(maybeId.value()); bool result = android::ResTable::stringToInt(idStr16.data(), idStr16.size(), &val); ResourceId nextId(val.data); if (!result || !nextId.isValid()) { Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeId.value() << "' in <public-group>"); << "invalid resource ID '" << maybeIdStr.value() << "' in <public-group>"); return false; } ResourceId nextId = maybeId.value(); std::string comment; bool error = false; const size_t depth = parser->getDepth(); Loading tools/aapt2/ResourceUtils.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -436,6 +436,22 @@ bool tryParseBool(const StringPiece& str, bool* outValue) { return false; } Maybe<ResourceId> tryParseResourceId(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); std::u16string str16 = util::utf8ToUtf16(trimmedStr); android::Res_value value; if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { if (value.dataType == android::Res_value::TYPE_INT_HEX) { ResourceId id(value.data); if (id.isValid()) { return id; } } } return {}; } Maybe<int> tryParseSdkVersion(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); Loading tools/aapt2/ResourceUtils.h +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,11 @@ bool isAttributeReference(const StringPiece& str); */ bool tryParseBool(const StringPiece& str, bool* outValue); /** * Returns an ID if it the string represented a valid ID. */ Maybe<ResourceId> tryParseResourceId(const StringPiece& str); /** * Parses an SDK version, which can be an integer, or a letter from A-Z. */ Loading tools/aapt2/compile/IdAssigner.cpp +150 −57 Original line number Diff line number Diff line Loading @@ -19,87 +19,180 @@ #include "process/IResourceTableConsumer.h" #include "util/Util.h" #include <bitset> #include <cassert> #include <set> #include <map> namespace aapt { /** * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and ResourceEntry, * as long as there is no existing ID or the ID is the same. */ static bool assignId(IDiagnostics* diag, const ResourceId id, const ResourceName& name, ResourceTablePackage* pkg, ResourceTableType* type, ResourceEntry* entry) { if (pkg->id.value() == id.packageId()) { if (!type->id || type->id.value() == id.typeId()) { type->id = id.typeId(); if (!entry->id || entry->id.value() == id.entryId()) { entry->id = id.entryId(); return true; } } } const ResourceId existingId(pkg->id.value(), type->id ? type->id.value() : 0, entry->id ? entry->id.value() : 0); diag->error(DiagMessage() << "can't assign ID " << id << " to resource " << name << " with conflicting ID " << existingId); return false; } bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) { std::bitset<256> usedTypeIds; std::set<uint16_t> usedEntryIds; std::map<ResourceId, ResourceName> assignedIds; for (auto& package : table->packages) { assert(package->id && "packages must have manually assigned IDs"); usedTypeIds.reset(); // Type ID 0 is invalid, reserve it. usedTypeIds.set(0); // Collect used type IDs. for (auto& type : package->types) { if (type->id) { usedEntryIds.clear(); if (usedTypeIds[type->id.value()]) { // This ID is already taken! context->getDiagnostics()->error(DiagMessage() << "type '" << type->type << "' in " << "package '" << package->name << "' has " << "duplicate ID " << std::hex << (int) type->id.value() << std::dec); for (auto& entry : type->entries) { const ResourceName name(package->name, type->type, entry->name); if (mAssignedIdMap) { // Assign the pre-assigned stable ID meant for this resource. const auto iter = mAssignedIdMap->find(name); if (iter != mAssignedIdMap->end()) { const ResourceId assignedId = iter->second; const bool result = assignId(context->getDiagnostics(), assignedId, name, package.get(), type.get(), entry.get()); if (!result) { return false; } } } // Mark the type ID as taken. usedTypeIds.set(type->id.value()); if (package->id && type->id && entry->id) { // If the ID is set for this resource, then reserve it. ResourceId resourceId(package->id.value(), type->id.value(), entry->id.value()); auto result = assignedIds.insert({ resourceId, name }); const ResourceName& existingName = result.first->second; if (!result.second) { context->getDiagnostics()->error(DiagMessage() << "resource " << name << " has same ID " << resourceId << " as " << existingName); return false; } } } } } // Collect used entry IDs. for (auto& entry : type->entries) { if (entry->id) { // Mark entry ID as taken. if (!usedEntryIds.insert(entry->id.value()).second) { // This ID existed before! ResourceNameRef nameRef(package->name, type->type, entry->name); context->getDiagnostics()->error(DiagMessage() << "resource '" << nameRef << "' " << "has duplicate entry ID " << std::hex << (int) entry->id.value() << std::dec); if (mAssignedIdMap) { // Reserve all the IDs mentioned in the stable ID map. That way we won't assign // IDs that were listed in the map if they don't exist in the table. for (const auto& stableIdEntry : *mAssignedIdMap) { const ResourceName& preAssignedName = stableIdEntry.first; const ResourceId& preAssignedId = stableIdEntry.second; auto result = assignedIds.insert({ preAssignedId, preAssignedName }); const ResourceName& existingName = result.first->second; if (!result.second && existingName != preAssignedName) { context->getDiagnostics()->error(DiagMessage() << "stable ID " << preAssignedId << " for resource " << preAssignedName << " is already taken by resource " << existingName); return false; } } } // Assign unused entry IDs. const auto endUsedEntryIter = usedEntryIds.end(); auto nextUsedEntryIter = usedEntryIds.begin(); uint16_t nextId = 0; // Assign any resources without IDs the next available ID. Gaps will be filled if possible, // unless those IDs have been reserved. const auto assignedIdsIterEnd = assignedIds.end(); for (auto& package : table->packages) { assert(package->id && "packages must have manually assigned IDs"); // Build a half filled ResourceId object, which will be used to find the closest matching // reserved ID in the assignedId map. From that point the next available type ID can be // found. ResourceId resourceId(package->id.value(), 0, 0); uint8_t nextExpectedTypeId = 1; // Find the closest matching ResourceId that is <= the one with only the package set. auto nextTypeIter = assignedIds.lower_bound(resourceId); for (auto& type : package->types) { if (!type->id) { // We need to assign a type ID. Iterate over the reserved IDs until we find // some type ID that is a distance of 2 greater than the last one we've seen. // That means there is an available type ID between these reserved IDs. while (nextTypeIter != assignedIdsIterEnd) { if (nextTypeIter->first.packageId() != package->id.value()) { break; } const uint8_t typeId = nextTypeIter->first.typeId(); if (typeId > nextExpectedTypeId) { // There is a gap in the type IDs, so use the missing one. type->id = nextExpectedTypeId++; break; } // Set our expectation to be the next type ID after the reserved one we // just saw. nextExpectedTypeId = typeId + 1; // Move to the next reserved ID. ++nextTypeIter; } if (!type->id) { // We must have hit the end of the reserved IDs and not found a gap. // That means the next ID is available. type->id = nextExpectedTypeId++; } } resourceId = ResourceId(package->id.value(), type->id.value(), 0); uint16_t nextExpectedEntryId = 0; // Find the closest matching ResourceId that is <= the one with only the package // and type set. auto nextEntryIter = assignedIds.lower_bound(resourceId); for (auto& entry : type->entries) { if (!entry->id) { // Assign the next available entryID. while (nextUsedEntryIter != endUsedEntryIter && nextId == *nextUsedEntryIter) { nextId++; ++nextUsedEntryIter; } entry->id = nextId++; // We need to assign an entry ID. Iterate over the reserved IDs until we find // some entry ID that is a distance of 2 greater than the last one we've seen. // That means there is an available entry ID between these reserved IDs. while (nextEntryIter != assignedIdsIterEnd) { if (nextEntryIter->first.packageId() != package->id.value() || nextEntryIter->first.typeId() != type->id.value()) { break; } const uint16_t entryId = nextEntryIter->first.entryId(); if (entryId > nextExpectedEntryId) { // There is a gap in the entry IDs, so use the missing one. entry->id = nextExpectedEntryId++; break; } // Set our expectation to be the next type ID after the reserved one we // just saw. nextExpectedEntryId = entryId + 1; // Move to the next reserved entry ID. ++nextEntryIter; } // Assign unused type IDs. size_t nextTypeId = 0; for (auto& type : package->types) { if (!type->id) { while (nextTypeId < usedTypeIds.size() && usedTypeIds[nextTypeId]) { nextTypeId++; if (!entry->id) { // We must have hit the end of the reserved IDs and not found a gap. // That means the next ID is available. entry->id = nextExpectedEntryId++; } } type->id = static_cast<uint8_t>(nextTypeId); nextTypeId++; } } } Loading Loading
tools/aapt2/Resource.h +16 −1 Original line number Diff line number Diff line Loading @@ -19,9 +19,10 @@ #include "ConfigDescription.h" #include "Source.h" #include "util/StringPiece.h" #include <utils/JenkinsHash.h> #include <iomanip> #include <limits> #include <sstream> Loading Loading @@ -353,4 +354,18 @@ inline bool operator==(const SourcedResourceName& lhs, const SourcedResourceName } // namespace aapt namespace std { template <> struct hash<aapt::ResourceName> { size_t operator()(const aapt::ResourceName& name) const { android::hash_t h = 0; h = android::JenkinsHashMix(h, hash<string>()(name.package)); h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type)); h = android::JenkinsHashMix(h, hash<string>()(name.entry)); return static_cast<size_t>(h); } }; } // namespace std #endif // AAPT_RESOURCE_H
tools/aapt2/ResourceParser.cpp +11 −15 Original line number Diff line number Diff line Loading @@ -589,17 +589,14 @@ bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* out outResource->name.type = *parsedType; if (Maybe<StringPiece> maybeId = xml::findNonEmptyAttribute(parser, "id")) { android::Res_value val; std::u16string idStr16 = util::utf8ToUtf16(maybeId.value()); bool result = android::ResTable::stringToInt(idStr16.data(), idStr16.size(), &val); ResourceId resourceId(val.data); if (!result || !resourceId.isValid()) { if (Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "id")) { Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeId.value() << "' in <public>"); return false; } outResource->id = resourceId; outResource->id = maybeId.value(); } if (*parsedType == ResourceType::kId) { Loading @@ -626,23 +623,22 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource return false; } Maybe<StringPiece> maybeId = xml::findNonEmptyAttribute(parser, "first-id"); if (!maybeId) { Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "first-id"); if (!maybeIdStr) { mDiag->error(DiagMessage(outResource->source) << "<public-group> must have a 'first-id' attribute"); return false; } android::Res_value val; std::u16string idStr16 = util::utf8ToUtf16(maybeId.value()); bool result = android::ResTable::stringToInt(idStr16.data(), idStr16.size(), &val); ResourceId nextId(val.data); if (!result || !nextId.isValid()) { Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeId.value() << "' in <public-group>"); << "invalid resource ID '" << maybeIdStr.value() << "' in <public-group>"); return false; } ResourceId nextId = maybeId.value(); std::string comment; bool error = false; const size_t depth = parser->getDepth(); Loading
tools/aapt2/ResourceUtils.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -436,6 +436,22 @@ bool tryParseBool(const StringPiece& str, bool* outValue) { return false; } Maybe<ResourceId> tryParseResourceId(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); std::u16string str16 = util::utf8ToUtf16(trimmedStr); android::Res_value value; if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { if (value.dataType == android::Res_value::TYPE_INT_HEX) { ResourceId id(value.data); if (id.isValid()) { return id; } } } return {}; } Maybe<int> tryParseSdkVersion(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); Loading
tools/aapt2/ResourceUtils.h +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,11 @@ bool isAttributeReference(const StringPiece& str); */ bool tryParseBool(const StringPiece& str, bool* outValue); /** * Returns an ID if it the string represented a valid ID. */ Maybe<ResourceId> tryParseResourceId(const StringPiece& str); /** * Parses an SDK version, which can be an integer, or a letter from A-Z. */ Loading
tools/aapt2/compile/IdAssigner.cpp +150 −57 Original line number Diff line number Diff line Loading @@ -19,87 +19,180 @@ #include "process/IResourceTableConsumer.h" #include "util/Util.h" #include <bitset> #include <cassert> #include <set> #include <map> namespace aapt { /** * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and ResourceEntry, * as long as there is no existing ID or the ID is the same. */ static bool assignId(IDiagnostics* diag, const ResourceId id, const ResourceName& name, ResourceTablePackage* pkg, ResourceTableType* type, ResourceEntry* entry) { if (pkg->id.value() == id.packageId()) { if (!type->id || type->id.value() == id.typeId()) { type->id = id.typeId(); if (!entry->id || entry->id.value() == id.entryId()) { entry->id = id.entryId(); return true; } } } const ResourceId existingId(pkg->id.value(), type->id ? type->id.value() : 0, entry->id ? entry->id.value() : 0); diag->error(DiagMessage() << "can't assign ID " << id << " to resource " << name << " with conflicting ID " << existingId); return false; } bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) { std::bitset<256> usedTypeIds; std::set<uint16_t> usedEntryIds; std::map<ResourceId, ResourceName> assignedIds; for (auto& package : table->packages) { assert(package->id && "packages must have manually assigned IDs"); usedTypeIds.reset(); // Type ID 0 is invalid, reserve it. usedTypeIds.set(0); // Collect used type IDs. for (auto& type : package->types) { if (type->id) { usedEntryIds.clear(); if (usedTypeIds[type->id.value()]) { // This ID is already taken! context->getDiagnostics()->error(DiagMessage() << "type '" << type->type << "' in " << "package '" << package->name << "' has " << "duplicate ID " << std::hex << (int) type->id.value() << std::dec); for (auto& entry : type->entries) { const ResourceName name(package->name, type->type, entry->name); if (mAssignedIdMap) { // Assign the pre-assigned stable ID meant for this resource. const auto iter = mAssignedIdMap->find(name); if (iter != mAssignedIdMap->end()) { const ResourceId assignedId = iter->second; const bool result = assignId(context->getDiagnostics(), assignedId, name, package.get(), type.get(), entry.get()); if (!result) { return false; } } } // Mark the type ID as taken. usedTypeIds.set(type->id.value()); if (package->id && type->id && entry->id) { // If the ID is set for this resource, then reserve it. ResourceId resourceId(package->id.value(), type->id.value(), entry->id.value()); auto result = assignedIds.insert({ resourceId, name }); const ResourceName& existingName = result.first->second; if (!result.second) { context->getDiagnostics()->error(DiagMessage() << "resource " << name << " has same ID " << resourceId << " as " << existingName); return false; } } } } } // Collect used entry IDs. for (auto& entry : type->entries) { if (entry->id) { // Mark entry ID as taken. if (!usedEntryIds.insert(entry->id.value()).second) { // This ID existed before! ResourceNameRef nameRef(package->name, type->type, entry->name); context->getDiagnostics()->error(DiagMessage() << "resource '" << nameRef << "' " << "has duplicate entry ID " << std::hex << (int) entry->id.value() << std::dec); if (mAssignedIdMap) { // Reserve all the IDs mentioned in the stable ID map. That way we won't assign // IDs that were listed in the map if they don't exist in the table. for (const auto& stableIdEntry : *mAssignedIdMap) { const ResourceName& preAssignedName = stableIdEntry.first; const ResourceId& preAssignedId = stableIdEntry.second; auto result = assignedIds.insert({ preAssignedId, preAssignedName }); const ResourceName& existingName = result.first->second; if (!result.second && existingName != preAssignedName) { context->getDiagnostics()->error(DiagMessage() << "stable ID " << preAssignedId << " for resource " << preAssignedName << " is already taken by resource " << existingName); return false; } } } // Assign unused entry IDs. const auto endUsedEntryIter = usedEntryIds.end(); auto nextUsedEntryIter = usedEntryIds.begin(); uint16_t nextId = 0; // Assign any resources without IDs the next available ID. Gaps will be filled if possible, // unless those IDs have been reserved. const auto assignedIdsIterEnd = assignedIds.end(); for (auto& package : table->packages) { assert(package->id && "packages must have manually assigned IDs"); // Build a half filled ResourceId object, which will be used to find the closest matching // reserved ID in the assignedId map. From that point the next available type ID can be // found. ResourceId resourceId(package->id.value(), 0, 0); uint8_t nextExpectedTypeId = 1; // Find the closest matching ResourceId that is <= the one with only the package set. auto nextTypeIter = assignedIds.lower_bound(resourceId); for (auto& type : package->types) { if (!type->id) { // We need to assign a type ID. Iterate over the reserved IDs until we find // some type ID that is a distance of 2 greater than the last one we've seen. // That means there is an available type ID between these reserved IDs. while (nextTypeIter != assignedIdsIterEnd) { if (nextTypeIter->first.packageId() != package->id.value()) { break; } const uint8_t typeId = nextTypeIter->first.typeId(); if (typeId > nextExpectedTypeId) { // There is a gap in the type IDs, so use the missing one. type->id = nextExpectedTypeId++; break; } // Set our expectation to be the next type ID after the reserved one we // just saw. nextExpectedTypeId = typeId + 1; // Move to the next reserved ID. ++nextTypeIter; } if (!type->id) { // We must have hit the end of the reserved IDs and not found a gap. // That means the next ID is available. type->id = nextExpectedTypeId++; } } resourceId = ResourceId(package->id.value(), type->id.value(), 0); uint16_t nextExpectedEntryId = 0; // Find the closest matching ResourceId that is <= the one with only the package // and type set. auto nextEntryIter = assignedIds.lower_bound(resourceId); for (auto& entry : type->entries) { if (!entry->id) { // Assign the next available entryID. while (nextUsedEntryIter != endUsedEntryIter && nextId == *nextUsedEntryIter) { nextId++; ++nextUsedEntryIter; } entry->id = nextId++; // We need to assign an entry ID. Iterate over the reserved IDs until we find // some entry ID that is a distance of 2 greater than the last one we've seen. // That means there is an available entry ID between these reserved IDs. while (nextEntryIter != assignedIdsIterEnd) { if (nextEntryIter->first.packageId() != package->id.value() || nextEntryIter->first.typeId() != type->id.value()) { break; } const uint16_t entryId = nextEntryIter->first.entryId(); if (entryId > nextExpectedEntryId) { // There is a gap in the entry IDs, so use the missing one. entry->id = nextExpectedEntryId++; break; } // Set our expectation to be the next type ID after the reserved one we // just saw. nextExpectedEntryId = entryId + 1; // Move to the next reserved entry ID. ++nextEntryIter; } // Assign unused type IDs. size_t nextTypeId = 0; for (auto& type : package->types) { if (!type->id) { while (nextTypeId < usedTypeIds.size() && usedTypeIds[nextTypeId]) { nextTypeId++; if (!entry->id) { // We must have hit the end of the reserved IDs and not found a gap. // That means the next ID is available. entry->id = nextExpectedEntryId++; } } type->id = static_cast<uint8_t>(nextTypeId); nextTypeId++; } } } Loading