summaryrefslogtreecommitdiff
path: root/tools/aapt2/io
diff options
context:
space:
mode:
authorAdam Lesinski <adamlesinski@google.com>2017-03-14 18:52:13 -0700
committerAdam Lesinski <adamlesinski@google.com>2017-03-20 16:53:46 -0700
commit06460ef0d7072114ea3280e1650f77f55e7223f4 (patch)
tree5045dafbd896153c191ee0ea14717359021175e2 /tools/aapt2/io
parent3910adfb62b6db705878058ccbae52af0fecb172 (diff)
AAPT2: Fix up file IO
This also enables an AAPT behavior that CTS tests have come to depend on. Small files that compress negatively (get larger) are stored uncompressed. Some CTS tests assume this and try to open these files by mmapping them, which is only possible if they are uncompressed. Bug: 35461578 Test: make aapt2_tests Change-Id: Id622a6150fe72477ad65d67d1bad897a8ee2ffb9
Diffstat (limited to 'tools/aapt2/io')
-rw-r--r--tools/aapt2/io/BigBufferInputStream.h56
-rw-r--r--tools/aapt2/io/BigBufferOutputStream.h48
-rw-r--r--tools/aapt2/io/BigBufferStreams.cpp86
-rw-r--r--tools/aapt2/io/Data.h144
-rw-r--r--tools/aapt2/io/File.h47
-rw-r--r--tools/aapt2/io/Io.cpp7
-rw-r--r--tools/aapt2/io/Io.h76
7 files changed, 384 insertions, 80 deletions
diff --git a/tools/aapt2/io/BigBufferInputStream.h b/tools/aapt2/io/BigBufferInputStream.h
new file mode 100644
index 000000000000..92612c744aae
--- /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 000000000000..95113bc2132c
--- /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 000000000000..eb99033e1cbe
--- /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 fdc044d86e5a..09dc7ea5b1c4 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 IData {
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 @@ class DataSegment : public IData {
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 1ef9743f65cf..7ef6d88c1e3b 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 @@ class IFile {
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 @@ class IFileCollectionIterator {
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 cab4b65f2f5a..f5c5737cb149 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 @@ namespace io {
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 33cdc7bbe498..2a34d4dd442d 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