Coverage Report

Created: 2026-10-09 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/common/allocator.h
Line
Count
Source
1
// Copyright 2024 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#ifndef THIRD_PARTY_CEL_CPP_COMMON_ALLOCATOR_H_
16
#define THIRD_PARTY_CEL_CPP_COMMON_ALLOCATOR_H_
17
18
#include <cstddef>
19
#include <memory>
20
#include <new>
21
#include <type_traits>
22
23
#include "absl/base/attributes.h"
24
#include "absl/base/nullability.h"
25
#include "absl/base/optimization.h"
26
#include "absl/log/absl_check.h"
27
#include "absl/log/die_if_null.h"
28
#include "absl/numeric/bits.h"
29
#include "common/arena.h"
30
#include "internal/new.h"
31
#include "google/protobuf/arena.h"
32
33
namespace cel {
34
35
enum class AllocatorKind {
36
  kArena = 1,
37
  kNewDelete = 2,
38
};
39
40
template <typename S>
41
void AbslStringify(S& sink, AllocatorKind kind) {
42
  switch (kind) {
43
    case AllocatorKind::kArena:
44
      sink.Append("ARENA");
45
      return;
46
    case AllocatorKind::kNewDelete:
47
      sink.Append("NEW_DELETE");
48
      return;
49
    default:
50
      sink.Append("ERROR");
51
      return;
52
  }
53
}
54
55
template <typename T = void>
56
class NewDeleteAllocator;
57
template <typename T = void>
58
class ArenaAllocator;
59
template <typename T = void>
60
class Allocator;
61
62
// `NewDeleteAllocator<>` is a type-erased vocabulary type capable of performing
63
// allocation/deallocation and construction/destruction using memory owned by
64
// `operator new`.
65
template <>
66
class NewDeleteAllocator<void> {
67
 public:
68
  using size_type = size_t;
69
  using difference_type = ptrdiff_t;
70
  using propagate_on_container_copy_assignment = std::true_type;
71
  using propagate_on_container_move_assignment = std::true_type;
72
  using propagate_on_container_swap = std::true_type;
73
  using is_always_equal = std::true_type;
74
75
  NewDeleteAllocator() = default;
76
  NewDeleteAllocator(const NewDeleteAllocator&) = default;
77
  NewDeleteAllocator& operator=(const NewDeleteAllocator&) = default;
78
79
  template <typename U, typename = std::enable_if_t<!std::is_void_v<U>>>
80
  // NOLINTNEXTLINE(google-explicit-constructor)
81
  constexpr NewDeleteAllocator(
82
      [[maybe_unused]] const NewDeleteAllocator<U>& other) noexcept {}
83
84
  // Allocates at least `nbytes` bytes with a minimum alignment of `alignment`
85
  // from the underlying memory resource. When the underlying memory resource is
86
  // `operator new`, `deallocate_bytes` must be called at some point, otherwise
87
  // calling `deallocate_bytes` is optional. The caller must not pass an object
88
  // constructed in the return memory to `delete_object`, doing so is undefined
89
  // behavior.
90
  ABSL_MUST_USE_RESULT void* allocate_bytes(
91
0
      size_type nbytes, size_type alignment = alignof(std::max_align_t)) {
92
0
    ABSL_DCHECK(absl::has_single_bit(alignment));
93
0
    if (nbytes == 0) {
94
0
      return nullptr;
95
0
    }
96
0
    return internal::AlignedNew(nbytes,
97
0
                                static_cast<std::align_val_t>(alignment));
98
0
  }
99
100
  // Deallocates memory previously returned by `allocate_bytes`.
101
  void deallocate_bytes(
102
      void* p, size_type nbytes,
103
0
      size_type alignment = alignof(std::max_align_t)) noexcept {
104
0
    ABSL_DCHECK((p == nullptr && nbytes == 0) || (p != nullptr && nbytes != 0));
105
0
    ABSL_DCHECK(absl::has_single_bit(alignment));
106
0
    internal::SizedAlignedDelete(p, nbytes,
107
0
                                 static_cast<std::align_val_t>(alignment));
108
0
  }
109
110
  template <typename T>
111
  ABSL_MUST_USE_RESULT T* allocate_object(size_type n = 1) {
112
    return static_cast<T*>(allocate_bytes(sizeof(T) * n, alignof(T)));
113
  }
114
115
  template <typename T>
116
  void deallocate_object(T* p, size_type n = 1) {
117
    deallocate_bytes(p, sizeof(T) * n, alignof(T));
118
  }
119
120
  // Allocates memory suitable for an object of type `T` and constructs the
121
  // object by forwarding the provided arguments. If the underlying memory
122
  // resource is `operator new` is false, `delete_object` must eventually be
123
  // called.
124
  template <typename T, typename... Args>
125
  ABSL_MUST_USE_RESULT T* new_object(Args&&... args) {
126
    return new T(std::forward<Args>(args)...);
127
  }
128
129
  // Destructs the object of type `T` located at address `p` and deallocates the
130
  // memory, `p` must have been previously returned by `new_object`.
131
  template <typename T>
132
  void delete_object(T* p) noexcept {
133
    ABSL_DCHECK(p != nullptr);
134
    delete p;
135
  }
136
137
  void delete_object(std::nullptr_t) = delete;
138
139
 private:
140
  template <typename U>
141
  friend class NewDeleteAllocator;
142
};
143
144
// `NewDeleteAllocator<T>` is an extension of `NewDeleteAllocator<>` which
145
// adheres to the named C++ requirements for `Allocator`, allowing it to be used
146
// in places which accept custom STL allocators.
147
template <typename T>
148
class NewDeleteAllocator : public NewDeleteAllocator<void> {
149
 public:
150
  static_assert(!std::is_const_v<T>, "T must not be const qualified");
151
  static_assert(!std::is_volatile_v<T>, "T must not be volatile qualified");
152
  static_assert(std::is_object_v<T>, "T must be an object type");
153
154
  using value_type = T;
155
  using pointer = value_type*;
156
  using const_pointer = const value_type*;
157
  using reference = value_type&;
158
  using const_reference = const value_type&;
159
160
  using NewDeleteAllocator<void>::NewDeleteAllocator;
161
162
  template <typename U, typename = std::enable_if_t<!std::is_same_v<U, T>>>
163
  // NOLINTNEXTLINE(google-explicit-constructor)
164
  constexpr NewDeleteAllocator(
165
      [[maybe_unused]] const NewDeleteAllocator<U>& other) noexcept {}
166
167
  pointer allocate(size_type n, const void* /*hint*/ = nullptr) {
168
    return reinterpret_cast<pointer>(internal::AlignedNew(
169
        n * sizeof(T), static_cast<std::align_val_t>(alignof(T))));
170
  }
171
172
#if defined(__cpp_lib_allocate_at_least) && \
173
    __cpp_lib_allocate_at_least >= 202302L
174
  std::allocation_result<pointer, size_type> allocate_at_least(size_type n) {
175
    void* addr;
176
    size_type size;
177
    std::tie(addr, size) = internal::SizeReturningAlignedNew(
178
        n * sizeof(T), static_cast<std::align_val_t>(alignof(T)));
179
    std::allocation_result<pointer, size_type> result;
180
    result.ptr = reinterpret_cast<pointer>(addr);
181
    result.count = size / sizeof(T);
182
    return result;
183
  }
184
#endif
185
186
  void deallocate(pointer p, size_type n) noexcept {
187
    internal::SizedAlignedDelete(p, n * sizeof(T),
188
                                 static_cast<std::align_val_t>(alignof(T)));
189
  }
190
191
  template <typename U, typename... Args>
192
  void construct(U* p, Args&&... args) {
193
    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
194
  }
195
196
  template <typename U>
197
  void destroy(U* p) noexcept {
198
    std::destroy_at(p);
199
  }
200
};
201
202
template <typename T, typename U>
203
inline bool operator==(NewDeleteAllocator<T>, NewDeleteAllocator<U>) noexcept {
204
  return true;
205
}
206
207
template <typename T, typename U>
208
inline bool operator!=(NewDeleteAllocator<T> lhs,
209
                       NewDeleteAllocator<U> rhs) noexcept {
210
  return !operator==(lhs, rhs);
211
}
212
213
NewDeleteAllocator() -> NewDeleteAllocator<void>;
214
template <typename T>
215
NewDeleteAllocator(const NewDeleteAllocator<T>&) -> NewDeleteAllocator<T>;
216
217
// `ArenaAllocator<>` is a type-erased vocabulary type capable of performing
218
// allocation/deallocation and construction/destruction using memory owned by
219
// `google::protobuf::Arena`.
220
template <>
221
class ArenaAllocator<void> {
222
 public:
223
  using size_type = size_t;
224
  using difference_type = ptrdiff_t;
225
  using propagate_on_container_copy_assignment = std::true_type;
226
  using propagate_on_container_move_assignment = std::true_type;
227
  using propagate_on_container_swap = std::true_type;
228
229
  ArenaAllocator() = delete;
230
231
  ArenaAllocator(const ArenaAllocator&) = default;
232
  ArenaAllocator& operator=(const ArenaAllocator&) = delete;
233
234
  ArenaAllocator(std::nullptr_t) = delete;
235
236
  template <typename U, typename = std::enable_if_t<!std::is_void_v<U>>>
237
  // NOLINTNEXTLINE(google-explicit-constructor)
238
  constexpr ArenaAllocator(const ArenaAllocator<U>& other) noexcept
239
1.15M
      : arena_(other.arena()) {}
cel::ArenaAllocator<void>::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value>, void>(cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> > const&)
Line
Count
Source
239
597k
      : arena_(other.arena()) {}
cel::ArenaAllocator<void>::ArenaAllocator<char, void>(cel::ArenaAllocator<char> const&)
Line
Count
Source
239
559k
      : arena_(other.arena()) {}
240
241
  // NOLINTNEXTLINE(google-explicit-constructor)
242
  ArenaAllocator(google::protobuf::Arena* absl_nonnull arena) noexcept
243
2.69M
      : arena_(ABSL_DIE_IF_NULL(arena))  // Crash OK
244
2.69M
  {}
245
246
2.71M
  constexpr google::protobuf::Arena* absl_nonnull arena() const noexcept {
247
2.71M
    ABSL_ASSUME(arena_ != nullptr);
248
2.71M
    return arena_;
249
2.71M
  }
250
251
  // Allocates at least `nbytes` bytes with a minimum alignment of `alignment`
252
  // from the underlying memory resource. When the underlying memory resource is
253
  // `operator new`, `deallocate_bytes` must be called at some point, otherwise
254
  // calling `deallocate_bytes` is optional. The caller must not pass an object
255
  // constructed in the return memory to `delete_object`, doing so is undefined
256
  // behavior.
257
  ABSL_MUST_USE_RESULT void* allocate_bytes(
258
0
      size_type nbytes, size_type alignment = alignof(std::max_align_t)) {
259
0
    ABSL_DCHECK(absl::has_single_bit(alignment));
260
0
    if (nbytes == 0) {
261
0
      return nullptr;
262
0
    }
263
0
    return arena()->AllocateAligned(nbytes, alignment);
264
0
  }
265
266
  // Deallocates memory previously returned by `allocate_bytes`.
267
  void deallocate_bytes(
268
      void* p, size_type nbytes,
269
0
      size_type alignment = alignof(std::max_align_t)) noexcept {
270
0
    ABSL_DCHECK((p == nullptr && nbytes == 0) || (p != nullptr && nbytes != 0));
271
0
    ABSL_DCHECK(absl::has_single_bit(alignment));
272
0
  }
273
274
  template <typename T>
275
  ABSL_MUST_USE_RESULT T* allocate_object(size_type n = 1) {
276
    return static_cast<T*>(allocate_bytes(sizeof(T) * n, alignof(T)));
277
  }
278
279
  template <typename T>
280
  void deallocate_object(T* p, size_type n = 1) {
281
    deallocate_bytes(p, sizeof(T) * n, alignof(T));
282
  }
283
284
  // Allocates memory suitable for an object of type `T` and constructs the
285
  // object by forwarding the provided arguments. If the underlying memory
286
  // resource is `operator new` is false, `delete_object` must eventually be
287
  // called.
288
  template <typename T, typename... Args>
289
  ABSL_MUST_USE_RESULT T* new_object(Args&&... args) {
290
    using U = std::remove_const_t<T>;
291
    U* object;
292
    if constexpr (google::protobuf::Arena::is_arena_constructable<U>::value) {
293
      object = google::protobuf::Arena::Create<U>(arena(), std::forward<Args>(args)...);
294
    } else {
295
      if constexpr (ArenaTraits<>::constructible<U>()) {
296
        object = ::new (static_cast<void*>(arena()->AllocateAligned(
297
            sizeof(U), alignof(U)))) U(arena(), std::forward<Args>(args)...);
298
      } else {
299
        object = ::new (static_cast<void*>(arena()->AllocateAligned(
300
            sizeof(U), alignof(U)))) U(std::forward<Args>(args)...);
301
      }
302
      if constexpr (!ArenaTraits<>::always_trivially_destructible<U>()) {
303
        if (!ArenaTraits<>::trivially_destructible(*object)) {
304
          arena()->OwnDestructor(object);
305
        }
306
      }
307
    }
308
    if constexpr (google::protobuf::Arena::is_arena_constructable<U>::value) {
309
      ABSL_DCHECK_EQ(object->GetArena(), arena());
310
    }
311
    return object;
312
  }
313
314
  // Destructs the object of type `T` located at address `p` and deallocates the
315
  // memory, `p` must have been previously returned by `new_object`.
316
  template <typename T>
317
  void delete_object(T* p) noexcept {
318
    using U = std::remove_const_t<T>;
319
    ABSL_DCHECK(p != nullptr);
320
    if constexpr (google::protobuf::Arena::is_arena_constructable<U>::value) {
321
      ABSL_DCHECK_EQ(p->GetArena(), arena());
322
    }
323
  }
324
325
  void delete_object(std::nullptr_t) = delete;
326
327
 private:
328
  template <typename U>
329
  friend class ArenaAllocator;
330
331
  google::protobuf::Arena* absl_nonnull arena_;
332
};
333
334
// `ArenaAllocator<T>` is an extension of `ArenaAllocator<>` which adheres to
335
// the named C++ requirements for `Allocator`, allowing it to be used in places
336
// which accept custom STL allocators.
337
template <typename T>
338
class ArenaAllocator : public ArenaAllocator<void> {
339
 private:
340
  using Base = ArenaAllocator<void>;
341
342
 public:
343
  static_assert(!std::is_const_v<T>, "T must not be const qualified");
344
  static_assert(!std::is_volatile_v<T>, "T must not be volatile qualified");
345
  static_assert(std::is_object_v<T>, "T must be an object type");
346
347
  using value_type = T;
348
  using pointer = value_type*;
349
  using const_pointer = const value_type*;
350
  using reference = value_type&;
351
  using const_reference = const value_type&;
352
353
  using ArenaAllocator<void>::ArenaAllocator;
354
355
  template <typename U, typename = std::enable_if_t<!std::is_same_v<U, T>>>
356
  // NOLINTNEXTLINE(google-explicit-constructor)
357
  constexpr ArenaAllocator(const ArenaAllocator<U>& other) noexcept
358
1.15M
      : Base(other) {}
cel::ArenaAllocator<char>::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value>, void>(cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> > const&)
Line
Count
Source
358
597k
      : Base(other) {}
cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> >::ArenaAllocator<char, void>(cel::ArenaAllocator<char> const&)
Line
Count
Source
358
362k
      : Base(other) {}
cel::ArenaAllocator<absl::lts_20260526::container_internal::AlignedType<8ul> >::ArenaAllocator<char, void>(cel::ArenaAllocator<char> const&)
Line
Count
Source
358
196k
      : Base(other) {}
359
360
1.55M
  pointer allocate(size_type n, const void* /*hint*/ = nullptr) {
361
1.55M
    return static_cast<pointer>(
362
1.55M
        arena()->AllocateAligned(n * sizeof(T), alignof(T)));
363
1.55M
  }
cel::ArenaAllocator<cel::Value>::allocate(unsigned long, void const*)
Line
Count
Source
360
1.36M
  pointer allocate(size_type n, const void* /*hint*/ = nullptr) {
361
1.36M
    return static_cast<pointer>(
362
1.36M
        arena()->AllocateAligned(n * sizeof(T), alignof(T)));
363
1.36M
  }
cel::ArenaAllocator<absl::lts_20260526::container_internal::AlignedType<8ul> >::allocate(unsigned long, void const*)
Line
Count
Source
360
196k
  pointer allocate(size_type n, const void* /*hint*/ = nullptr) {
361
196k
    return static_cast<pointer>(
362
196k
        arena()->AllocateAligned(n * sizeof(T), alignof(T)));
363
196k
  }
364
365
#if defined(__cpp_lib_allocate_at_least) && \
366
    __cpp_lib_allocate_at_least >= 202302L
367
  std::allocation_result<pointer, size_type> allocate_at_least(size_type n) {
368
    std::allocation_result<pointer, size_type> result;
369
    result.ptr = allocate(n);
370
    result.count = n;
371
    return result;
372
  }
373
#endif
374
375
0
  void deallocate(pointer, size_type) noexcept {}
Unexecuted instantiation: cel::ArenaAllocator<cel::Value>::deallocate(cel::Value*, unsigned long)
Unexecuted instantiation: cel::ArenaAllocator<absl::lts_20260526::container_internal::AlignedType<8ul> >::deallocate(absl::lts_20260526::container_internal::AlignedType<8ul>*, unsigned long)
376
377
  template <typename U, typename... Args>
378
9.76M
  void construct(U* p, Args&&... args) {
379
9.76M
    static_assert(!google::protobuf::Arena::is_arena_constructable<U>::value);
380
9.76M
    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
381
9.76M
  }
void cel::ArenaAllocator<cel::Value>::construct<cel::Value, cel::Value>(cel::Value*, cel::Value&&)
Line
Count
Source
378
9.40M
  void construct(U* p, Args&&... args) {
379
9.40M
    static_assert(!google::protobuf::Arena::is_arena_constructable<U>::value);
380
9.40M
    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
381
9.40M
  }
Unexecuted instantiation: void cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> >::construct<std::__1::pair<cel::Value, cel::Value>, std::__1::pair<cel::Value, cel::Value> >(std::__1::pair<cel::Value, cel::Value>*, std::__1::pair<cel::Value, cel::Value>&&)
void cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> >::construct<std::__1::pair<cel::Value, cel::Value>, std::__1::piecewise_construct_t const&, std::__1::tuple<cel::Value&&>, std::__1::tuple<cel::Value&&> >(std::__1::pair<cel::Value, cel::Value>*, std::__1::piecewise_construct_t const&, std::__1::tuple<cel::Value&&>&&, std::__1::tuple<cel::Value&&>&&)
Line
Count
Source
378
362k
  void construct(U* p, Args&&... args) {
379
362k
    static_assert(!google::protobuf::Arena::is_arena_constructable<U>::value);
380
362k
    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
381
362k
  }
Unexecuted instantiation: void cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> >::construct<std::__1::pair<cel::Value const, cel::Value>, std::__1::pair<cel::Value const, cel::Value> >(std::__1::pair<cel::Value const, cel::Value>*, std::__1::pair<cel::Value const, cel::Value>&&)
Unexecuted instantiation: void cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> >::construct<std::__1::pair<cel::Value const, cel::Value>, std::__1::piecewise_construct_t const&, std::__1::tuple<cel::Value&&>, std::__1::tuple<cel::Value&&> >(std::__1::pair<cel::Value const, cel::Value>*, std::__1::piecewise_construct_t const&, std::__1::tuple<cel::Value&&>&&, std::__1::tuple<cel::Value&&>&&)
382
383
  template <typename U>
384
0
  void destroy(U* p) noexcept {
385
0
    static_assert(!google::protobuf::Arena::is_arena_constructable<U>::value);
386
0
    std::destroy_at(p);
387
0
  }
Unexecuted instantiation: void cel::ArenaAllocator<cel::Value>::destroy<cel::Value>(cel::Value*)
Unexecuted instantiation: void cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> >::destroy<std::__1::pair<cel::Value, cel::Value> >(std::__1::pair<cel::Value, cel::Value>*)
Unexecuted instantiation: void cel::ArenaAllocator<std::__1::pair<cel::Value const, cel::Value> >::destroy<std::__1::pair<cel::Value const, cel::Value> >(std::__1::pair<cel::Value const, cel::Value>*)
388
};
389
390
template <typename T, typename U>
391
inline bool operator==(ArenaAllocator<T> lhs, ArenaAllocator<U> rhs) noexcept {
392
  return lhs.arena() == rhs.arena();
393
}
394
395
template <typename T, typename U>
396
inline bool operator!=(ArenaAllocator<T> lhs, ArenaAllocator<U> rhs) noexcept {
397
  return !operator==(lhs, rhs);
398
}
399
400
ArenaAllocator(google::protobuf::Arena* absl_nonnull) -> ArenaAllocator<void>;
401
template <typename T>
402
ArenaAllocator(const ArenaAllocator<T>&) -> ArenaAllocator<T>;
403
404
// `Allocator<>` is a type-erased vocabulary type capable of performing
405
// allocation/deallocation and construction/destruction using memory owned by
406
// `google::protobuf::Arena` or `operator new`.
407
template <>
408
class Allocator<void> {
409
 public:
410
  using size_type = size_t;
411
  using difference_type = ptrdiff_t;
412
  using propagate_on_container_copy_assignment = std::true_type;
413
  using propagate_on_container_move_assignment = std::true_type;
414
  using propagate_on_container_swap = std::true_type;
415
416
  Allocator() = delete;
417
418
  Allocator(const Allocator&) = default;
419
  Allocator& operator=(const Allocator&) = delete;
420
421
  Allocator(std::nullptr_t) = delete;
422
423
  template <typename U, typename = std::enable_if_t<!std::is_void_v<U>>>
424
  // NOLINTNEXTLINE(google-explicit-constructor)
425
  constexpr Allocator(const Allocator<U>& other) noexcept
426
      : arena_(other.arena_) {}
427
428
  // NOLINTNEXTLINE(google-explicit-constructor)
429
  constexpr Allocator(google::protobuf::Arena* absl_nullable arena) noexcept
430
975k
      : arena_(arena) {}
431
432
  template <typename U>
433
  // NOLINTNEXTLINE(google-explicit-constructor)
434
  constexpr Allocator(
435
      [[maybe_unused]] const NewDeleteAllocator<U>& other) noexcept
436
      : arena_(nullptr) {}
437
438
  template <typename U>
439
  // NOLINTNEXTLINE(google-explicit-constructor)
440
  constexpr Allocator(const ArenaAllocator<U>& other) noexcept
441
      : arena_(other.arena()) {}
442
443
1.90M
  constexpr google::protobuf::Arena* absl_nullable arena() const noexcept {
444
1.90M
    return arena_;
445
1.90M
  }
446
447
  // Allocates at least `nbytes` bytes with a minimum alignment of `alignment`
448
  // from the underlying memory resource. When the underlying memory resource is
449
  // `operator new`, `deallocate_bytes` must be called at some point, otherwise
450
  // calling `deallocate_bytes` is optional. The caller must not pass an object
451
  // constructed in the return memory to `delete_object`, doing so is undefined
452
  // behavior.
453
  ABSL_MUST_USE_RESULT void* allocate_bytes(
454
0
      size_type nbytes, size_type alignment = alignof(std::max_align_t)) {
455
0
    return arena() != nullptr
456
0
               ? ArenaAllocator<void>(arena()).allocate_bytes(nbytes, alignment)
457
0
               : NewDeleteAllocator<void>().allocate_bytes(nbytes, alignment);
458
0
  }
459
460
  // Deallocates memory previously returned by `allocate_bytes`.
461
  void deallocate_bytes(
462
      void* p, size_type nbytes,
463
0
      size_type alignment = alignof(std::max_align_t)) noexcept {
464
0
    arena() != nullptr
465
0
        ? ArenaAllocator<void>(arena()).deallocate_bytes(p, nbytes, alignment)
466
0
        : NewDeleteAllocator<void>().deallocate_bytes(p, nbytes, alignment);
467
0
  }
468
469
  template <typename T>
470
  ABSL_MUST_USE_RESULT T* allocate_object(size_type n = 1) {
471
    return arena() != nullptr
472
               ? ArenaAllocator<void>(arena()).allocate_object<T>(n)
473
               : NewDeleteAllocator<void>().allocate_object<T>(n);
474
  }
475
476
  template <typename T>
477
  void deallocate_object(T* p, size_type n = 1) {
478
    arena() != nullptr ? ArenaAllocator<void>(arena()).deallocate_object(p, n)
479
                       : NewDeleteAllocator<void>().deallocate_object(p, n);
480
  }
481
482
  // Allocates memory suitable for an object of type `T` and constructs the
483
  // object by forwarding the provided arguments. If the underlying memory
484
  // resource is `operator new` is false, `delete_object` must eventually be
485
  // called.
486
  template <typename T, typename... Args>
487
  ABSL_MUST_USE_RESULT T* new_object(Args&&... args) {
488
    return arena() != nullptr ? ArenaAllocator<void>(arena()).new_object<T>(
489
                                    std::forward<Args>(args)...)
490
                              : NewDeleteAllocator<void>().new_object<T>(
491
                                    std::forward<Args>(args)...);
492
  }
493
494
  // Destructs the object of type `T` located at address `p` and deallocates the
495
  // memory, `p` must have been previously returned by `new_object`.
496
  template <typename T>
497
  void delete_object(T* p) noexcept {
498
    arena() != nullptr ? ArenaAllocator<void>(arena()).delete_object(p)
499
                       : NewDeleteAllocator<void>().delete_object(p);
500
  }
501
502
  void delete_object(std::nullptr_t) = delete;
503
504
 private:
505
  template <typename U>
506
  friend class Allocator;
507
508
  google::protobuf::Arena* absl_nullable arena_;
509
};
510
511
// `Allocator<T>` is an extension of `Allocator<>` which adheres to the named
512
// C++ requirements for `Allocator`, allowing it to be used in places which
513
// accept custom STL allocators.
514
template <typename T>
515
class Allocator : public Allocator<void> {
516
 public:
517
  static_assert(!std::is_const_v<T>, "T must not be const qualified");
518
  static_assert(!std::is_volatile_v<T>, "T must not be volatile qualified");
519
  static_assert(std::is_object_v<T>, "T must be an object type");
520
521
  using value_type = T;
522
  using pointer = value_type*;
523
  using const_pointer = const value_type*;
524
  using reference = value_type&;
525
  using const_reference = const value_type&;
526
527
  using Allocator<void>::Allocator;
528
529
  template <typename U, typename = std::enable_if_t<!std::is_same_v<U, T>>>
530
  // NOLINTNEXTLINE(google-explicit-constructor)
531
  constexpr Allocator(const Allocator<U>& other) noexcept
532
      : Allocator(other.arena_) {}
533
534
  pointer allocate(size_type n, const void* /*hint*/ = nullptr) {
535
    return arena() != nullptr ? ArenaAllocator<T>(arena()).allocate(n)
536
                              : NewDeleteAllocator<T>().allocate(n);
537
  }
538
539
#if defined(__cpp_lib_allocate_at_least) && \
540
    __cpp_lib_allocate_at_least >= 202302L
541
  std::allocation_result<pointer, size_type> allocate_at_least(size_type n) {
542
    return arena() != nullptr ? ArenaAllocator<T>(arena()).allocate_at_least(n)
543
                              : NewDeleteAllocator<T>().allocate_at_least(n);
544
  }
545
#endif
546
547
  void deallocate(pointer p, size_type n) noexcept {
548
    arena() != nullptr ? ArenaAllocator<T>(arena()).deallocate(p, n)
549
                       : NewDeleteAllocator<T>().deallocate(p, n);
550
  }
551
552
  template <typename U, typename... Args>
553
  void construct(U* p, Args&&... args) {
554
    arena() != nullptr
555
        ? ArenaAllocator<T>(arena()).construct(p, std::forward<Args>(args)...)
556
        : NewDeleteAllocator<T>().construct(p, std::forward<Args>(args)...);
557
  }
558
559
  template <typename U>
560
  void destroy(U* p) noexcept {
561
    arena() != nullptr ? ArenaAllocator<T>(arena()).destroy(p)
562
                       : NewDeleteAllocator<T>().destroy(p);
563
  }
564
};
565
566
template <typename T, typename U>
567
inline bool operator==(Allocator<T> lhs, Allocator<U> rhs) noexcept {
568
  return lhs.arena() == rhs.arena();
569
}
570
571
template <typename T, typename U>
572
inline bool operator!=(Allocator<T> lhs, Allocator<U> rhs) noexcept {
573
  return !operator==(lhs, rhs);
574
}
575
576
Allocator(google::protobuf::Arena* absl_nullable) -> Allocator<void>;
577
template <typename T>
578
Allocator(const Allocator<T>&) -> Allocator<T>;
579
template <typename T>
580
Allocator(const NewDeleteAllocator<T>&) -> Allocator<T>;
581
template <typename T>
582
Allocator(const ArenaAllocator<T>&) -> Allocator<T>;
583
584
template <typename T>
585
inline NewDeleteAllocator<T> NewDeleteAllocatorFor() noexcept {
586
  static_assert(!std::is_void_v<T>);
587
  return NewDeleteAllocator<T>();
588
}
589
590
template <typename T>
591
inline Allocator<T> ArenaAllocatorFor(
592
    google::protobuf::Arena* absl_nonnull arena) noexcept {
593
  static_assert(!std::is_void_v<T>);
594
  ABSL_DCHECK(arena != nullptr);
595
  return Allocator<T>(arena);
596
}
597
598
}  // namespace cel
599
600
#endif  // THIRD_PARTY_CEL_CPP_COMMON_ALLOCATOR_H_