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 _ANDROID_BIG_BUFFER_H
18 #define _ANDROID_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 android {
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) noexcept;
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) : block_size_(block_size), size_(0) {
137 }
138
BigBuffer(BigBuffer && rhs)139 inline BigBuffer::BigBuffer(BigBuffer&& rhs) noexcept
140 : block_size_(rhs.block_size_), size_(rhs.size_), blocks_(std::move(rhs.blocks_)) {
141 }
142
size()143 inline size_t BigBuffer::size() const {
144 return size_;
145 }
146
block_size()147 inline size_t BigBuffer::block_size() const {
148 return block_size_;
149 }
150
151 template <typename T>
NextBlock(size_t count)152 inline T* BigBuffer::NextBlock(size_t count) {
153 static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
154 CHECK(count != 0);
155 return reinterpret_cast<T*>(NextBlockImpl(sizeof(T) * count));
156 }
157
BackUp(size_t count)158 inline void BigBuffer::BackUp(size_t count) {
159 Block& block = blocks_.back();
160 block.size -= count;
161 size_ -= count;
162 }
163
AppendBuffer(BigBuffer && buffer)164 inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
165 std::move(buffer.blocks_.begin(), buffer.blocks_.end(), std::back_inserter(blocks_));
166 size_ += buffer.size_;
167 buffer.blocks_.clear();
168 buffer.size_ = 0;
169 }
170
Pad(size_t bytes)171 inline void BigBuffer::Pad(size_t bytes) {
172 NextBlock<char>(bytes);
173 }
174
Align4()175 inline void BigBuffer::Align4() {
176 const size_t unaligned = size_ % 4;
177 if (unaligned != 0) {
178 Pad(4 - unaligned);
179 }
180 }
181
begin()182 inline BigBuffer::const_iterator BigBuffer::begin() const {
183 return blocks_.begin();
184 }
185
end()186 inline BigBuffer::const_iterator BigBuffer::end() const {
187 return blocks_.end();
188 }
189
190 } // namespace android
191
192 #endif // _ANDROID_BIG_BUFFER_H
193