1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef AAPT_BIG_BUFFER_H
18 #define AAPT_BIG_BUFFER_H
19 
20 #include <cassert>
21 #include <cstring>
22 #include <memory>
23 #include <vector>
24 
25 namespace aapt {
26 
27 /**
28  * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory
29  * in which to write without knowing the full size of the entire payload.
30  * This is essentially a list of memory blocks. As one fills up, another
31  * block is allocated and appended to the end of the list.
32  */
33 class BigBuffer {
34 public:
35     /**
36      * A contiguous block of allocated memory.
37      */
38     struct Block {
39         /**
40          * Pointer to the memory.
41          */
42         std::unique_ptr<uint8_t[]> buffer;
43 
44         /**
45          * Size of memory that is currently occupied. The actual
46          * allocation may be larger.
47          */
48         size_t size;
49 
50     private:
51         friend class BigBuffer;
52 
53         /**
54          * The size of the memory block allocation.
55          */
56         size_t mBlockSize;
57     };
58 
59     typedef std::vector<Block>::const_iterator const_iterator;
60 
61     /**
62      * Create a BigBuffer with block allocation sizes
63      * of blockSize.
64      */
65     BigBuffer(size_t blockSize);
66 
67     BigBuffer(const BigBuffer&) = delete; // No copying.
68 
69     BigBuffer(BigBuffer&& rhs);
70 
71     /**
72      * Number of occupied bytes in all the allocated blocks.
73      */
74     size_t size() const;
75 
76     /**
77      * Returns a pointer to an array of T, where T is
78      * a POD type. The elements are zero-initialized.
79      */
80     template <typename T>
81     T* nextBlock(size_t count = 1);
82 
83     /**
84      * Moves the specified BigBuffer into this one. When this method
85      * returns, buffer is empty.
86      */
87     void appendBuffer(BigBuffer&& buffer);
88 
89     /**
90      * Pads the block with 'bytes' bytes of zero values.
91      */
92     void pad(size_t bytes);
93 
94     /**
95      * Pads the block so that it aligns on a 4 byte boundary.
96      */
97     void align4();
98 
99     const_iterator begin() const;
100     const_iterator end() const;
101 
102 private:
103     /**
104      * Returns a pointer to a buffer of the requested size.
105      * The buffer is zero-initialized.
106      */
107     void* nextBlockImpl(size_t size);
108 
109     size_t mBlockSize;
110     size_t mSize;
111     std::vector<Block> mBlocks;
112 };
113 
BigBuffer(size_t blockSize)114 inline BigBuffer::BigBuffer(size_t blockSize) : mBlockSize(blockSize), mSize(0) {
115 }
116 
BigBuffer(BigBuffer && rhs)117 inline BigBuffer::BigBuffer(BigBuffer&& rhs) :
118         mBlockSize(rhs.mBlockSize), mSize(rhs.mSize), mBlocks(std::move(rhs.mBlocks)) {
119 }
120 
size()121 inline size_t BigBuffer::size() const {
122     return mSize;
123 }
124 
125 template <typename T>
nextBlock(size_t count)126 inline T* BigBuffer::nextBlock(size_t count) {
127     assert(count != 0);
128     return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
129 }
130 
appendBuffer(BigBuffer && buffer)131 inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
132     std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
133     mSize += buffer.mSize;
134     buffer.mBlocks.clear();
135     buffer.mSize = 0;
136 }
137 
pad(size_t bytes)138 inline void BigBuffer::pad(size_t bytes) {
139     nextBlock<char>(bytes);
140 }
141 
align4()142 inline void BigBuffer::align4() {
143     const size_t unaligned = mSize % 4;
144     if (unaligned != 0) {
145         pad(4 - unaligned);
146     }
147 }
148 
begin()149 inline BigBuffer::const_iterator BigBuffer::begin() const {
150     return mBlocks.begin();
151 }
152 
end()153 inline BigBuffer::const_iterator BigBuffer::end() const {
154     return mBlocks.end();
155 }
156 
157 } // namespace aapt
158 
159 #endif // AAPT_BIG_BUFFER_H
160