Coverage Report

Created: 2026-05-27 07:00

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