Coverage Report

Created: 2026-09-26 08:22

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
67.7k
      : allocator_(allocator),
37
67.7k
        own_allocator_(own_allocator),
38
67.7k
        initial_size_(initial_size),
39
67.7k
        buffer_minalign_(buffer_minalign),
40
67.7k
        reserved_(0),
41
67.7k
        size_(0),
42
67.7k
        buf_(nullptr),
43
67.7k
        cur_(nullptr),
44
67.7k
        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
67.7k
  ~vector_downward() {
75
67.7k
    clear_buffer();
76
67.7k
    clear_allocator();
77
67.7k
  }
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
427
  void clear_scratch() { scratch_ = buf_; }
96
97
67.7k
  void clear_allocator() {
98
67.7k
    if (own_allocator_ && allocator_) { delete allocator_; }
99
67.7k
    allocator_ = nullptr;
100
67.7k
    own_allocator_ = false;
101
67.7k
  }
102
103
67.7k
  void clear_buffer() {
104
67.7k
    if (buf_) Deallocate(allocator_, buf_, reserved_);
105
67.7k
    buf_ = nullptr;
106
67.7k
  }
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
135k
  size_t ensure_space(size_t len) {
136
135k
    FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_);
137
135k
    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
135k
    FLATBUFFERS_ASSERT(size() < FLATBUFFERS_MAX_BUFFER_SIZE);
141
135k
    return len;
142
135k
  }
143
144
177k
  inline uint8_t *make_space(size_t len) {
145
177k
    if (len) {
146
105k
      ensure_space(len);
147
105k
      cur_ -= len;
148
105k
      size_ += static_cast<uoffset_t>(len);
149
105k
    }
150
177k
    return cur_;
151
177k
  }
152
153
  // Returns nullptr if using the DefaultAllocator.
154
0
  Allocator *get_custom_allocator() { return allocator_; }
155
156
353k
  inline uoffset_t size() const { return size_; }
157
158
608
  uoffset_t scratch_size() const {
159
608
    return static_cast<uoffset_t>(scratch_ - buf_);
160
608
  }
161
162
0
  size_t capacity() const { return reserved_; }
163
164
87.7k
  uint8_t *data() const {
165
87.7k
    FLATBUFFERS_ASSERT(cur_);
166
87.7k
    return cur_;
167
87.7k
  }
168
169
9.78k
  uint8_t *scratch_data() const {
170
9.78k
    FLATBUFFERS_ASSERT(buf_);
171
9.78k
    return buf_;
172
9.78k
  }
173
174
58.8k
  uint8_t *scratch_end() const {
175
58.8k
    FLATBUFFERS_ASSERT(scratch_);
176
58.8k
    return scratch_;
177
58.8k
  }
178
179
28.2k
  uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; }
180
181
9.88k
  void push(const uint8_t *bytes, size_t num) {
182
9.88k
    if (num > 0) { memcpy(make_space(num), bytes, num); }
183
9.88k
  }
184
185
  // Specialized version of push() that avoids memcpy call for small data.
186
58.7k
  template<typename T> void push_small(const T &little_endian_t) {
187
58.7k
    make_space(sizeof(T));
188
58.7k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
58.7k
  }
void gdal_flatbuffers::vector_downward::push_small<int>(int const&)
Line
Count
Source
186
18.8k
  template<typename T> void push_small(const T &little_endian_t) {
187
18.8k
    make_space(sizeof(T));
188
18.8k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
18.8k
  }
void gdal_flatbuffers::vector_downward::push_small<unsigned int>(unsigned int const&)
Line
Count
Source
186
30.3k
  template<typename T> void push_small(const T &little_endian_t) {
187
30.3k
    make_space(sizeof(T));
188
30.3k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
30.3k
  }
void gdal_flatbuffers::vector_downward::push_small<unsigned char>(unsigned char const&)
Line
Count
Source
186
9.35k
  template<typename T> void push_small(const T &little_endian_t) {
187
9.35k
    make_space(sizeof(T));
188
9.35k
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
9.35k
  }
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
125
  template<typename T> void push_small(const T &little_endian_t) {
187
125
    make_space(sizeof(T));
188
125
    *reinterpret_cast<T *>(cur_) = little_endian_t;
189
125
  }
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
29.8k
  template<typename T> void scratch_push_small(const T &t) {
192
29.8k
    ensure_space(sizeof(T));
193
29.8k
    *reinterpret_cast<T *>(scratch_) = t;
194
29.8k
    scratch_ += sizeof(T);
195
29.8k
  }
void gdal_flatbuffers::vector_downward::scratch_push_small<gdal_flatbuffers::FlatBufferBuilder::FieldLoc>(gdal_flatbuffers::FlatBufferBuilder::FieldLoc const&)
Line
Count
Source
191
28.9k
  template<typename T> void scratch_push_small(const T &t) {
192
28.9k
    ensure_space(sizeof(T));
193
28.9k
    *reinterpret_cast<T *>(scratch_) = t;
194
28.9k
    scratch_ += sizeof(T);
195
28.9k
  }
void gdal_flatbuffers::vector_downward::scratch_push_small<unsigned int>(unsigned int const&)
Line
Count
Source
191
875
  template<typename T> void scratch_push_small(const T &t) {
192
875
    ensure_space(sizeof(T));
193
875
    *reinterpret_cast<T *>(scratch_) = t;
194
875
    scratch_ += sizeof(T);
195
875
  }
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
99.0k
  void fill(size_t zero_pad_bytes) {
200
99.0k
    make_space(zero_pad_bytes);
201
156k
    for (size_t i = 0; i < zero_pad_bytes; i++) cur_[i] = 0;
202
99.0k
  }
203
204
  // Version for when we know the size is larger.
205
  // Precondition: zero_pad_bytes > 0
206
9.78k
  void fill_big(size_t zero_pad_bytes) {
207
9.78k
    memset(make_space(zero_pad_bytes), 0, zero_pad_bytes);
208
9.78k
  }
209
210
8.90k
  void pop(size_t bytes_to_remove) {
211
8.90k
    cur_ += bytes_to_remove;
212
8.90k
    size_ -= static_cast<uoffset_t>(bytes_to_remove);
213
8.90k
  }
214
215
9.78k
  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
608
  void reallocate(size_t len) {
252
608
    auto old_reserved = reserved_;
253
608
    auto old_size = size();
254
608
    auto old_scratch_size = scratch_size();
255
608
    reserved_ +=
256
608
        (std::max)(len, old_reserved ? old_reserved / 2 : initial_size_);
257
608
    reserved_ = (reserved_ + buffer_minalign_ - 1) & ~(buffer_minalign_ - 1);
258
608
    if (buf_) {
259
181
      buf_ = ReallocateDownward(allocator_, buf_, old_reserved, reserved_,
260
181
                                old_size, old_scratch_size);
261
427
    } else {
262
427
      buf_ = Allocate(allocator_, reserved_);
263
427
    }
264
608
    cur_ = buf_ + reserved_ - old_size;
265
608
    scratch_ = buf_ + old_scratch_size;
266
608
  }
267
};
268
269
}  // namespace flatbuffers
270
271
#endif  // FLATBUFFERS_VECTOR_DOWNWARD_H_