Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/third_party/flatbuffers/vector_downward.h
Line
Count
Source
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
  explicit vector_downward(size_t initial_size, Allocator *allocator,
35
                           bool own_allocator, size_t buffer_minalign)
36
68.2k
      : allocator_(allocator),
37
68.2k
        own_allocator_(own_allocator),
38
68.2k
        initial_size_(initial_size),
39
68.2k
        buffer_minalign_(buffer_minalign),
40
68.2k
        reserved_(0),
41
68.2k
        size_(0),
42
68.2k
        buf_(nullptr),
43
68.2k
        cur_(nullptr),
44
68.2k
        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
0
        scratch_(other.scratch_) {
57
0
    // No change in other.allocator_
58
0
    // No change in other.initial_size_
59
0
    // No change in other.buffer_minalign_
60
0
    other.own_allocator_ = false;
61
0
    other.reserved_ = 0;
62
0
    other.buf_ = nullptr;
63
0
    other.cur_ = nullptr;
64
0
    other.scratch_ = nullptr;
65
0
  }
66
67
0
  vector_downward &operator=(vector_downward &&other) {
68
0
    // Move construct a temporary and swap idiom
69
0
    vector_downward temp(std::move(other));
70
0
    swap(temp);
71
0
    return *this;
72
0
  }
73
74
68.2k
  ~vector_downward() {
75
68.2k
    clear_buffer();
76
68.2k
    clear_allocator();
77
68.2k
  }
78
79
0
  void reset() {
80
0
    clear_buffer();
81
0
    clear();
82
0
  }
83
84
0
  void clear() {
85
0
    if (buf_) {
86
0
      cur_ = buf_ + reserved_;
87
0
    } else {
88
0
      reserved_ = 0;
89
0
      cur_ = nullptr;
90
0
    }
91
0
    size_ = 0;
92
0
    clear_scratch();
93
0
  }
94
95
421
  void clear_scratch() { scratch_ = buf_; }
96
97
68.2k
  void clear_allocator() {
98
68.2k
    if (own_allocator_ && allocator_) { delete allocator_; }
99
68.2k
    allocator_ = nullptr;
100
68.2k
    own_allocator_ = false;
101
68.2k
  }
102
103
68.2k
  void clear_buffer() {
104
68.2k
    if (buf_) Deallocate(allocator_, buf_, reserved_);
105
68.2k
    buf_ = nullptr;
106
68.2k
  }
107
108
  // Relinquish the pointer to the caller.
109
0
  uint8_t *release_raw(size_t &allocated_bytes, size_t &offset) {
110
0
    auto *buf = buf_;
111
0
    allocated_bytes = reserved_;
112
0
    offset = static_cast<size_t>(cur_ - buf_);
113
0
114
0
    // release_raw only relinquishes the buffer ownership.
115
0
    // Does not deallocate or reset the allocator. Destructor will do that.
116
0
    buf_ = nullptr;
117
0
    clear();
118
0
    return buf;
119
0
  }
120
121
  // Relinquish the pointer to the caller.
122
0
  DetachedBuffer release() {
123
0
    // allocator ownership (if any) is transferred to DetachedBuffer.
124
0
    DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_,
125
0
                      size());
126
0
    if (own_allocator_) {
127
0
      allocator_ = nullptr;
128
0
      own_allocator_ = false;
129
0
    }
130
0
    buf_ = nullptr;
131
0
    clear();
132
0
    return fb;
133
0
  }
134
135
88.6k
  size_t ensure_space(size_t len) {
136
88.6k
    FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_);
137
88.6k
    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
88.6k
    FLATBUFFERS_ASSERT(size() < FLATBUFFERS_MAX_BUFFER_SIZE);
141
88.6k
    return len;
142
88.6k
  }
143
144
116k
  inline uint8_t *make_space(size_t len) {
145
116k
    if (len) {
146
68.9k
      ensure_space(len);
147
68.9k
      cur_ -= len;
148
68.9k
      size_ += static_cast<uoffset_t>(len);
149
68.9k
    }
150
116k
    return cur_;
151
116k
  }
152
153
  // Returns nullptr if using the DefaultAllocator.
154
0
  Allocator *get_custom_allocator() { return allocator_; }
155
156
232k
  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
557
  }
161
162
0
  size_t capacity() const { return reserved_; }
163
164
57.3k
  uint8_t *data() const {
165
57.3k
    FLATBUFFERS_ASSERT(cur_);
166
57.3k
    return cur_;
167
57.3k
  }
168
169
6.40k
  uint8_t *scratch_data() const {
170
6.40k
    FLATBUFFERS_ASSERT(buf_);
171
6.40k
    return buf_;
172
6.40k
  }
173
174
38.5k
  uint8_t *scratch_end() const {
175
38.5k
    FLATBUFFERS_ASSERT(scratch_);
176
38.5k
    return scratch_;
177
38.5k
  }
178
179
18.1k
  uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; }
180
181
6.50k
  void push(const uint8_t *bytes, size_t num) {
182
6.50k
    if (num > 0) { memcpy(make_space(num), bytes, num); }
183
6.50k
  }
184
185
  // Specialized version of push() that avoids memcpy call for small data.
186
38.4k
  template<typename T> void push_small(const T &little_endian_t) {
187
38.4k
    make_space(sizeof(T));
188
38.4k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
38.4k
  }
void gdal_flatbuffers::vector_downward::push_small<int>(int const&)
Line
Count
Source
186
12.1k
  template<typename T> void push_small(const T &little_endian_t) {
187
12.1k
    make_space(sizeof(T));
188
12.1k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
12.1k
  }
void gdal_flatbuffers::vector_downward::push_small<unsigned int>(unsigned int const&)
Line
Count
Source
186
20.1k
  template<typename T> void push_small(const T &little_endian_t) {
187
20.1k
    make_space(sizeof(T));
188
20.1k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
20.1k
  }
void gdal_flatbuffers::vector_downward::push_small<unsigned char>(unsigned char const&)
Line
Count
Source
186
5.97k
  template<typename T> void push_small(const T &little_endian_t) {
187
5.97k
    make_space(sizeof(T));
188
5.97k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
5.97k
  }
void gdal_flatbuffers::vector_downward::push_small<unsigned long>(unsigned long const&)
Line
Count
Source
186
84
  template<typename T> void push_small(const T &little_endian_t) {
187
84
    make_space(sizeof(T));
188
84
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
84
  }
void gdal_flatbuffers::vector_downward::push_small<unsigned short>(unsigned short const&)
Line
Count
Source
186
119
  template<typename T> void push_small(const T &little_endian_t) {
187
119
    make_space(sizeof(T));
188
119
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
119
  }
Unexecuted instantiation: void gdal_flatbuffers::vector_downward::push_small<gdal_generated_icechunk::ObjectId12>(gdal_generated_icechunk::ObjectId12 const&)
Unexecuted instantiation: void gdal_flatbuffers::vector_downward::push_small<gdal_generated_icechunk::ObjectId8>(gdal_generated_icechunk::ObjectId8 const&)
190
191
19.6k
  template<typename T> void scratch_push_small(const T &t) {
192
19.6k
    ensure_space(sizeof(T));
193
19.6k
    *reinterpret_cast<T *>(scratch_) = t;
194
19.6k
    scratch_ += sizeof(T);
195
19.6k
  }
void gdal_flatbuffers::vector_downward::scratch_push_small<gdal_flatbuffers::FlatBufferBuilder::FieldLoc>(gdal_flatbuffers::FlatBufferBuilder::FieldLoc const&)
Line
Count
Source
191
18.8k
  template<typename T> void scratch_push_small(const T &t) {
192
18.8k
    ensure_space(sizeof(T));
193
18.8k
    *reinterpret_cast<T *>(scratch_) = t;
194
18.8k
    scratch_ += sizeof(T);
195
18.8k
  }
void gdal_flatbuffers::vector_downward::scratch_push_small<unsigned int>(unsigned int const&)
Line
Count
Source
191
863
  template<typename T> void scratch_push_small(const T &t) {
192
863
    ensure_space(sizeof(T));
193
863
    *reinterpret_cast<T *>(scratch_) = t;
194
863
    scratch_ += sizeof(T);
195
863
  }
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
65.2k
  void fill(size_t zero_pad_bytes) {
200
65.2k
    make_space(zero_pad_bytes);
201
102k
    for (size_t i = 0; i < zero_pad_bytes; i++) cur_[i] = 0;
202
65.2k
  }
203
204
  // Version for when we know the size is larger.
205
  // Precondition: zero_pad_bytes > 0
206
6.40k
  void fill_big(size_t zero_pad_bytes) {
207
6.40k
    memset(make_space(zero_pad_bytes), 0, zero_pad_bytes);
208
6.40k
  }
209
210
5.54k
  void pop(size_t bytes_to_remove) {
211
5.54k
    cur_ += bytes_to_remove;
212
5.54k
    size_ -= static_cast<uoffset_t>(bytes_to_remove);
213
5.54k
  }
214
215
6.40k
  void scratch_pop(size_t bytes_to_remove) { scratch_ -= bytes_to_remove; }
216
217
0
  void swap(vector_downward &other) {
218
0
    using std::swap;
219
0
    swap(allocator_, other.allocator_);
220
0
    swap(own_allocator_, other.own_allocator_);
221
0
    swap(initial_size_, other.initial_size_);
222
0
    swap(buffer_minalign_, other.buffer_minalign_);
223
0
    swap(reserved_, other.reserved_);
224
0
    swap(size_, other.size_);
225
0
    swap(buf_, other.buf_);
226
0
    swap(cur_, other.cur_);
227
0
    swap(scratch_, other.scratch_);
228
0
  }
229
230
0
  void swap_allocator(vector_downward &other) {
231
0
    using std::swap;
232
0
    swap(allocator_, other.allocator_);
233
0
    swap(own_allocator_, other.own_allocator_);
234
0
  }
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
136
      buf_ = ReallocateDownward(allocator_, buf_, old_reserved, reserved_,
260
136
                                old_size, old_scratch_size);
261
421
    } else {
262
421
      buf_ = Allocate(allocator_, reserved_);
263
421
    }
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_