Merge "AAPT2: Fix up file IO"
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 57036aa..ef3797c 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -87,6 +87,7 @@
"flatten/Archive.cpp",
"flatten/TableFlattener.cpp",
"flatten/XmlFlattener.cpp",
+ "io/BigBufferStreams.cpp",
"io/File.cpp",
"io/FileSystem.cpp",
"io/Io.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 1d04b35..b855f8f 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -20,6 +20,7 @@
#include "ValueVisitor.h"
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
+#include "io/BigBufferInputStream.h"
namespace aapt {
@@ -27,8 +28,7 @@
const android::StringPiece& path) {
Source source(path);
std::string error;
- std::unique_ptr<io::ZipFileCollection> apk =
- io::ZipFileCollection::Create(path, &error);
+ std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error);
if (!apk) {
context->GetDiagnostics()->Error(DiagMessage(source) << error);
return {};
@@ -36,21 +36,18 @@
io::IFile* file = apk->FindFile("resources.arsc");
if (!file) {
- context->GetDiagnostics()->Error(DiagMessage(source)
- << "no resources.arsc found");
+ context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found");
return {};
}
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
- context->GetDiagnostics()->Error(DiagMessage(source)
- << "could not open resources.arsc");
+ context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc");
return {};
}
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), source, data->data(),
- data->size());
+ BinaryResourceParser parser(context, table.get(), source, data->data(), data->size());
if (!parser.Parse()) {
return {};
}
@@ -92,9 +89,9 @@
continue;
}
- // The resource table needs to be reserialized since it might have changed.
+ // The resource table needs to be re-serialized since it might have changed.
if (path == "resources.arsc") {
- BigBuffer buffer = BigBuffer(1024);
+ BigBuffer buffer(4096);
// TODO(adamlesinski): How to determine if there were sparse entries (and if to encode
// with sparse entries) b/35389232.
TableFlattener flattener(options, &buffer);
@@ -102,8 +99,8 @@
return false;
}
- if (!writer->StartEntry(path, ArchiveEntry::kAlign) || !writer->WriteEntry(buffer) ||
- !writer->FinishEntry()) {
+ io::BigBufferInputStream input_stream(&buffer);
+ if (!writer->WriteFile(path, ArchiveEntry::kAlign, &input_stream)) {
context->GetDiagnostics()->Error(DiagMessage()
<< "Error when writing file '" << path << "' in APK.");
return false;
@@ -113,14 +110,12 @@
std::unique_ptr<io::IData> data = file->OpenAsData();
uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
- if (!writer->StartEntry(path, compression_flags) ||
- !writer->WriteEntry(data->data(), data->size()) || !writer->FinishEntry()) {
+ if (!writer->WriteFile(path, compression_flags, data.get())) {
context->GetDiagnostics()->Error(DiagMessage()
<< "Error when writing file '" << path << "' in APK.");
return false;
}
}
-
return true;
}
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 456f686..5e9b81a 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -25,7 +25,7 @@
static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "10";
+static const char* sMinorVersion = "11";
int PrintVersion() {
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 8027f42..1fe30f0 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -37,6 +37,7 @@
#include "compile/XmlIdCollector.h"
#include "flatten/Archive.h"
#include "flatten/XmlFlattener.h"
+#include "io/BigBufferOutputStream.h"
#include "proto/ProtoSerialize.h"
#include "util/Files.h"
#include "util/Maybe.h"
@@ -46,7 +47,6 @@
using android::StringPiece;
using google::protobuf::io::CopyingOutputStreamAdaptor;
-using google::protobuf::io::ZeroCopyOutputStream;
namespace aapt {
@@ -142,10 +142,10 @@
IAaptContext* context, const CompileOptions& options,
std::vector<ResourcePathData>* out_path_data) {
const std::string& root_dir = options.res_dir.value();
- std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()),
- closedir);
+ std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
if (!d) {
- context->GetDiagnostics()->Error(DiagMessage() << strerror(errno));
+ context->GetDiagnostics()->Error(DiagMessage()
+ << android::base::SystemErrorCodeToString(errno));
return false;
}
@@ -161,10 +161,10 @@
continue;
}
- std::unique_ptr<DIR, decltype(closedir)*> subdir(
- opendir(prefix_path.data()), closedir);
+ std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir);
if (!subdir) {
- context->GetDiagnostics()->Error(DiagMessage() << strerror(errno));
+ context->GetDiagnostics()->Error(DiagMessage()
+ << android::base::SystemErrorCodeToString(errno));
return false;
}
@@ -177,8 +177,7 @@
file::AppendPath(&full_path, leaf_entry->d_name);
std::string err_str;
- Maybe<ResourcePathData> path_data =
- ExtractResourcePathData(full_path, &err_str);
+ Maybe<ResourcePathData> path_data = ExtractResourcePathData(full_path, &err_str);
if (!path_data) {
context->GetDiagnostics()->Error(DiagMessage() << err_str);
return false;
@@ -199,7 +198,7 @@
std::ifstream fin(path_data.source.path, std::ifstream::binary);
if (!fin) {
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
- << strerror(errno));
+ << android::base::SystemErrorCodeToString(errno));
return false;
}
@@ -249,8 +248,7 @@
// Create the file/zip entry.
if (!writer->StartEntry(output_path, 0)) {
- context->GetDiagnostics()->Error(DiagMessage(output_path)
- << "failed to open");
+ context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open");
return false;
}
@@ -258,21 +256,18 @@
// writer->FinishEntry().
{
// Wrap our IArchiveWriter with an adaptor that implements the
- // ZeroCopyOutputStream
- // interface.
+ // ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
if (!pb_table->SerializeToZeroCopyStream(©ing_adaptor)) {
- context->GetDiagnostics()->Error(DiagMessage(output_path)
- << "failed to write");
+ context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
return false;
}
}
if (!writer->FinishEntry()) {
- context->GetDiagnostics()->Error(DiagMessage(output_path)
- << "failed to finish entry");
+ context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish entry");
return false;
}
return true;
@@ -293,16 +288,14 @@
// writer->FinishEntry().
{
// Wrap our IArchiveWriter with an adaptor that implements the
- // ZeroCopyOutputStream
- // interface.
+ // ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
CompiledFileOutputStream output_stream(©ing_adaptor);
// Number of CompiledFiles.
output_stream.WriteLittleEndian32(1);
- std::unique_ptr<pb::CompiledFile> compiled_file =
- SerializeCompiledFileToPb(file);
+ std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
output_stream.WriteCompiledFile(compiled_file.get());
output_stream.WriteData(&buffer);
@@ -371,14 +364,12 @@
return false;
}
- std::unique_ptr<pb::CompiledFile> pb_compiled_file =
- SerializeCompiledFileToPb(xmlres->file);
+ std::unique_ptr<pb::CompiledFile> pb_compiled_file = SerializeCompiledFileToPb(xmlres->file);
out->WriteCompiledFile(pb_compiled_file.get());
out->WriteData(&buffer);
if (out->HadError()) {
- context->GetDiagnostics()->Error(DiagMessage(output_path)
- << "failed to write data");
+ context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write data");
return false;
}
return true;
@@ -388,8 +379,7 @@
const ResourcePathData& path_data,
IArchiveWriter* writer, const std::string& output_path) {
if (context->IsVerbose()) {
- context->GetDiagnostics()->Note(DiagMessage(path_data.source)
- << "compiling XML");
+ context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling XML");
}
std::unique_ptr<xml::XmlResource> xmlres;
@@ -397,7 +387,7 @@
std::ifstream fin(path_data.source.path, std::ifstream::binary);
if (!fin) {
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
- << strerror(errno));
+ << android::base::SystemErrorCodeToString(errno));
return false;
}
@@ -470,31 +460,6 @@
return true;
}
-class BigBufferOutputStream : public io::OutputStream {
- public:
- explicit BigBufferOutputStream(BigBuffer* buffer) : buffer_(buffer) {}
-
- bool Next(void** data, int* len) override {
- size_t count;
- *data = buffer_->NextBlock(&count);
- *len = static_cast<int>(count);
- return true;
- }
-
- void BackUp(int count) override { buffer_->BackUp(count); }
-
- google::protobuf::int64 ByteCount() const override {
- return buffer_->size();
- }
-
- bool HadError() const override { return false; }
-
- private:
- BigBuffer* buffer_;
-
- DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
-};
-
static bool CompilePng(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& path_data,
IArchiveWriter* writer, const std::string& output_path) {
@@ -520,7 +485,7 @@
}
BigBuffer crunched_png_buffer(4096);
- BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
+ io::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
// Ensure that we only keep the chunks we care about if we end up
// using the original PNG instead of the crunched one.
@@ -533,8 +498,7 @@
std::unique_ptr<NinePatch> nine_patch;
if (path_data.extension == "9.png") {
std::string err;
- nine_patch = NinePatch::Create(image->rows.get(), image->width,
- image->height, &err);
+ nine_patch = NinePatch::Create(image->rows.get(), image->width, image->height, &err);
if (!nine_patch) {
context->GetDiagnostics()->Error(DiagMessage() << err);
return false;
@@ -547,8 +511,7 @@
// width - 2.
image->width -= 2;
image->height -= 2;
- memmove(image->rows.get(), image->rows.get() + 1,
- image->height * sizeof(uint8_t**));
+ memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
for (int32_t h = 0; h < image->height; h++) {
memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
}
@@ -560,8 +523,7 @@
}
// Write the crunched PNG.
- if (!WritePng(context, image.get(), nine_patch.get(),
- &crunched_png_buffer_out, {})) {
+ if (!WritePng(context, image.get(), nine_patch.get(), &crunched_png_buffer_out, {})) {
return false;
}
@@ -574,24 +536,21 @@
// The re-encoded PNG is larger than the original, and there is
// no mandatory transformation. Use the original.
if (context->IsVerbose()) {
- context->GetDiagnostics()->Note(
- DiagMessage(path_data.source)
- << "original PNG is smaller than crunched PNG"
- << ", using original");
+ context->GetDiagnostics()->Note(DiagMessage(path_data.source)
+ << "original PNG is smaller than crunched PNG"
+ << ", using original");
}
- PngChunkFilter png_chunk_filter_again(content);
+ png_chunk_filter.Rewind();
BigBuffer filtered_png_buffer(4096);
- BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
- io::Copy(&filtered_png_buffer_out, &png_chunk_filter_again);
+ io::BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
+ io::Copy(&filtered_png_buffer_out, &png_chunk_filter);
buffer.AppendBuffer(std::move(filtered_png_buffer));
}
if (context->IsVerbose()) {
- // For debugging only, use the legacy PNG cruncher and compare the
- // resulting file sizes.
- // This will help catch exotic cases where the new code may generate
- // larger PNGs.
+ // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
+ // This will help catch exotic cases where the new code may generate larger PNGs.
std::stringstream legacy_stream(content);
BigBuffer legacy_buffer(4096);
Png png(context->GetDiagnostics());
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
index 5e15c88..6d6147d 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/tools/aapt2/compile/Png.cpp
@@ -33,7 +33,6 @@
namespace aapt {
constexpr bool kDebug = false;
-constexpr size_t kPngSignatureSize = 8u;
struct PngInfo {
~PngInfo() {
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index a820051..e4255e7 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -31,6 +31,9 @@
namespace aapt {
+// Size in bytes of the PNG signature.
+constexpr size_t kPngSignatureSize = 8u;
+
struct PngOptions {
int grayscale_tolerance = 0;
};
@@ -46,9 +49,9 @@
const PngOptions& options);
private:
- IDiagnostics* mDiag;
-
DISALLOW_COPY_AND_ASSIGN(Png);
+
+ IDiagnostics* mDiag;
};
/**
@@ -57,26 +60,26 @@
class PngChunkFilter : public io::InputStream {
public:
explicit PngChunkFilter(const android::StringPiece& data);
+ virtual ~PngChunkFilter() = default;
- bool Next(const void** buffer, int* len) override;
- void BackUp(int count) override;
- bool Skip(int count) override;
+ bool Next(const void** buffer, size_t* len) override;
+ void BackUp(size_t count) override;
- google::protobuf::int64 ByteCount() const override {
- return static_cast<google::protobuf::int64>(window_start_);
- }
+ bool CanRewind() const override { return true; }
+ bool Rewind() override;
+ size_t ByteCount() const override { return window_start_; }
bool HadError() const override { return error_; }
private:
- bool ConsumeWindow(const void** buffer, int* len);
+ DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
+
+ bool ConsumeWindow(const void** buffer, size_t* len);
android::StringPiece data_;
size_t window_start_ = 0;
size_t window_end_ = 0;
bool error_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
};
/**
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index edec123..f9043b5 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -71,16 +71,16 @@
PngChunkFilter::PngChunkFilter(const StringPiece& data) : data_(data) {
if (util::StartsWith(data_, kPngSignature)) {
window_start_ = 0;
- window_end_ = strlen(kPngSignature);
+ window_end_ = kPngSignatureSize;
} else {
error_ = true;
}
}
-bool PngChunkFilter::ConsumeWindow(const void** buffer, int* len) {
+bool PngChunkFilter::ConsumeWindow(const void** buffer, size_t* len) {
if (window_start_ != window_end_) {
// We have bytes to give from our window.
- const int bytes_read = (int)(window_end_ - window_start_);
+ const size_t bytes_read = window_end_ - window_start_;
*buffer = data_.data() + window_start_;
*len = bytes_read;
window_start_ = window_end_;
@@ -89,7 +89,7 @@
return false;
}
-bool PngChunkFilter::Next(const void** buffer, int* len) {
+bool PngChunkFilter::Next(const void** buffer, size_t* len) {
if (error_) {
return false;
}
@@ -113,16 +113,14 @@
// Verify the chunk length.
const uint32_t chunk_len = Peek32LE(data_.data() + window_end_);
- if (((uint64_t)chunk_len) + ((uint64_t)window_end_) + sizeof(uint32_t) >
- data_.size()) {
+ if (((uint64_t)chunk_len) + ((uint64_t)window_end_) + sizeof(uint32_t) > data_.size()) {
// Overflow.
error_ = true;
return false;
}
// Do we strip this chunk?
- const uint32_t chunk_type =
- Peek32LE(data_.data() + window_end_ + sizeof(uint32_t));
+ const uint32_t chunk_type = Peek32LE(data_.data() + window_end_ + sizeof(uint32_t));
if (IsPngChunkWhitelisted(chunk_type)) {
// Advance the window to include this chunk.
window_end_ += kMinChunkHeaderSize + chunk_len;
@@ -146,31 +144,19 @@
return false;
}
-void PngChunkFilter::BackUp(int count) {
+void PngChunkFilter::BackUp(size_t count) {
if (error_) {
return;
}
window_start_ -= count;
}
-bool PngChunkFilter::Skip(int count) {
+bool PngChunkFilter::Rewind() {
if (error_) {
return false;
}
-
- const void* buffer;
- int len;
- while (count > 0) {
- if (!Next(&buffer, &len)) {
- return false;
- }
- if (len > count) {
- BackUp(len - count);
- count = 0;
- } else {
- count -= len;
- }
- }
+ window_start_ = 0;
+ window_end_ = kPngSignatureSize;
return true;
}
diff --git a/tools/aapt2/compile/PngCrunch.cpp b/tools/aapt2/compile/PngCrunch.cpp
index 3b46d8b..ae98afc 100644
--- a/tools/aapt2/compile/PngCrunch.cpp
+++ b/tools/aapt2/compile/PngCrunch.cpp
@@ -29,12 +29,7 @@
namespace aapt {
-// Size in bytes of the PNG signature.
-constexpr size_t kPngSignatureSize = 8u;
-
-/**
- * Custom deleter that destroys libpng read and info structs.
- */
+// Custom deleter that destroys libpng read and info structs.
class PngReadStructDeleter {
public:
PngReadStructDeleter(png_structp read_ptr, png_infop info_ptr)
@@ -51,9 +46,7 @@
DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
};
-/**
- * Custom deleter that destroys libpng write and info structs.
- */
+// Custom deleter that destroys libpng write and info structs.
class PngWriteStructDeleter {
public:
PngWriteStructDeleter(png_structp write_ptr, png_infop info_ptr)
@@ -82,12 +75,11 @@
diag->Error(DiagMessage() << error_msg);
}
-static void ReadDataFromStream(png_structp png_ptr, png_bytep buffer,
- png_size_t len) {
+static void ReadDataFromStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
io::InputStream* in = (io::InputStream*)png_get_io_ptr(png_ptr);
const void* in_buffer;
- int in_len;
+ size_t in_len;
if (!in->Next(&in_buffer, &in_len)) {
if (in->HadError()) {
std::string err = in->GetError();
@@ -96,19 +88,18 @@
return;
}
- const size_t bytes_read = std::min(static_cast<size_t>(in_len), len);
+ const size_t bytes_read = std::min(in_len, len);
memcpy(buffer, in_buffer, bytes_read);
- if (bytes_read != static_cast<size_t>(in_len)) {
- in->BackUp(in_len - static_cast<int>(bytes_read));
+ if (bytes_read != in_len) {
+ in->BackUp(in_len - bytes_read);
}
}
-static void WriteDataToStream(png_structp png_ptr, png_bytep buffer,
- png_size_t len) {
+static void WriteDataToStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(png_ptr);
void* out_buffer;
- int out_len;
+ size_t out_len;
while (len > 0) {
if (!out->Next(&out_buffer, &out_len)) {
if (out->HadError()) {
@@ -118,7 +109,7 @@
return;
}
- const size_t bytes_written = std::min(static_cast<size_t>(out_len), len);
+ const size_t bytes_written = std::min(out_len, len);
memcpy(out_buffer, buffer, bytes_written);
// Advance the input buffer.
@@ -126,7 +117,7 @@
len -= bytes_written;
// Advance the output buffer.
- out_len -= static_cast<int>(bytes_written);
+ out_len -= bytes_written;
}
// If the entire output buffer wasn't used, backup.
@@ -139,41 +130,35 @@
// Read the first 8 bytes of the file looking for the PNG signature.
// Bail early if it does not match.
const png_byte* signature;
- int buffer_size;
+ size_t buffer_size;
if (!in->Next((const void**)&signature, &buffer_size)) {
- context->GetDiagnostics()->Error(
- DiagMessage() << android::base::SystemErrorCodeToString(errno));
+ context->GetDiagnostics()->Error(DiagMessage()
+ << android::base::SystemErrorCodeToString(errno));
return {};
}
- if (static_cast<size_t>(buffer_size) < kPngSignatureSize ||
- png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
- context->GetDiagnostics()->Error(
- DiagMessage() << "file signature does not match PNG signature");
+ if (buffer_size < kPngSignatureSize || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ context->GetDiagnostics()->Error(DiagMessage()
+ << "file signature does not match PNG signature");
return {};
}
// Start at the beginning of the first chunk.
- in->BackUp(buffer_size - static_cast<int>(kPngSignatureSize));
+ in->BackUp(buffer_size - kPngSignatureSize);
- // Create and initialize the png_struct with the default error and warning
- // handlers.
- // The header version is also passed in to ensure that this was built against
- // the same
+ // Create and initialize the png_struct with the default error and warning handlers.
+ // The header version is also passed in to ensure that this was built against the same
// version of libpng.
- png_structp read_ptr =
- png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ png_structp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (read_ptr == nullptr) {
- context->GetDiagnostics()->Error(
- DiagMessage() << "failed to create libpng read png_struct");
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng read png_struct");
return {};
}
// Create and initialize the memory for image header and data.
png_infop info_ptr = png_create_info_struct(read_ptr);
if (info_ptr == nullptr) {
- context->GetDiagnostics()->Error(
- DiagMessage() << "failed to create libpng read png_info");
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng read png_info");
png_destroy_read_struct(&read_ptr, nullptr, nullptr);
return {};
}
@@ -189,8 +174,7 @@
}
// Handle warnings ourselves via IDiagnostics.
- png_set_error_fn(read_ptr, (png_voidp)context->GetDiagnostics(), LogError,
- LogWarning);
+ png_set_error_fn(read_ptr, (png_voidp)context->GetDiagnostics(), LogError, LogWarning);
// Set up the read functions which read from our custom data sources.
png_set_read_fn(read_ptr, (png_voidp)in, ReadDataFromStream);
@@ -203,8 +187,7 @@
// Extract image meta-data from the various chunk headers.
uint32_t width, height;
- int bit_depth, color_type, interlace_method, compression_method,
- filter_method;
+ int bit_depth, color_type, interlace_method, compression_method, filter_method;
png_get_IHDR(read_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_method, &compression_method, &filter_method);
@@ -247,11 +230,9 @@
// 9-patch uses int32_t to index images, so we cap the image dimensions to
// something
// that can always be represented by 9-patch.
- if (width > std::numeric_limits<int32_t>::max() ||
- height > std::numeric_limits<int32_t>::max()) {
- context->GetDiagnostics()->Error(DiagMessage()
- << "PNG image dimensions are too large: "
- << width << "x" << height);
+ if (width > std::numeric_limits<int32_t>::max() || height > std::numeric_limits<int32_t>::max()) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "PNG image dimensions are too large: " << width << "x" << height);
return {};
}
@@ -263,8 +244,7 @@
CHECK(row_bytes == 4 * width); // RGBA
// Allocate one large block to hold the image.
- output_image->data =
- std::unique_ptr<uint8_t[]>(new uint8_t[height * row_bytes]);
+ output_image->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * row_bytes]);
// Create an array of rows that index into the data block.
output_image->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
@@ -281,19 +261,13 @@
return output_image;
}
-/**
- * Experimentally chosen constant to be added to the overhead of using color
- * type
- * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette
- * chunk.
- * Without this, many small PNGs encoded with palettes are larger after
- * compression than
- * the same PNGs encoded as RGBA.
- */
+// Experimentally chosen constant to be added to the overhead of using color type
+// PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
+// Without this, many small PNGs encoded with palettes are larger after compression than
+// the same PNGs encoded as RGBA.
constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
-// Pick a color type by which to encode the image, based on which color type
-// will take
+// Pick a color type by which to encode the image, based on which color type will take
// the least amount of disk space.
//
// 9-patch images traditionally have not been encoded with palettes.
@@ -372,20 +346,17 @@
return PNG_COLOR_TYPE_RGBA;
}
-// Assigns indices to the color and alpha palettes, encodes them, and then
-// invokes
+// Assigns indices to the color and alpha palettes, encodes them, and then invokes
// png_set_PLTE/png_set_tRNS.
// This must be done before writing image data.
-// Image data must be transformed to use the indices assigned within the
-// palette.
+// Image data must be transformed to use the indices assigned within the palette.
static void WritePalette(png_structp write_ptr, png_infop write_info_ptr,
std::unordered_map<uint32_t, int>* color_palette,
std::unordered_set<uint32_t>* alpha_palette) {
CHECK(color_palette->size() <= 256);
CHECK(alpha_palette->size() <= 256);
- // Populate the PNG palette struct and assign indices to the color
- // palette.
+ // Populate the PNG palette struct and assign indices to the color palette.
// Colors in the alpha palette should have smaller indices.
// This will ensure that we can truncate the alpha palette if it is
@@ -403,13 +374,11 @@
}
// Create the PNG color palette struct.
- auto color_palette_bytes =
- std::unique_ptr<png_color[]>(new png_color[color_palette->size()]);
+ auto color_palette_bytes = std::unique_ptr<png_color[]>(new png_color[color_palette->size()]);
std::unique_ptr<png_byte[]> alpha_palette_bytes;
if (!alpha_palette->empty()) {
- alpha_palette_bytes =
- std::unique_ptr<png_byte[]>(new png_byte[alpha_palette->size()]);
+ alpha_palette_bytes = std::unique_ptr<png_byte[]>(new png_byte[alpha_palette->size()]);
}
for (const auto& entry : *color_palette) {
@@ -433,23 +402,20 @@
// The bytes get copied here, so it is safe to release color_palette_bytes at
// the end of function
// scope.
- png_set_PLTE(write_ptr, write_info_ptr, color_palette_bytes.get(),
- color_palette->size());
+ png_set_PLTE(write_ptr, write_info_ptr, color_palette_bytes.get(), color_palette->size());
if (alpha_palette_bytes) {
- png_set_tRNS(write_ptr, write_info_ptr, alpha_palette_bytes.get(),
- alpha_palette->size(), nullptr);
+ png_set_tRNS(write_ptr, write_info_ptr, alpha_palette_bytes.get(), alpha_palette->size(),
+ nullptr);
}
}
// Write the 9-patch custom PNG chunks to write_info_ptr. This must be done
-// before
-// writing image data.
+// before writing image data.
static void WriteNinePatch(png_structp write_ptr, png_infop write_info_ptr,
const NinePatch* nine_patch) {
// The order of the chunks is important.
- // 9-patch code in older platforms expects the 9-patch chunk to
- // be last.
+ // 9-patch code in older platforms expects the 9-patch chunk to be last.
png_unknown_chunk unknown_chunks[3];
memset(unknown_chunks, 0, sizeof(unknown_chunks));
@@ -475,8 +441,7 @@
index++;
}
- std::unique_ptr<uint8_t[]> serialized_nine_patch =
- nine_patch->SerializeBase(&chunk_len);
+ std::unique_ptr<uint8_t[]> serialized_nine_patch = nine_patch->SerializeBase(&chunk_len);
strcpy((char*)unknown_chunks[index].name, "npTc");
unknown_chunks[index].size = chunk_len;
unknown_chunks[index].data = (png_bytep)serialized_nine_patch.get();
@@ -497,22 +462,18 @@
const PngOptions& options) {
// Create and initialize the write png_struct with the default error and
// warning handlers.
- // The header version is also passed in to ensure that this was built against
- // the same
+ // The header version is also passed in to ensure that this was built against the same
// version of libpng.
- png_structp write_ptr =
- png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (write_ptr == nullptr) {
- context->GetDiagnostics()->Error(
- DiagMessage() << "failed to create libpng write png_struct");
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng write png_struct");
return false;
}
// Allocate memory to store image header data.
png_infop write_info_ptr = png_create_info_struct(write_ptr);
if (write_info_ptr == nullptr) {
- context->GetDiagnostics()->Error(
- DiagMessage() << "failed to create libpng write png_info");
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng write png_info");
png_destroy_write_struct(&write_ptr, nullptr);
return false;
}
@@ -527,8 +488,7 @@
}
// Handle warnings with our IDiagnostics.
- png_set_error_fn(write_ptr, (png_voidp)context->GetDiagnostics(), LogError,
- LogWarning);
+ png_set_error_fn(write_ptr, (png_voidp)context->GetDiagnostics(), LogError, LogWarning);
// Set up the write functions which write to our custom data sources.
png_set_write_fn(write_ptr, (png_voidp)out, WriteDataToStream, nullptr);
@@ -599,8 +559,7 @@
context->GetDiagnostics()->Note(msg);
}
- const bool convertible_to_grayscale =
- max_gray_deviation <= options.grayscale_tolerance;
+ const bool convertible_to_grayscale = max_gray_deviation <= options.grayscale_tolerance;
const int new_color_type = PickColorType(
image->width, image->height, grayscale, convertible_to_grayscale,
@@ -715,15 +674,12 @@
}
png_write_row(write_ptr, out_row.get());
}
- } else if (new_color_type == PNG_COLOR_TYPE_RGB ||
- new_color_type == PNG_COLOR_TYPE_RGBA) {
+ } else if (new_color_type == PNG_COLOR_TYPE_RGB || new_color_type == PNG_COLOR_TYPE_RGBA) {
const size_t bpp = new_color_type == PNG_COLOR_TYPE_RGB ? 3 : 4;
if (needs_to_zero_rgb_channels_of_transparent_pixels) {
// The source RGBA data can't be used as-is, because we need to zero out
- // the RGB
- // values of transparent pixels.
- auto out_row =
- std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+ // the RGB values of transparent pixels.
+ auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
for (int32_t y = 0; y < image->height; y++) {
png_const_bytep in_row = image->rows[y];
@@ -747,8 +703,7 @@
}
} else {
// The source image can be used as-is, just tell libpng whether or not to
- // ignore
- // the alpha channel.
+ // ignore the alpha channel.
if (new_color_type == PNG_COLOR_TYPE_RGB) {
// Delete the extraneous alpha values that we appended to our buffer
// when reading the original values.
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp
index 5c96a4d..826f91b 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -21,6 +21,7 @@
#include <string>
#include <vector>
+#include "android-base/errors.h"
#include "android-base/macros.h"
#include "androidfw/StringPiece.h"
#include "ziparchive/zip_writer.h"
@@ -37,14 +38,14 @@
public:
DirectoryWriter() = default;
- bool Open(IDiagnostics* diag, const StringPiece& out_dir) {
+ bool Open(const StringPiece& out_dir) {
dir_ = out_dir.to_string();
file::FileType type = file::GetFileType(dir_);
if (type == file::FileType::kNonexistant) {
- diag->Error(DiagMessage() << "directory " << dir_ << " does not exist");
+ error_ = "directory does not exist";
return false;
} else if (type != file::FileType::kDirectory) {
- diag->Error(DiagMessage() << dir_ << " is not a directory");
+ error_ = "not a directory";
return false;
}
return true;
@@ -61,27 +62,19 @@
file_ = {fopen(full_path.data(), "wb"), fclose};
if (!file_) {
+ error_ = android::base::SystemErrorCodeToString(errno);
return false;
}
return true;
}
- bool WriteEntry(const BigBuffer& buffer) override {
+ bool Write(const void* data, int len) override {
if (!file_) {
return false;
}
- for (const BigBuffer::Block& b : buffer) {
- if (fwrite(b.buffer.get(), 1, b.size, file_.get()) != b.size) {
- file_.reset(nullptr);
- return false;
- }
- }
- return true;
- }
-
- bool WriteEntry(const void* data, size_t len) override {
- if (fwrite(data, 1, len, file_.get()) != len) {
+ if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
+ error_ = android::base::SystemErrorCodeToString(errno);
file_.reset(nullptr);
return false;
}
@@ -96,22 +89,41 @@
return true;
}
+ bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
+ if (!StartEntry(path, flags)) {
+ return false;
+ }
+
+ const void* data = nullptr;
+ size_t len = 0;
+ while (in->Next(&data, &len)) {
+ if (!Write(data, static_cast<int>(len))) {
+ return false;
+ }
+ }
+ return !in->HadError();
+ }
+
+ bool HadError() const override { return !error_.empty(); }
+
+ std::string GetError() const override { return error_; }
+
private:
DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
std::string dir_;
std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
+ std::string error_;
};
class ZipFileWriter : public IArchiveWriter {
public:
ZipFileWriter() = default;
- bool Open(IDiagnostics* diag, const StringPiece& path) {
+ bool Open(const StringPiece& path) {
file_ = {fopen(path.data(), "w+b"), fclose};
if (!file_) {
- diag->Error(DiagMessage() << "failed to Open " << path << ": "
- << strerror(errno));
+ error_ = android::base::SystemErrorCodeToString(errno);
return false;
}
writer_ = util::make_unique<ZipWriter>(file_.get());
@@ -134,37 +146,83 @@
int32_t result = writer_->StartEntry(path.data(), zip_flags);
if (result != 0) {
+ error_ = ZipWriter::ErrorCodeString(result);
return false;
}
return true;
}
- bool WriteEntry(const void* data, size_t len) override {
+ bool Write(const void* data, int len) override {
int32_t result = writer_->WriteBytes(data, len);
if (result != 0) {
+ error_ = ZipWriter::ErrorCodeString(result);
return false;
}
return true;
}
- bool WriteEntry(const BigBuffer& buffer) override {
- for (const BigBuffer::Block& b : buffer) {
- int32_t result = writer_->WriteBytes(b.buffer.get(), b.size);
- if (result != 0) {
- return false;
- }
- }
- return true;
- }
-
bool FinishEntry() override {
int32_t result = writer_->FinishEntry();
if (result != 0) {
+ error_ = ZipWriter::ErrorCodeString(result);
return false;
}
return true;
}
+ bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
+ while (true) {
+ if (!StartEntry(path, flags)) {
+ return false;
+ }
+
+ const void* data = nullptr;
+ size_t len = 0;
+ while (in->Next(&data, &len)) {
+ if (!Write(data, static_cast<int>(len))) {
+ return false;
+ }
+ }
+
+ if (in->HadError()) {
+ return false;
+ }
+
+ if (!FinishEntry()) {
+ return false;
+ }
+
+ // Check to see if the file was compressed enough. This is preserving behavior of AAPT.
+ if ((flags & ArchiveEntry::kCompress) != 0 && in->CanRewind()) {
+ ZipWriter::FileEntry last_entry;
+ int32_t result = writer_->GetLastEntry(&last_entry);
+ CHECK(result == 0);
+ if (last_entry.compressed_size + (last_entry.compressed_size / 10) >
+ last_entry.uncompressed_size) {
+ // The file was not compressed enough, rewind and store it uncompressed.
+ if (!in->Rewind()) {
+ // Well we tried, may as well keep what we had.
+ return true;
+ }
+
+ int32_t result = writer_->DiscardLastEntry();
+ if (result != 0) {
+ error_ = ZipWriter::ErrorCodeString(result);
+ return false;
+ }
+ flags &= ~ArchiveEntry::kCompress;
+
+ continue;
+ }
+ }
+ return true;
+ }
+ }
+
+ bool HadError() const override { return !error_.empty(); }
+
+ std::string GetError() const override { return error_; }
+
virtual ~ZipFileWriter() {
if (writer_) {
writer_->Finish();
@@ -176,24 +234,26 @@
std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
std::unique_ptr<ZipWriter> writer_;
+ std::string error_;
};
} // namespace
-std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(
- IDiagnostics* diag, const StringPiece& path) {
- std::unique_ptr<DirectoryWriter> writer =
- util::make_unique<DirectoryWriter>();
- if (!writer->Open(diag, path)) {
+std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
+ const StringPiece& path) {
+ std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
+ if (!writer->Open(path)) {
+ diag->Error(DiagMessage(path) << writer->GetError());
return {};
}
return std::move(writer);
}
-std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(
- IDiagnostics* diag, const StringPiece& path) {
+std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
+ const StringPiece& path) {
std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
- if (!writer->Open(diag, path)) {
+ if (!writer->Open(path)) {
+ diag->Error(DiagMessage(path) << writer->GetError());
return {};
}
return std::move(writer);
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/flatten/Archive.h
index f0681bd..4ee4ce7 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/flatten/Archive.h
@@ -26,6 +26,7 @@
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "Diagnostics.h"
+#include "io/Io.h"
#include "util/BigBuffer.h"
#include "util/Files.h"
@@ -42,19 +43,31 @@
size_t uncompressed_size;
};
-class IArchiveWriter : public google::protobuf::io::CopyingOutputStream {
+class IArchiveWriter : public ::google::protobuf::io::CopyingOutputStream {
public:
virtual ~IArchiveWriter() = default;
+ virtual bool WriteFile(const android::StringPiece& path, uint32_t flags, io::InputStream* in) = 0;
+
+ // Starts a new entry and allows caller to write bytes to it sequentially.
+ // Only use StartEntry if code you do not control needs to write to a CopyingOutputStream.
+ // Prefer WriteFile instead of manually calling StartEntry/FinishEntry.
virtual bool StartEntry(const android::StringPiece& path, uint32_t flags) = 0;
- virtual bool WriteEntry(const BigBuffer& buffer) = 0;
- virtual bool WriteEntry(const void* data, size_t len) = 0;
+
+ // Called to finish writing an entry previously started by StartEntry.
+ // Prefer WriteFile instead of manually calling StartEntry/FinishEntry.
virtual bool FinishEntry() = 0;
- // CopyingOutputStream implementations.
- bool Write(const void* buffer, int size) override {
- return WriteEntry(buffer, size);
- }
+ // CopyingOutputStream implementation that allows sequential writes to this archive. Only
+ // valid between calls to StartEntry and FinishEntry.
+ virtual bool Write(const void* buffer, int size) = 0;
+
+ // Returns true if there was an error writing to the archive.
+ // The resulting error message can be retrieved from GetError().
+ virtual bool HadError() const = 0;
+
+ // Returns the error message if HadError() returns true.
+ virtual std::string GetError() const = 0;
};
std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
diff --git a/tools/aapt2/io/BigBufferInputStream.h b/tools/aapt2/io/BigBufferInputStream.h
new file mode 100644
index 0000000..92612c7
--- /dev/null
+++ b/tools/aapt2/io/BigBufferInputStream.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_IO_BIGBUFFERINPUTSTREAM_H
+#define AAPT_IO_BIGBUFFERINPUTSTREAM_H
+
+#include "io/Io.h"
+#include "util/BigBuffer.h"
+
+namespace aapt {
+namespace io {
+
+class BigBufferInputStream : public InputStream {
+ public:
+ inline explicit BigBufferInputStream(const BigBuffer* buffer)
+ : buffer_(buffer), iter_(buffer->begin()) {}
+ virtual ~BigBufferInputStream() = default;
+
+ bool Next(const void** data, size_t* size) override;
+
+ void BackUp(size_t count) override;
+
+ bool CanRewind() const override;
+
+ bool Rewind() override;
+
+ size_t ByteCount() const override;
+
+ bool HadError() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BigBufferInputStream);
+
+ const BigBuffer* buffer_;
+ BigBuffer::const_iterator iter_;
+ size_t offset_ = 0;
+ size_t bytes_read_ = 0;
+};
+
+} // namespace io
+} // namespace aapt
+
+#endif // AAPT_IO_BIGBUFFERINPUTSTREAM_H
diff --git a/tools/aapt2/io/BigBufferOutputStream.h b/tools/aapt2/io/BigBufferOutputStream.h
new file mode 100644
index 0000000..95113bc
--- /dev/null
+++ b/tools/aapt2/io/BigBufferOutputStream.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_IO_BIGBUFFEROUTPUTSTREAM_H
+#define AAPT_IO_BIGBUFFEROUTPUTSTREAM_H
+
+#include "io/Io.h"
+#include "util/BigBuffer.h"
+
+namespace aapt {
+namespace io {
+
+class BigBufferOutputStream : public OutputStream {
+ public:
+ inline explicit BigBufferOutputStream(BigBuffer* buffer) : buffer_(buffer) {}
+ virtual ~BigBufferOutputStream() = default;
+
+ bool Next(void** data, size_t* size) override;
+
+ void BackUp(size_t count) override;
+
+ size_t ByteCount() const override;
+
+ bool HadError() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
+
+ BigBuffer* buffer_;
+};
+
+} // namespace io
+} // namespace aapt
+
+#endif // AAPT_IO_BIGBUFFEROUTPUTSTREAM_H
diff --git a/tools/aapt2/io/BigBufferStreams.cpp b/tools/aapt2/io/BigBufferStreams.cpp
new file mode 100644
index 0000000..eb99033
--- /dev/null
+++ b/tools/aapt2/io/BigBufferStreams.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "io/BigBufferInputStream.h"
+#include "io/BigBufferOutputStream.h"
+
+namespace aapt {
+namespace io {
+
+//
+// BigBufferInputStream
+//
+
+bool BigBufferInputStream::Next(const void** data, size_t* size) {
+ if (iter_ == buffer_->end()) {
+ return false;
+ }
+
+ if (offset_ == iter_->size) {
+ ++iter_;
+ if (iter_ == buffer_->end()) {
+ return false;
+ }
+ offset_ = 0;
+ }
+
+ *data = iter_->buffer.get() + offset_;
+ *size = iter_->size - offset_;
+ bytes_read_ += iter_->size - offset_;
+ offset_ = iter_->size;
+ return true;
+}
+
+void BigBufferInputStream::BackUp(size_t count) {
+ if (count > offset_) {
+ bytes_read_ -= offset_;
+ offset_ = 0;
+ } else {
+ offset_ -= count;
+ bytes_read_ -= count;
+ }
+}
+
+bool BigBufferInputStream::CanRewind() const { return true; }
+
+bool BigBufferInputStream::Rewind() {
+ iter_ = buffer_->begin();
+ offset_ = 0;
+ bytes_read_ = 0;
+ return true;
+}
+
+size_t BigBufferInputStream::ByteCount() const { return bytes_read_; }
+
+bool BigBufferInputStream::HadError() const { return false; }
+
+//
+// BigBufferOutputStream
+//
+
+bool BigBufferOutputStream::Next(void** data, size_t* size) {
+ *data = buffer_->NextBlock(size);
+ return true;
+}
+
+void BigBufferOutputStream::BackUp(size_t count) { buffer_->BackUp(count); }
+
+size_t BigBufferOutputStream::ByteCount() const { return buffer_->size(); }
+
+bool BigBufferOutputStream::HadError() const { return false; }
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index fdc044d..09dc7ea 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -22,14 +22,13 @@
#include "android-base/macros.h"
#include "utils/FileMap.h"
+#include "io/Io.h"
+
namespace aapt {
namespace io {
-/**
- * Interface for a block of contiguous memory. An instance of this interface
- * owns the data.
- */
-class IData {
+// Interface for a block of contiguous memory. An instance of this interface owns the data.
+class IData : public InputStream {
public:
virtual ~IData() = default;
@@ -40,7 +39,8 @@
class DataSegment : public IData {
public:
explicit DataSegment(std::unique_ptr<IData> data, size_t offset, size_t len)
- : data_(std::move(data)), offset_(offset), len_(len) {}
+ : data_(std::move(data)), offset_(offset), len_(len), next_read_(offset) {}
+ virtual ~DataSegment() = default;
const void* data() const override {
return static_cast<const uint8_t*>(data_->data()) + offset_;
@@ -48,63 +48,163 @@
size_t size() const override { return len_; }
+ bool Next(const void** data, size_t* size) override {
+ if (next_read_ == offset_ + len_) {
+ return false;
+ }
+ *data = static_cast<const uint8_t*>(data_->data()) + next_read_;
+ *size = len_ - (next_read_ - offset_);
+ next_read_ = offset_ + len_;
+ return true;
+ }
+
+ void BackUp(size_t count) override {
+ if (count > next_read_ - offset_) {
+ next_read_ = offset_;
+ } else {
+ next_read_ -= count;
+ }
+ }
+
+ bool CanRewind() const override { return true; }
+
+ bool Rewind() override {
+ next_read_ = offset_;
+ return true;
+ }
+
+ size_t ByteCount() const override { return next_read_ - offset_; }
+
+ bool HadError() const override { return false; }
+
private:
DISALLOW_COPY_AND_ASSIGN(DataSegment);
std::unique_ptr<IData> data_;
size_t offset_;
size_t len_;
+ size_t next_read_;
};
-/**
- * Implementation of IData that exposes a memory mapped file. The mmapped file
- * is owned by this
- * object.
- */
+// Implementation of IData that exposes a memory mapped file.
+// The mmapped file is owned by this object.
class MmappedData : public IData {
public:
- explicit MmappedData(android::FileMap&& map)
- : map_(std::forward<android::FileMap>(map)) {}
+ explicit MmappedData(android::FileMap&& map) : map_(std::forward<android::FileMap>(map)) {}
+ virtual ~MmappedData() = default;
const void* data() const override { return map_.getDataPtr(); }
size_t size() const override { return map_.getDataLength(); }
+ bool Next(const void** data, size_t* size) override {
+ if (next_read_ == map_.getDataLength()) {
+ return false;
+ }
+ *data = reinterpret_cast<const uint8_t*>(map_.getDataPtr()) + next_read_;
+ *size = map_.getDataLength() - next_read_;
+ next_read_ = map_.getDataLength();
+ return true;
+ }
+
+ void BackUp(size_t count) override {
+ if (count > next_read_) {
+ next_read_ = 0;
+ } else {
+ next_read_ -= count;
+ }
+ }
+
+ bool CanRewind() const override { return true; }
+
+ bool Rewind() override {
+ next_read_ = 0;
+ return true;
+ }
+
+ size_t ByteCount() const override { return next_read_; }
+
+ bool HadError() const override { return false; }
+
private:
+ DISALLOW_COPY_AND_ASSIGN(MmappedData);
+
android::FileMap map_;
+ size_t next_read_ = 0;
};
-/**
- * Implementation of IData that exposes a block of memory that was malloc'ed
- * (new'ed). The
- * memory is owned by this object.
- */
+// Implementation of IData that exposes a block of memory that was malloc'ed (new'ed).
+// The memory is owned by this object.
class MallocData : public IData {
public:
MallocData(std::unique_ptr<const uint8_t[]> data, size_t size)
: data_(std::move(data)), size_(size) {}
+ virtual ~MallocData() = default;
const void* data() const override { return data_.get(); }
size_t size() const override { return size_; }
+ bool Next(const void** data, size_t* size) override {
+ if (next_read_ == size_) {
+ return false;
+ }
+ *data = data_.get() + next_read_;
+ *size = size_ - next_read_;
+ next_read_ = size_;
+ return true;
+ }
+
+ void BackUp(size_t count) override {
+ if (count > next_read_) {
+ next_read_ = 0;
+ } else {
+ next_read_ -= count;
+ }
+ }
+
+ bool CanRewind() const override { return true; }
+
+ bool Rewind() override {
+ next_read_ = 0;
+ return true;
+ }
+
+ size_t ByteCount() const override { return next_read_; }
+
+ bool HadError() const override { return false; }
+
private:
+ DISALLOW_COPY_AND_ASSIGN(MallocData);
+
std::unique_ptr<const uint8_t[]> data_;
size_t size_;
+ size_t next_read_ = 0;
};
-/**
- * When mmap fails because the file has length 0, we use the EmptyData to
- * simulate data of length 0.
- */
+// When mmap fails because the file has length 0, we use the EmptyData to simulate data of length 0.
class EmptyData : public IData {
public:
+ virtual ~EmptyData() = default;
+
const void* data() const override {
static const uint8_t d = 0;
return &d;
}
size_t size() const override { return 0u; }
+
+ bool Next(const void** /*data*/, size_t* /*size*/) override { return false; }
+
+ void BackUp(size_t /*count*/) override {}
+
+ bool CanRewind() const override { return true; }
+
+ bool Rewind() override { return true; }
+
+ size_t ByteCount() const override { return 0u; }
+
+ bool HadError() const override { return false; }
};
} // namespace io
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 1ef9743..7ef6d88 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -30,40 +30,27 @@
namespace aapt {
namespace io {
-/**
- * Interface for a file, which could be a real file on the file system, or a
- * file inside
- * a ZIP archive.
- */
+// Interface for a file, which could be a real file on the file system, or a
+// file inside a ZIP archive.
class IFile {
public:
virtual ~IFile() = default;
- /**
- * Open the file and return it as a block of contiguous memory. How this
- * occurs is
- * implementation dependent. For example, if this is a file on the file
- * system, it may
- * simply mmap the contents. If this file represents a compressed file in a
- * ZIP archive,
- * it may need to inflate it to memory, incurring a copy.
- *
- * Returns nullptr on failure.
- */
+ // Open the file and return it as a block of contiguous memory. How this
+ // occurs is implementation dependent. For example, if this is a file on the file
+ // system, it may simply mmap the contents. If this file represents a compressed file in a
+ // ZIP archive, it may need to inflate it to memory, incurring a copy.
+ // Returns nullptr on failure.
virtual std::unique_ptr<IData> OpenAsData() = 0;
- /**
- * Returns the source of this file. This is for presentation to the user and
- * may not be a
- * valid file system path (for example, it may contain a '@' sign to separate
- * the files within
- * a ZIP archive from the path to the containing ZIP archive.
- */
+ // Returns the source of this file. This is for presentation to the user and
+ // may not be a valid file system path (for example, it may contain a '@' sign to separate
+ // the files within a ZIP archive from the path to the containing ZIP archive.
virtual const Source& GetSource() const = 0;
IFile* CreateFileSegment(size_t offset, size_t len);
- /** Returns whether the file was compressed before it was stored in memory. */
+ // Returns whether the file was compressed before it was stored in memory.
virtual bool WasCompressed() {
return false;
}
@@ -77,10 +64,7 @@
std::list<std::unique_ptr<IFile>> segments_;
};
-/**
- * An IFile that wraps an underlying IFile but limits it to a subsection of that
- * file.
- */
+// An IFile that wraps an underlying IFile but limits it to a subsection of that file.
class FileSegment : public IFile {
public:
explicit FileSegment(IFile* file, size_t offset, size_t len)
@@ -106,11 +90,8 @@
virtual IFile* Next() = 0;
};
-/**
- * Interface for a collection of files, all of which share a common source. That
- * source may
- * simply be the filesystem, or a ZIP archive.
- */
+// Interface for a collection of files, all of which share a common source. That source may
+// simply be the filesystem, or a ZIP archive.
class IFileCollection {
public:
virtual ~IFileCollection() = default;
diff --git a/tools/aapt2/io/Io.cpp b/tools/aapt2/io/Io.cpp
index cab4b65..f5c5737 100644
--- a/tools/aapt2/io/Io.cpp
+++ b/tools/aapt2/io/Io.cpp
@@ -16,7 +16,6 @@
#include "io/Io.h"
-#include <algorithm>
#include <cstring>
namespace aapt {
@@ -24,15 +23,15 @@
bool Copy(OutputStream* out, InputStream* in) {
const void* in_buffer;
- int in_len;
+ size_t in_len;
while (in->Next(&in_buffer, &in_len)) {
void* out_buffer;
- int out_len;
+ size_t out_len;
if (!out->Next(&out_buffer, &out_len)) {
return !out->HadError();
}
- const int bytes_to_copy = std::min(in_len, out_len);
+ const size_t bytes_to_copy = in_len < out_len ? in_len : out_len;
memcpy(out_buffer, in_buffer, bytes_to_copy);
out->BackUp(out_len - bytes_to_copy);
in->BackUp(in_len - bytes_to_copy);
diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h
index 33cdc7b..2a34d4d 100644
--- a/tools/aapt2/io/Io.h
+++ b/tools/aapt2/io/Io.h
@@ -19,42 +19,76 @@
#include <string>
-#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-
namespace aapt {
namespace io {
-/**
- * InputStream interface that inherits from protobuf's ZeroCopyInputStream,
- * but adds error handling methods to better report issues.
- *
- * The code style here matches the protobuf style.
- */
-class InputStream : public ::google::protobuf::io::ZeroCopyInputStream {
+// InputStream interface that mimics protobuf's ZeroCopyInputStream,
+// with added error handling methods to better report issues.
+class InputStream {
public:
+ virtual ~InputStream() = default;
+
+ // Returns a chunk of data for reading. data and size must not be nullptr.
+ // Returns true so long as there is more data to read, returns false if an error occurred
+ // or no data remains. If an error occurred, check HadError().
+ // The stream owns the buffer returned from this method and the buffer is invalidated
+ // anytime another mutable method is called.
+ virtual bool Next(const void** data, size_t* size) = 0;
+
+ // Backup count bytes, where count is smaller or equal to the size of the last buffer returned
+ // from Next().
+ // Useful when the last block returned from Next() wasn't fully read.
+ virtual void BackUp(size_t count) = 0;
+
+ // Returns true if this InputStream can rewind. If so, Rewind() can be called.
+ virtual bool CanRewind() const { return false; };
+
+ // Rewinds the stream to the beginning so it can be read again.
+ // Returns true if the rewind succeeded.
+ // This does nothing if CanRewind() returns false.
+ virtual bool Rewind() { return false; }
+
+ // Returns the number of bytes that have been read from the stream.
+ virtual size_t ByteCount() const = 0;
+
+ // Returns an error message if HadError() returned true.
virtual std::string GetError() const { return {}; }
+ // Returns true if an error occurred. Errors are permanent.
virtual bool HadError() const = 0;
};
-/**
- * OutputStream interface that inherits from protobuf's ZeroCopyOutputStream,
- * but adds error handling methods to better report issues.
- *
- * The code style here matches the protobuf style.
- */
-class OutputStream : public ::google::protobuf::io::ZeroCopyOutputStream {
+// OutputStream interface that mimics protobuf's ZeroCopyOutputStream,
+// with added error handling methods to better report issues.
+class OutputStream {
public:
+ virtual ~OutputStream() = default;
+
+ // Returns a buffer to which data can be written to. The data written to this buffer will
+ // eventually be written to the stream. Call BackUp() if the data written doesn't occupy the
+ // entire buffer.
+ // Return false if there was an error.
+ // The stream owns the buffer returned from this method and the buffer is invalidated
+ // anytime another mutable method is called.
+ virtual bool Next(void** data, size_t* size) = 0;
+
+ // Backup count bytes, where count is smaller or equal to the size of the last buffer returned
+ // from Next().
+ // Useful for when the last block returned from Next() wasn't fully written to.
+ virtual void BackUp(size_t count) = 0;
+
+ // Returns the number of bytes that have been written to the stream.
+ virtual size_t ByteCount() const = 0;
+
+ // Returns an error message if HadError() returned true.
virtual std::string GetError() const { return {}; }
+ // Returns true if an error occurred. Errors are permanent.
virtual bool HadError() const = 0;
};
-/**
- * Copies the data from in to out. Returns true if there was no error.
- * If there was an error, check the individual streams' HadError/GetError
- * methods.
- */
+// Copies the data from in to out. Returns false if there was an error.
+// If there was an error, check the individual streams' HadError/GetError methods.
bool Copy(OutputStream* out, InputStream* in);
} // namespace io
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 1b4d5bb..7f71589 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -38,6 +38,7 @@
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
#include "flatten/XmlFlattener.h"
+#include "io/BigBufferInputStream.h"
#include "io/FileSystem.h"
#include "io/ZipArchive.h"
#include "java/JavaClassGenerator.h"
@@ -168,34 +169,57 @@
int min_sdk_version_ = 0;
};
+static bool CopyInputStreamToArchive(io::InputStream* in, const std::string& out_path,
+ uint32_t compression_flags, IArchiveWriter* writer,
+ IAaptContext* context) {
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path << " to archive");
+ }
+
+ if (!writer->WriteFile(out_path, compression_flags, in)) {
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << out_path
+ << " to archive: " << writer->GetError());
+ return false;
+ }
+ return true;
+}
+
static bool CopyFileToArchive(io::IFile* file, const std::string& out_path,
uint32_t compression_flags,
IArchiveWriter* writer, IAaptContext* context) {
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
- context->GetDiagnostics()->Error(DiagMessage(file->GetSource())
- << "failed to open file");
+ context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file");
return false;
}
+ return CopyInputStreamToArchive(data.get(), out_path, compression_flags, writer, context);
+}
- const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
- const size_t buffer_size = data->size();
-
+static bool CopyProtoToArchive(::google::protobuf::MessageLite* proto_msg,
+ const std::string& out_path, uint32_t compression_flags,
+ IArchiveWriter* writer, IAaptContext* context) {
if (context->IsVerbose()) {
- context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path
- << " to archive");
+ context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path << " to archive");
}
if (writer->StartEntry(out_path, compression_flags)) {
- if (writer->WriteEntry(buffer, buffer_size)) {
- if (writer->FinishEntry()) {
- return true;
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
+ ::google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+ if (!proto_msg->SerializeToZeroCopyStream(&adaptor)) {
+ context->GetDiagnostics()->Error(DiagMessage()
+ << "failed to write " << out_path << " to archive");
+ return false;
}
}
- }
- context->GetDiagnostics()->Error(DiagMessage() << "failed to write file "
- << out_path);
+ if (writer->FinishEntry()) {
+ return true;
+ }
+ }
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << out_path
+ << " to archive: " << writer->GetError());
return false;
}
@@ -221,16 +245,9 @@
context->GetDiagnostics()->Note(msg);
}
- if (writer->StartEntry(path, ArchiveEntry::kCompress)) {
- if (writer->WriteEntry(buffer)) {
- if (writer->FinishEntry()) {
- return true;
- }
- }
- }
- context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << path
- << " to archive");
- return false;
+ io::BigBufferInputStream input_stream(&buffer);
+ return CopyInputStreamToArchive(&input_stream, path.to_string(), ArchiveEntry::kCompress, writer,
+ context);
}
static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source,
@@ -243,8 +260,7 @@
return {};
}
- std::unique_ptr<ResourceTable> table =
- DeserializeTableFromPb(pb_table, source, diag);
+ std::unique_ptr<ResourceTable> table = DeserializeTableFromPb(pb_table, source, diag);
if (!table) {
return {};
}
@@ -898,49 +914,18 @@
BigBuffer buffer(1024);
TableFlattener flattener(options_.table_flattener_options, &buffer);
if (!flattener.Consume(context_, table)) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to flatten resource table");
return false;
}
- if (writer->StartEntry("resources.arsc", ArchiveEntry::kAlign)) {
- if (writer->WriteEntry(buffer)) {
- if (writer->FinishEntry()) {
- return true;
- }
- }
- }
-
- context_->GetDiagnostics()->Error(
- DiagMessage() << "failed to write resources.arsc to archive");
- return false;
+ io::BigBufferInputStream input_stream(&buffer);
+ return CopyInputStreamToArchive(&input_stream, "resources.arsc", ArchiveEntry::kAlign, writer,
+ context_);
}
bool FlattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
- // Create the file/zip entry.
- if (!writer->StartEntry("resources.arsc.flat", 0)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to open");
- return false;
- }
-
- // Make sure CopyingOutputStreamAdaptor is deleted before we call
- // writer->FinishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the
- // ZeroCopyOutputStream interface.
- CopyingOutputStreamAdaptor adaptor(writer);
-
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table);
- if (!pb_table->SerializeToZeroCopyStream(&adaptor)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to write");
- return false;
- }
- }
-
- if (!writer->FinishEntry()) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed to finish entry");
- return false;
- }
- return true;
+ std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table);
+ return CopyProtoToArchive(pb_table.get(), "resources.arsc.flat", 0, writer, context_);
}
bool WriteJavaFile(ResourceTable* table,
@@ -971,8 +956,7 @@
JavaClassGenerator generator(context_, table, java_options);
if (!generator.Generate(package_name_to_generate, out_package, &fout)) {
- context_->GetDiagnostics()->Error(DiagMessage(out_path)
- << generator.getError());
+ context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.getError());
return false;
}
@@ -1484,7 +1468,6 @@
if (options_.package_type == PackageType::kStaticLib) {
if (!FlattenTableToPb(table, writer)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resources.arsc.flat");
return false;
}
} else {
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 1c9a75d..9899f80 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,12 @@
# Android Asset Packaging Tool 2.0 (AAPT2) release notes
+## Version 2.11
+### `aapt2 link ...`
+- Adds the ability to specify assets directories with the -A parameter. Assets work just like
+ assets in the original AAPT. It is not recommended to package assets with aapt2, however,
+ since the resulting APK is post-processed by other tools anyways. Assets do not get processed
+ by AAPT2, just copied, so incremental building gets slower if they are included early on.
+
## Version 2.10
### `aapt2 link ...`
- Add ability to specify package ID to compile with for regular apps (not shared or static libs).