blob: 07db73dff2c5cdecae4018a8c06ce46eb78c66f2 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinski1ab598f2015-08-14 14:26:04 -070017#include "compile/IdAssigner.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070018
19#include <map>
Ryan Mitchell9634efb2021-03-19 14:53:17 -070020#include <unordered_map>
Adam Lesinskice5e56e2016-10-21 17:56:45 -070021
Ryan Mitchell9634efb2021-03-19 14:53:17 -070022#include "android-base/expected.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070023#include "android-base/logging.h"
24
Adam Lesinskicacb28f2016-10-19 12:18:14 -070025#include "ResourceTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070026#include "process/IResourceTableConsumer.h"
27#include "util/Util.h"
28
Ryan Mitchell9634efb2021-03-19 14:53:17 -070029using android::base::expected;
30using android::base::unexpected;
31
Adam Lesinski1ab598f2015-08-14 14:26:04 -070032namespace aapt {
33
Ryan Mitchell9634efb2021-03-19 14:53:17 -070034namespace {
35template <typename T>
36using Result = expected<T, std::string>;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070037
Ryan Mitchell9634efb2021-03-19 14:53:17 -070038template <typename Id, typename Key>
39struct NextIdFinder {
40 explicit NextIdFinder(Id start_id = 0u) : next_id_(start_id){};
41
42 // Attempts to reserve an identifier for the specified key.
43 // If the identifier is already reserved by a different key, an error message is returned.
44 // Reserving identifiers must be completed before `NextId` is called for the first time.
45 Result<Id> ReserveId(Key key, Id id);
46
47 // Retrieves the next available identifier that has not been reserved.
48 std::optional<Id> NextId();
49
50 private:
51 // Attempts to set `next_id_` to the next available identifier that has not been reserved.
52 // Returns whether there were any available identifiers.
53 std::optional<Id> SkipToNextAvailableId();
54
55 Id next_id_;
56 bool next_id_called_ = false;
57 bool exhausted_ = false;
58 std::map<Id, Key> pre_assigned_ids_;
59 typename std::map<Id, Key>::iterator next_preassigned_id_;
60};
61
62struct TypeGroup {
63 explicit TypeGroup(uint8_t package_id, uint8_t type_id)
64 : package_id_(package_id), type_id_(type_id){};
65
66 // Attempts to reserve the resource id for the specified resource name.
67 // If the id is already reserved by a different name, an error message is returned.
68 // Reserving identifiers must be completed before `NextId` is called for the first time.
69 Result<std::monostate> ReserveId(const ResourceName& name, ResourceId id);
70
71 // Retrieves the next available resource id that has not been reserved.
72 Result<ResourceId> NextId();
73
74 private:
75 uint8_t package_id_;
76 uint8_t type_id_;
77 NextIdFinder<uint16_t, ResourceName> next_entry_id_;
78};
79
80struct IdAssignerContext {
81 IdAssignerContext(std::string package_name, uint8_t package_id)
82 : package_name_(std::move(package_name)), package_id_(package_id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070083 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070084
Ryan Mitchell9634efb2021-03-19 14:53:17 -070085 // Attempts to reserve the resource id for the specified resource name.
86 // Returns whether the id was reserved successfully.
87 // Reserving identifiers must be completed before `NextId` is called for the first time.
88 bool ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag);
89
90 // Retrieves the next available resource id that has not been reserved.
91 std::optional<ResourceId> NextId(const ResourceName& name, IDiagnostics* diag);
92
93 private:
94 std::string package_name_;
95 uint8_t package_id_;
96 std::map<ResourceType, TypeGroup> types_;
97 NextIdFinder<uint8_t, ResourceType> type_id_finder_ = NextIdFinder<uint8_t, ResourceType>(1);
98};
99
100} // namespace
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700101
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700102bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) {
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700103 IdAssignerContext assigned_ids(context->GetCompilationPackage(), context->GetPackageId());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700104 for (auto& package : table->packages) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700105 for (auto& type : package->types) {
106 for (auto& entry : type->entries) {
107 const ResourceName name(package->name, type->type, entry->name);
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700108 if (entry->id) {
109 if (!assigned_ids.ReserveId(name, entry->id.value(), context->GetDiagnostics())) {
110 return false;
111 }
112 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700113
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700114 if (assigned_id_map_) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700115 // Assign the pre-assigned stable ID meant for this resource.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700116 const auto iter = assigned_id_map_->find(name);
117 if (iter != assigned_id_map_->end()) {
118 const ResourceId assigned_id = iter->second;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700119 if (!assigned_ids.ReserveId(name, assigned_id, context->GetDiagnostics())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700120 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700121 }
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700122 entry->id = assigned_id;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700123 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700124 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700125 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700126 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700127 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700128
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700129 if (assigned_id_map_) {
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700130 // Reserve all the IDs mentioned in the stable ID map. That way we won't assig IDs that were
131 // listed in the map if they don't exist in the table.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700132 for (const auto& stable_id_entry : *assigned_id_map_) {
133 const ResourceName& pre_assigned_name = stable_id_entry.first;
134 const ResourceId& pre_assigned_id = stable_id_entry.second;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700135 if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, context->GetDiagnostics())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700136 return false;
137 }
138 }
139 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700140
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700141 // Assign any resources without IDs the next available ID. Gaps will be filled if possible,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700142 // unless those IDs have been reserved.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 for (auto& package : table->packages) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700144 for (auto& type : package->types) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700145 for (auto& entry : type->entries) {
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700146 const ResourceName name(package->name, type->type, entry->name);
147 if (entry->id) {
148 continue;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700149 }
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700150 auto id = assigned_ids.NextId(name, context->GetDiagnostics());
151 if (!id.has_value()) {
152 return false;
153 }
154 entry->id = id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700155 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700156 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 }
158 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700159}
160
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700161namespace {
162template <typename Id, typename Key>
163Result<Id> NextIdFinder<Id, Key>::ReserveId(Key key, Id id) {
164 CHECK(!next_id_called_) << "ReserveId cannot be called after NextId";
165 auto assign_result = pre_assigned_ids_.emplace(id, key);
166 if (!assign_result.second && assign_result.first->second != key) {
167 std::stringstream error;
168 error << "ID " << id << " is already assigned to " << assign_result.first->second;
169 return unexpected(error.str());
170 }
171 return id;
172}
173
174template <typename Id, typename Key>
175std::optional<Id> NextIdFinder<Id, Key>::NextId() {
176 if (!next_id_called_) {
177 next_id_called_ = true;
178 next_preassigned_id_ = pre_assigned_ids_.begin();
179 }
180 return SkipToNextAvailableId();
181}
182
183template <typename Id, typename Key>
184std::optional<Id> NextIdFinder<Id, Key>::SkipToNextAvailableId() {
185 if (exhausted_) {
186 return {};
187 }
188 while (next_preassigned_id_ != pre_assigned_ids_.end()) {
189 if (next_preassigned_id_->first == next_id_) {
190 if (next_id_ == std::numeric_limits<Id>::max()) {
191 // The last identifier was reserved so there are no more available identifiers.
192 exhausted_ = true;
193 return {};
194 }
195 ++next_id_;
196 ++next_preassigned_id_;
197 continue;
198 }
199 CHECK(next_preassigned_id_->first > next_id_) << "Preassigned IDs are not in sorted order";
200 break;
201 }
202 if (next_id_ == std::numeric_limits<Id>::max()) {
203 // There are no more identifiers after this one, but this one is still available so return it.
204 exhausted_ = true;
205 }
206 return next_id_++;
207}
208
209Result<std::monostate> TypeGroup::ReserveId(const ResourceName& name, ResourceId id) {
210 if (type_id_ != id.type_id()) {
211 // Currently there cannot be multiple type ids for a single type.
212 std::stringstream error;
213 error << "type '" << name.type << "' already has ID " << id.type_id();
214 return unexpected(error.str());
215 }
216
217 auto assign_result = next_entry_id_.ReserveId(name, id.entry_id());
218 if (!assign_result.has_value()) {
219 std::stringstream error;
220 error << "entry " << assign_result.error();
221 return unexpected(error.str());
222 }
223 return {};
224}
225
226Result<ResourceId> TypeGroup::NextId() {
227 auto entry_id = next_entry_id_.NextId();
228 if (!entry_id.has_value()) {
229 std::stringstream error;
230 error << "resource type ID has exceeded the maximum number of resource entries ("
231 << (std::numeric_limits<uint16_t>::max() + 1u) << ")";
232 return unexpected(error.str());
233 }
234 return ResourceId(package_id_, type_id_, entry_id.value());
235}
236
237bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag) {
238 if (package_id_ != id.package_id()) {
239 diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
240 << " because package already has ID " << id.package_id());
241 return false;
242 }
243
244 auto type = types_.find(name.type);
245 if (type == types_.end()) {
246 // The type has not been assigned an id yet. Ensure that the specified id is not being used by
247 // another type.
248 auto assign_result = type_id_finder_.ReserveId(name.type, id.type_id());
249 if (!assign_result.has_value()) {
250 diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
251 << " because type " << assign_result.error());
252 return false;
253 }
254 type = types_.emplace(name.type, TypeGroup(package_id_, id.type_id())).first;
255 }
256
257 auto assign_result = type->second.ReserveId(name, id);
258 if (!assign_result.has_value()) {
259 diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name << " because "
260 << assign_result.error());
261 return false;
262 }
263
264 return true;
265}
266
267std::optional<ResourceId> IdAssignerContext::NextId(const ResourceName& name, IDiagnostics* diag) {
268 // The package name is not known during the compile stage.
269 // Resources without a package name are considered a part of the app being linked.
270 CHECK(name.package.empty() || name.package == package_name_);
271 auto type = types_.find(name.type);
272 if (type == types_.end()) {
273 auto next_type_id = type_id_finder_.NextId();
274 CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
275 type = types_.emplace(name.type, TypeGroup(package_id_, next_type_id.value())).first;
276 }
277
278 auto assign_result = type->second.NextId();
279 if (!assign_result.has_value()) {
280 diag->Error(DiagMessage() << "can't assign resource ID to resource " << name << " because "
281 << assign_result.error());
282 return {};
283 }
284 return assign_result.value();
285}
286} // namespace
287
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700288} // namespace aapt