Line data Source code
1 : /*
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 :
24 : namespace 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.
32 : class vector_downward {
33 : public:
34 559 : explicit vector_downward(size_t initial_size, Allocator *allocator,
35 : bool own_allocator, size_t buffer_minalign)
36 559 : : 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 559 : 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 1118 : ~vector_downward() {
75 559 : clear_buffer();
76 559 : clear_allocator();
77 559 : }
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 509 : void clear_scratch() { scratch_ = buf_; }
96 :
97 559 : void clear_allocator() {
98 559 : if (own_allocator_ && allocator_) { delete allocator_; }
99 559 : allocator_ = nullptr;
100 559 : own_allocator_ = false;
101 559 : }
102 :
103 559 : void clear_buffer() {
104 559 : if (buf_) Deallocate(allocator_, buf_, reserved_);
105 559 : buf_ = nullptr;
106 559 : }
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 1835660 : size_t ensure_space(size_t len) {
136 1835660 : FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_);
137 1835660 : 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 1835660 : FLATBUFFERS_ASSERT(size() < FLATBUFFERS_MAX_BUFFER_SIZE);
141 1835660 : return len;
142 : }
143 :
144 2385300 : inline uint8_t *make_space(size_t len) {
145 2385300 : if (len) {
146 1437660 : ensure_space(len);
147 1437660 : cur_ -= len;
148 1437660 : size_ += static_cast<uoffset_t>(len);
149 : }
150 2385300 : return cur_;
151 : }
152 :
153 : // Returns nullptr if using the DefaultAllocator.
154 : Allocator *get_custom_allocator() { return allocator_; }
155 :
156 4751260 : inline uoffset_t size() const { return size_; }
157 :
158 557 : uoffset_t scratch_size() const {
159 557 : return static_cast<uoffset_t>(scratch_ - buf_);
160 : }
161 :
162 : size_t capacity() const { return reserved_; }
163 :
164 1191320 : uint8_t *data() const {
165 1191320 : FLATBUFFERS_ASSERT(cur_);
166 1191320 : return cur_;
167 : }
168 :
169 132230 : uint8_t *scratch_data() const {
170 132230 : FLATBUFFERS_ASSERT(buf_);
171 132230 : return buf_;
172 : }
173 :
174 794356 : uint8_t *scratch_end() const {
175 794356 : FLATBUFFERS_ASSERT(scratch_);
176 794356 : return scratch_;
177 : }
178 :
179 395529 : uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; }
180 :
181 132549 : void push(const uint8_t *bytes, size_t num) {
182 132549 : if (num > 0) { memcpy(make_space(num), bytes, num); }
183 132549 : }
184 :
185 : // Specialized version of push() that avoids memcpy call for small data.
186 794736 : template<typename T> void push_small(const T &little_endian_t) {
187 794736 : make_space(sizeof(T));
188 794736 : *reinterpret_cast<T *>(cur_) = little_endian_t;
189 794736 : }
190 :
191 397999 : template<typename T> void scratch_push_small(const T &t) {
192 397999 : ensure_space(sizeof(T));
193 397999 : *reinterpret_cast<T *>(scratch_) = t;
194 397999 : scratch_ += sizeof(T);
195 397999 : }
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 1325790 : void fill(size_t zero_pad_bytes) {
200 1325790 : make_space(zero_pad_bytes);
201 2193270 : for (size_t i = 0; i < zero_pad_bytes; i++) cur_[i] = 0;
202 1325790 : }
203 :
204 : // Version for when we know the size is larger.
205 : // Precondition: zero_pad_bytes > 0
206 132230 : void fill_big(size_t zero_pad_bytes) {
207 132230 : memset(make_space(zero_pad_bytes), 0, zero_pad_bytes);
208 132230 : }
209 :
210 131292 : void pop(size_t bytes_to_remove) {
211 131292 : cur_ += bytes_to_remove;
212 131292 : size_ -= static_cast<uoffset_t>(bytes_to_remove);
213 131292 : }
214 :
215 132230 : 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 557 : void reallocate(size_t len) {
252 557 : auto old_reserved = reserved_;
253 557 : auto old_size = size();
254 557 : auto old_scratch_size = scratch_size();
255 557 : reserved_ +=
256 557 : (std::max)(len, old_reserved ? old_reserved / 2 : initial_size_);
257 557 : reserved_ = (reserved_ + buffer_minalign_ - 1) & ~(buffer_minalign_ - 1);
258 557 : if (buf_) {
259 48 : buf_ = ReallocateDownward(allocator_, buf_, old_reserved, reserved_,
260 : old_size, old_scratch_size);
261 : } else {
262 509 : buf_ = Allocate(allocator_, reserved_);
263 : }
264 557 : cur_ = buf_ + reserved_ - old_size;
265 557 : scratch_ = buf_ + old_scratch_size;
266 557 : }
267 : };
268 :
269 : } // namespace flatbuffers
270 :
271 : #endif // FLATBUFFERS_VECTOR_DOWNWARD_H_
|