blob: a5c2cbc0277bb8a8e750839b1d0a32e6c7b6bd6a [file] [log] [blame]
Adam Lesinski59e04c62016-02-04 15:59:23 -08001/*
2 * Copyright (C) 2016 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
17#include "Resource.h"
18#include "ResourceTable.h"
19#include "StringPool.h"
20#include "ValueVisitor.h"
21#include "proto/ProtoHelpers.h"
22#include "proto/ProtoSerialize.h"
23#include "util/BigBuffer.h"
24
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070025using google::protobuf::io::CodedOutputStream;
26using google::protobuf::io::CodedInputStream;
27using google::protobuf::io::ZeroCopyOutputStream;
28
Adam Lesinski59e04c62016-02-04 15:59:23 -080029namespace aapt {
30
31namespace {
32
33class PbSerializerVisitor : public RawValueVisitor {
34public:
35 using RawValueVisitor::visit;
36
37 /**
38 * Constructor to use when expecting to serialize any value.
39 */
40 PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Value* outPbValue) :
41 mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(outPbValue),
42 mOutPbItem(nullptr) {
43 }
44
45 /**
46 * Constructor to use when expecting to serialize an Item.
47 */
48 PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) :
49 mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(nullptr),
50 mOutPbItem(outPbItem) {
51 }
52
53 void visit(Reference* ref) override {
54 serializeReferenceToPb(*ref, getPbItem()->mutable_ref());
55 }
56
57 void visit(String* str) override {
58 getPbItem()->mutable_str()->set_idx(str->value.getIndex());
59 }
60
61 void visit(StyledString* str) override {
62 getPbItem()->mutable_str()->set_idx(str->value.getIndex());
63 }
64
65 void visit(FileReference* file) override {
66 getPbItem()->mutable_file()->set_path_idx(file->path.getIndex());
67 }
68
69 void visit(Id* id) override {
70 getPbItem()->mutable_id();
71 }
72
73 void visit(RawString* rawStr) override {
74 getPbItem()->mutable_raw_str()->set_idx(rawStr->value.getIndex());
75 }
76
77 void visit(BinaryPrimitive* prim) override {
78 android::Res_value val = {};
79 prim->flatten(&val);
80
81 pb::Primitive* pbPrim = getPbItem()->mutable_prim();
82 pbPrim->set_type(val.dataType);
83 pbPrim->set_data(val.data);
84 }
85
86 void visitItem(Item* item) override {
87 assert(false && "unimplemented item");
88 }
89
90 void visit(Attribute* attr) override {
91 pb::Attribute* pbAttr = getPbCompoundValue()->mutable_attr();
92 pbAttr->set_format_flags(attr->typeMask);
93 pbAttr->set_min_int(attr->minInt);
94 pbAttr->set_max_int(attr->maxInt);
95
96 for (auto& symbol : attr->symbols) {
97 pb::Attribute_Symbol* pbSymbol = pbAttr->add_symbols();
98 serializeItemCommonToPb(symbol.symbol, pbSymbol);
99 serializeReferenceToPb(symbol.symbol, pbSymbol->mutable_name());
100 pbSymbol->set_value(symbol.value);
101 }
102 }
103
104 void visit(Style* style) override {
105 pb::Style* pbStyle = getPbCompoundValue()->mutable_style();
106 if (style->parent) {
107 serializeReferenceToPb(style->parent.value(), pbStyle->mutable_parent());
108 serializeSourceToPb(style->parent.value().getSource(),
109 mSourcePool,
110 pbStyle->mutable_parent_source());
111 }
112
113 for (Style::Entry& entry : style->entries) {
114 pb::Style_Entry* pbEntry = pbStyle->add_entries();
115 serializeReferenceToPb(entry.key, pbEntry->mutable_key());
116
117 pb::Item* pbItem = pbEntry->mutable_item();
Adam Lesinski5f7c4612016-02-12 23:38:08 -0800118 serializeItemCommonToPb(entry.key, pbEntry);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800119 PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbItem);
120 entry.value->accept(&subVisitor);
121 }
122 }
123
124 void visit(Styleable* styleable) override {
125 pb::Styleable* pbStyleable = getPbCompoundValue()->mutable_styleable();
126 for (Reference& entry : styleable->entries) {
127 pb::Styleable_Entry* pbEntry = pbStyleable->add_entries();
128 serializeItemCommonToPb(entry, pbEntry);
129 serializeReferenceToPb(entry, pbEntry->mutable_attr());
130 }
131 }
132
133 void visit(Array* array) override {
134 pb::Array* pbArray = getPbCompoundValue()->mutable_array();
135 for (auto& value : array->items) {
136 pb::Array_Entry* pbEntry = pbArray->add_entries();
137 serializeItemCommonToPb(*value, pbEntry);
138 PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbEntry->mutable_item());
139 value->accept(&subVisitor);
140 }
141 }
142
143 void visit(Plural* plural) override {
144 pb::Plural* pbPlural = getPbCompoundValue()->mutable_plural();
145 const size_t count = plural->values.size();
146 for (size_t i = 0; i < count; i++) {
147 if (!plural->values[i]) {
148 // No plural value set here.
149 continue;
150 }
151
152 pb::Plural_Entry* pbEntry = pbPlural->add_entries();
153 pbEntry->set_arity(serializePluralEnumToPb(i));
154 pb::Item* pbElement = pbEntry->mutable_item();
155 serializeItemCommonToPb(*plural->values[i], pbEntry);
156 PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbElement);
157 plural->values[i]->accept(&subVisitor);
158 }
159 }
160
161private:
162 pb::Item* getPbItem() {
163 if (mOutPbValue) {
164 return mOutPbValue->mutable_item();
165 }
166 return mOutPbItem;
167 }
168
169 pb::CompoundValue* getPbCompoundValue() {
170 assert(mOutPbValue);
171 return mOutPbValue->mutable_compound_value();
172 }
173
174 template <typename T>
175 void serializeItemCommonToPb(const Item& item, T* pbItem) {
176 serializeSourceToPb(item.getSource(), mSourcePool, pbItem->mutable_source());
177 if (!item.getComment().empty()) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700178 pbItem->set_comment(item.getComment());
Adam Lesinski59e04c62016-02-04 15:59:23 -0800179 }
180 }
181
182 void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) {
183 if (ref.id) {
184 pbRef->set_id(ref.id.value().id);
Adam Lesinski64587af2016-02-18 18:33:06 -0800185 }
186
187 if (ref.name) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800188 StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString());
189 pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex()));
190 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800191
Adam Lesinski59e04c62016-02-04 15:59:23 -0800192 pbRef->set_private_(ref.privateReference);
193 pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType));
194 }
195
196 StringPool* mSourcePool;
197 StringPool* mSymbolPool;
198 pb::Value* mOutPbValue;
199 pb::Item* mOutPbItem;
200};
201
202} // namespace
203
204std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) {
205 // We must do this before writing the resources, since the string pool IDs may change.
206 table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
207 int diff = a.context.priority - b.context.priority;
208 if (diff < 0) return true;
209 if (diff > 0) return false;
210 diff = a.context.config.compare(b.context.config);
211 if (diff < 0) return true;
212 if (diff > 0) return false;
213 return a.value < b.value;
214 });
215 table->stringPool.prune();
216
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700217 auto pbTable = util::make_unique<pb::ResourceTable>();
Adam Lesinski59e04c62016-02-04 15:59:23 -0800218 serializeStringPoolToPb(table->stringPool, pbTable->mutable_string_pool());
219
220 StringPool sourcePool, symbolPool;
221
222 for (auto& package : table->packages) {
223 pb::Package* pbPackage = pbTable->add_packages();
224 if (package->id) {
225 pbPackage->set_package_id(package->id.value());
226 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700227 pbPackage->set_package_name(package->name);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800228
229 for (auto& type : package->types) {
230 pb::Type* pbType = pbPackage->add_types();
231 if (type->id) {
232 pbType->set_id(type->id.value());
233 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700234 pbType->set_name(toString(type->type).toString());
Adam Lesinski59e04c62016-02-04 15:59:23 -0800235
236 for (auto& entry : type->entries) {
237 pb::Entry* pbEntry = pbType->add_entries();
238 if (entry->id) {
239 pbEntry->set_id(entry->id.value());
240 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700241 pbEntry->set_name(entry->name);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800242
243 // Write the SymbolStatus struct.
244 pb::SymbolStatus* pbStatus = pbEntry->mutable_symbol_status();
245 pbStatus->set_visibility(serializeVisibilityToPb(entry->symbolStatus.state));
246 serializeSourceToPb(entry->symbolStatus.source, &sourcePool,
247 pbStatus->mutable_source());
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700248 pbStatus->set_comment(entry->symbolStatus.comment);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800249
250 for (auto& configValue : entry->values) {
251 pb::ConfigValue* pbConfigValue = pbEntry->add_config_values();
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800252 serializeConfig(configValue->config, pbConfigValue->mutable_config());
253 if (!configValue->product.empty()) {
254 pbConfigValue->mutable_config()->set_product(configValue->product);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800255 }
256
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800257 pb::Value* pbValue = pbConfigValue->mutable_value();
258 serializeSourceToPb(configValue->value->getSource(), &sourcePool,
259 pbValue->mutable_source());
260 if (!configValue->value->getComment().empty()) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700261 pbValue->set_comment(configValue->value->getComment());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800262 }
263
264 if (configValue->value->isWeak()) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800265 pbValue->set_weak(true);
266 }
267
268 PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800269 configValue->value->accept(&visitor);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800270 }
271 }
272 }
273 }
274
275 serializeStringPoolToPb(sourcePool, pbTable->mutable_source_pool());
276 serializeStringPoolToPb(symbolPool, pbTable->mutable_symbol_pool());
277 return pbTable;
278}
279
280std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file) {
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700281 auto pbFile = util::make_unique<pb::CompiledFile>();
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700282 pbFile->set_resource_name(file.name.toString());
Adam Lesinski59e04c62016-02-04 15:59:23 -0800283 pbFile->set_source_path(file.source.path);
284 serializeConfig(file.config, pbFile->mutable_config());
285
286 for (const SourcedResourceName& exported : file.exportedSymbols) {
287 pb::CompiledFile_Symbol* pbSymbol = pbFile->add_exported_symbols();
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700288 pbSymbol->set_resource_name(exported.name.toString());
Adam Lesinski59e04c62016-02-04 15:59:23 -0800289 pbSymbol->set_line_no(exported.line);
290 }
291 return pbFile;
292}
293
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700294CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : mOut(out) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800295}
296
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700297void CompiledFileOutputStream::ensureAlignedWrite() {
298 const int padding = mOut.ByteCount() % 4;
299 if (padding > 0) {
300 uint32_t zero = 0u;
301 mOut.WriteRaw(&zero, padding);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800302 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800303}
304
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700305void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
306 ensureAlignedWrite();
307 mOut.WriteLittleEndian32(val);
308}
309
310void CompiledFileOutputStream::WriteCompiledFile(const pb::CompiledFile* compiledFile) {
311 ensureAlignedWrite();
312 mOut.WriteLittleEndian64(static_cast<uint64_t>(compiledFile->ByteSize()));
313 compiledFile->SerializeWithCachedSizes(&mOut);
314}
315
316void CompiledFileOutputStream::WriteData(const BigBuffer* buffer) {
317 ensureAlignedWrite();
318 mOut.WriteLittleEndian64(static_cast<uint64_t>(buffer->size()));
319 for (const BigBuffer::Block& block : *buffer) {
320 mOut.WriteRaw(block.buffer.get(), block.size);
321 }
322}
323
324void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
325 ensureAlignedWrite();
326 mOut.WriteLittleEndian64(static_cast<uint64_t>(len));
327 mOut.WriteRaw(data, len);
328}
329
330bool CompiledFileOutputStream::HadError() {
331 return mOut.HadError();
332}
333
334CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
335 mIn(static_cast<const uint8_t*>(data), size) {
336}
337
338void CompiledFileInputStream::ensureAlignedRead() {
339 const int padding = mIn.CurrentPosition() % 4;
340 if (padding > 0) {
341 // Reads are always 4 byte aligned.
342 mIn.Skip(padding);
343 }
344}
345
346bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* outVal) {
347 ensureAlignedRead();
348 return mIn.ReadLittleEndian32(outVal);
349}
350
351bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* outVal) {
352 ensureAlignedRead();
353
354 uint64_t pbSize = 0u;
355 if (!mIn.ReadLittleEndian64(&pbSize)) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800356 return false;
357 }
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700358
359 CodedInputStream::Limit l = mIn.PushLimit(static_cast<int>(pbSize));
360
361 // Check that we haven't tried to read past the end.
362 if (static_cast<uint64_t>(mIn.BytesUntilLimit()) != pbSize) {
363 mIn.PopLimit(l);
364 mIn.PushLimit(0);
365 return false;
366 }
367
368 if (!outVal->ParsePartialFromCodedStream(&mIn)) {
369 mIn.PopLimit(l);
370 mIn.PushLimit(0);
371 return false;
372 }
373
374 mIn.PopLimit(l);
375 return true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800376}
377
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700378bool CompiledFileInputStream::ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen) {
379 ensureAlignedRead();
380
381 uint64_t pbSize = 0u;
382 if (!mIn.ReadLittleEndian64(&pbSize)) {
383 return false;
384 }
385
386 // Check that we aren't trying to read past the end.
387 if (pbSize > static_cast<uint64_t>(mIn.BytesUntilLimit())) {
388 mIn.PushLimit(0);
389 return false;
390 }
391
392 uint64_t offset = static_cast<uint64_t>(mIn.CurrentPosition());
393 if (!mIn.Skip(pbSize)) {
394 return false;
395 }
396
397 *outOffset = offset;
398 *outLen = pbSize;
399 return true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800400}
401
402} // namespace aapt