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 <cstring>
21 #include <memory>
22 #include <string>
23 #include <type_traits>
24 #include <vector>
25 
26 #include "android-base/logging.h"
27 #include "android-base/macros.h"
28 
29 namespace aapt {
30 
31 /**
32  * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory
33  * in which to write without knowing the full size of the entire payload.
34  * This is essentially a list of memory blocks. As one fills up, another
35  * block is allocated and appended to the end of the list.
36  */
37 class BigBuffer {
38  public:
39   /**
40    * A contiguous block of allocated memory.
41    */
42   struct Block {
43     /**
44      * Pointer to the memory.
45      */
46     std::unique_ptr<uint8_t[]> buffer;
47 
48     /**
49      * Size of memory that is currently occupied. The actual
50      * allocation may be larger.
51      */
52     size_t size;
53 
54    private:
55     friend class BigBuffer;
56 
57     /**
58      * The size of the memory block allocation.
59      */
60     size_t block_size_;
61   };
62 
63   typedef std::vector<Block>::const_iterator const_iterator;
64 
65   /**
66    * Create a BigBuffer with block allocation sizes
67    * of block_size.
68    */
69   explicit BigBuffer(size_t block_size);
70 
71   BigBuffer(BigBuffer&& rhs);
72 
73   /**
74    * Number of occupied bytes in all the allocated blocks.
75    */
76   size_t size() const;
77 
78   /**
79    * Returns a pointer to an array of T, where T is
80    * a POD type. The elements are zero-initialized.
81    */
82   template <typename T>
83   T* NextBlock(size_t count = 1);
84 
85   /**
86    * Returns the next block available and puts the size in out_count.
87    * This is useful for grabbing blocks where the size doesn't matter.
88    * Use BackUp() to give back any bytes that were not used.
89    */
90   void* NextBlock(size_t* out_count);
91 
92   /**
93    * Backs up count bytes. This must only be called after NextBlock()
94    * and can not be larger than sizeof(T) * count of the last NextBlock()
95    * call.
96    */
97   void BackUp(size_t count);
98 
99   /**
100    * Moves the specified BigBuffer into this one. When this method
101    * returns, buffer is empty.
102    */
103   void AppendBuffer(BigBuffer&& buffer);
104 
105   /**
106    * Pads the block with 'bytes' bytes of zero values.
107    */
108   void Pad(size_t bytes);
109 
110   /**
111    * Pads the block so that it aligns on a 4 byte boundary.
112    */
113   void Align4();
114 
115   size_t block_size() const;
116 
117   const_iterator begin() const;
118   const_iterator end() const;
119 
120   std::string to_string() const;
121 
122  private:
123   DISALLOW_COPY_AND_ASSIGN(BigBuffer);
124 
125   /**
126    * Returns a pointer to a buffer of the requested size.
127    * The buffer is zero-initialized.
128    */
129   void* NextBlockImpl(size_t size);
130 
131   size_t block_size_;
132   size_t size_;
133   std::vector<Block> blocks_;
134 };
135 
BigBuffer(size_t block_size)136 inline BigBuffer::BigBuffer(size_t block_size)
137     : block_size_(block_size), size_(0) {}
138 
BigBuffer(BigBuffer && rhs)139 inline BigBuffer::BigBuffer(BigBuffer&& rhs)
140     : block_size_(rhs.block_size_),
141       size_(rhs.size_),
142       blocks_(std::move(rhs.blocks_)) {}
143 
size()144 inline size_t BigBuffer::size() const { return size_; }
145 
block_size()146 inline size_t BigBuffer::block_size() const { return block_size_; }
147 
148 template <typename T>
NextBlock(size_t count)149 inline T* BigBuffer::NextBlock(size_t count) {
150   static_assert(std::is_standard_layout<T>::value,
151                 "T must be standard_layout type");
152   CHECK(count != 0);
153   return reinterpret_cast<T*>(NextBlockImpl(sizeof(T) * count));
154 }
155 
BackUp(size_t count)156 inline void BigBuffer::BackUp(size_t count) {
157   Block& block = blocks_.back();
158   block.size -= count;
159   size_ -= count;
160 }
161 
AppendBuffer(BigBuffer && buffer)162 inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
163   std::move(buffer.blocks_.begin(), buffer.blocks_.end(),
164             std::back_inserter(blocks_));
165   size_ += buffer.size_;
166   buffer.blocks_.clear();
167   buffer.size_ = 0;
168 }
169 
Pad(size_t bytes)170 inline void BigBuffer::Pad(size_t bytes) { NextBlock<char>(bytes); }
171 
Align4()172 inline void BigBuffer::Align4() {
173   const size_t unaligned = size_ % 4;
174   if (unaligned != 0) {
175     Pad(4 - unaligned);
176   }
177 }
178 
begin()179 inline BigBuffer::const_iterator BigBuffer::begin() const {
180   return blocks_.begin();
181 }
182 
end()183 inline BigBuffer::const_iterator BigBuffer::end() const {
184   return blocks_.end();
185 }
186 
187 }  // namespace aapt
188 
189 #endif  // AAPT_BIG_BUFFER_H
190