blob: 3391391856fb7085876687eb200d43e96fd40cf2 [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001/*
2 * Copyright 2021 Google Inc. All rights reserved.
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 FLATBUFFERS_VECTOR_DOWNWARD_H_
18#define FLATBUFFERS_VECTOR_DOWNWARD_H_
19
20#include "flatbuffers/base.h"
21#include "flatbuffers/default_allocator.h"
22#include "flatbuffers/detached_buffer.h"
23
24namespace flatbuffers {
25
26// This is a minimal replication of std::vector<uint8_t> functionality,
27// except growing from higher to lower addresses. i.e push_back() inserts data
28// in the lowest address in the vector.
29// Since this vector leaves the lower part unused, we support a "scratch-pad"
30// that can be stored there for temporary data, to share the allocated space.
31// Essentially, this supports 2 std::vectors in a single buffer.
32class vector_downward {
33 public:
34 explicit vector_downward(size_t initial_size, Allocator *allocator,
35 bool own_allocator, size_t buffer_minalign)
36 : allocator_(allocator),
37 own_allocator_(own_allocator),
38 initial_size_(initial_size),
39 buffer_minalign_(buffer_minalign),
40 reserved_(0),
41 size_(0),
42 buf_(nullptr),
43 cur_(nullptr),
44 scratch_(nullptr) {}
45
46 vector_downward(vector_downward &&other)
47 // clang-format on
48 : allocator_(other.allocator_),
49 own_allocator_(other.own_allocator_),
50 initial_size_(other.initial_size_),
51 buffer_minalign_(other.buffer_minalign_),
52 reserved_(other.reserved_),
53 size_(other.size_),
54 buf_(other.buf_),
55 cur_(other.cur_),
56 scratch_(other.scratch_) {
57 // No change in other.allocator_
58 // No change in other.initial_size_
59 // No change in other.buffer_minalign_
60 other.own_allocator_ = false;
61 other.reserved_ = 0;
62 other.buf_ = nullptr;
63 other.cur_ = nullptr;
64 other.scratch_ = nullptr;
65 }
66
67 vector_downward &operator=(vector_downward &&other) {
68 // Move construct a temporary and swap idiom
69 vector_downward temp(std::move(other));
70 swap(temp);
71 return *this;
72 }
73
74 ~vector_downward() {
75 clear_buffer();
76 clear_allocator();
77 }
78
79 void reset() {
80 clear_buffer();
81 clear();
82 }
83
84 void clear() {
85 if (buf_) {
86 cur_ = buf_ + reserved_;
87 } else {
88 reserved_ = 0;
89 cur_ = nullptr;
90 }
91 size_ = 0;
92 clear_scratch();
93 }
94
95 void clear_scratch() { scratch_ = buf_; }
96
97 void clear_allocator() {
98 if (own_allocator_ && allocator_) { delete allocator_; }
99 allocator_ = nullptr;
100 own_allocator_ = false;
101 }
102
103 void clear_buffer() {
104 if (buf_) Deallocate(allocator_, buf_, reserved_);
105 buf_ = nullptr;
106 }
107
108 // Relinquish the pointer to the caller.
109 uint8_t *release_raw(size_t &allocated_bytes, size_t &offset) {
110 auto *buf = buf_;
111 allocated_bytes = reserved_;
112 offset = static_cast<size_t>(cur_ - buf_);
113
114 // release_raw only relinquishes the buffer ownership.
115 // Does not deallocate or reset the allocator. Destructor will do that.
116 buf_ = nullptr;
117 clear();
118 return buf;
119 }
120
121 // Relinquish the pointer to the caller.
122 DetachedBuffer release() {
123 // allocator ownership (if any) is transferred to DetachedBuffer.
124 DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_,
125 size());
126 if (own_allocator_) {
127 allocator_ = nullptr;
128 own_allocator_ = false;
129 }
130 buf_ = nullptr;
131 clear();
132 return fb;
133 }
134
135 size_t ensure_space(size_t len) {
136 FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_);
137 if (len > static_cast<size_t>(cur_ - scratch_)) { reallocate(len); }
138 // Beyond this, signed offsets may not have enough range:
139 // (FlatBuffers > 2GB not supported).
140 FLATBUFFERS_ASSERT(size() < FLATBUFFERS_MAX_BUFFER_SIZE);
141 return len;
142 }
143
144 inline uint8_t *make_space(size_t len) {
145 if (len) {
146 ensure_space(len);
147 cur_ -= len;
148 size_ += static_cast<uoffset_t>(len);
149 }
150 return cur_;
151 }
152
153 // Returns nullptr if using the DefaultAllocator.
154 Allocator *get_custom_allocator() { return allocator_; }
155
156 inline uoffset_t size() const { return size_; }
157
158 uoffset_t scratch_size() const {
159 return static_cast<uoffset_t>(scratch_ - buf_);
160 }
161
162 size_t capacity() const { return reserved_; }
163
164 uint8_t *data() const {
165 FLATBUFFERS_ASSERT(cur_);
166 return cur_;
167 }
168
169 uint8_t *scratch_data() const {
170 FLATBUFFERS_ASSERT(buf_);
171 return buf_;
172 }
173
174 uint8_t *scratch_end() const {
175 FLATBUFFERS_ASSERT(scratch_);
176 return scratch_;
177 }
178
179 uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; }
180
181 void push(const uint8_t *bytes, size_t num) {
182 if (num > 0) { memcpy(make_space(num), bytes, num); }
183 }
184
185 // Specialized version of push() that avoids memcpy call for small data.
186 template<typename T> void push_small(const T &little_endian_t) {
187 make_space(sizeof(T));
188 *reinterpret_cast<T *>(cur_) = little_endian_t;
189 }
190
191 template<typename T> void scratch_push_small(const T &t) {
192 ensure_space(sizeof(T));
193 *reinterpret_cast<T *>(scratch_) = t;
194 scratch_ += sizeof(T);
195 }
196
197 // fill() is most frequently called with small byte counts (<= 4),
198 // which is why we're using loops rather than calling memset.
199 void fill(size_t zero_pad_bytes) {
200 make_space(zero_pad_bytes);
201 for (size_t i = 0; i < zero_pad_bytes; i++) cur_[i] = 0;
202 }
203
204 // Version for when we know the size is larger.
205 // Precondition: zero_pad_bytes > 0
206 void fill_big(size_t zero_pad_bytes) {
207 memset(make_space(zero_pad_bytes), 0, zero_pad_bytes);
208 }
209
210 void pop(size_t bytes_to_remove) {
211 cur_ += bytes_to_remove;
212 size_ -= static_cast<uoffset_t>(bytes_to_remove);
213 }
214
215 void scratch_pop(size_t bytes_to_remove) { scratch_ -= bytes_to_remove; }
216
217 void swap(vector_downward &other) {
218 using std::swap;
219 swap(allocator_, other.allocator_);
220 swap(own_allocator_, other.own_allocator_);
221 swap(initial_size_, other.initial_size_);
222 swap(buffer_minalign_, other.buffer_minalign_);
223 swap(reserved_, other.reserved_);
224 swap(size_, other.size_);
225 swap(buf_, other.buf_);
226 swap(cur_, other.cur_);
227 swap(scratch_, other.scratch_);
228 }
229
230 void swap_allocator(vector_downward &other) {
231 using std::swap;
232 swap(allocator_, other.allocator_);
233 swap(own_allocator_, other.own_allocator_);
234 }
235
236 private:
237 // You shouldn't really be copying instances of this class.
238 FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &));
239 FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &));
240
241 Allocator *allocator_;
242 bool own_allocator_;
243 size_t initial_size_;
244 size_t buffer_minalign_;
245 size_t reserved_;
246 uoffset_t size_;
247 uint8_t *buf_;
248 uint8_t *cur_; // Points at location between empty (below) and used (above).
249 uint8_t *scratch_; // Points to the end of the scratchpad in use.
250
251 void reallocate(size_t len) {
252 auto old_reserved = reserved_;
253 auto old_size = size();
254 auto old_scratch_size = scratch_size();
255 reserved_ +=
256 (std::max)(len, old_reserved ? old_reserved / 2 : initial_size_);
257 reserved_ = (reserved_ + buffer_minalign_ - 1) & ~(buffer_minalign_ - 1);
258 if (buf_) {
259 buf_ = ReallocateDownward(allocator_, buf_, old_reserved, reserved_,
260 old_size, old_scratch_size);
261 } else {
262 buf_ = Allocate(allocator_, reserved_);
263 }
264 cur_ = buf_ + reserved_ - old_size;
265 scratch_ = buf_ + old_scratch_size;
266 }
267};
268
269} // namespace flatbuffers
270
271#endif // FLATBUFFERS_VECTOR_DOWNWARD_H_