Coverage Report

Created: 2026-10-09 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/common/memory.h
Line
Count
Source
1
// Copyright 2023 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_MEMORY_H_
16
#define THIRD_PARTY_CEL_CPP_COMMON_MEMORY_H_
17
18
#include <cstddef>
19
#include <memory>
20
#include <ostream>
21
#include <type_traits>
22
#include <utility>
23
24
#include "absl/base/attributes.h"
25
#include "absl/base/nullability.h"
26
#include "absl/log/absl_check.h"
27
#include "absl/numeric/bits.h"
28
#include "common/allocator.h"
29
#include "internal/to_address.h"  // IWYU pragma: keep
30
#include "google/protobuf/arena.h"
31
32
namespace cel {
33
34
// Obtain the address of the underlying element from a raw pointer or "fancy"
35
// pointer.
36
using internal::to_address;
37
38
// MemoryManagement is an enumeration of supported memory management forms
39
// underlying `cel::MemoryManager`.
40
enum class MemoryManagement {
41
  // Region-based (a.k.a. arena). Memory is allocated in fixed size blocks and
42
  // deallocated all at once upon destruction of the `cel::MemoryManager`.
43
  kPooling = 1,
44
  // Reference counting. Memory is allocated with an associated reference
45
  // counter. When the reference counter hits 0, it is deallocated.
46
  kReferenceCounting,
47
};
48
49
std::ostream& operator<<(std::ostream& out, MemoryManagement memory_management);
50
51
class MemoryManager;
52
53
// `ReferenceCountingMemoryManager` is a `MemoryManager` which employs automatic
54
// memory management through reference counting.
55
class ABSL_DEPRECATED("Do not use") ReferenceCountingMemoryManager final {
56
 public:
57
  ReferenceCountingMemoryManager(const ReferenceCountingMemoryManager&) =
58
      delete;
59
  ReferenceCountingMemoryManager(ReferenceCountingMemoryManager&&) = delete;
60
  ReferenceCountingMemoryManager& operator=(
61
      const ReferenceCountingMemoryManager&) = delete;
62
  ReferenceCountingMemoryManager& operator=(ReferenceCountingMemoryManager&&) =
63
      delete;
64
65
 private:
66
  static void* Allocate(size_t size, size_t alignment);
67
68
  static bool Deallocate(void* ptr, size_t size, size_t alignment) noexcept;
69
70
  explicit ReferenceCountingMemoryManager() = default;
71
72
  friend class MemoryManager;
73
};
74
75
// `PoolingMemoryManager` is a `MemoryManager` which employs automatic
76
// memory management through memory pooling.
77
class ABSL_DEPRECATED("Do not use") PoolingMemoryManager final {
78
 public:
79
  PoolingMemoryManager(const PoolingMemoryManager&) = delete;
80
  PoolingMemoryManager(PoolingMemoryManager&&) = delete;
81
  PoolingMemoryManager& operator=(const PoolingMemoryManager&) = delete;
82
  PoolingMemoryManager& operator=(PoolingMemoryManager&&) = delete;
83
84
 private:
85
  // Allocates memory directly from the allocator used by this memory manager.
86
  // If `memory_management()` returns `MemoryManagement::kReferenceCounting`,
87
  // this allocation *must* be explicitly deallocated at some point via
88
  // `Deallocate`. Otherwise deallocation is optional.
89
  ABSL_MUST_USE_RESULT static void* Allocate(google::protobuf::Arena* absl_nonnull arena,
90
0
                                             size_t size, size_t alignment) {
91
0
    ABSL_DCHECK(absl::has_single_bit(alignment))
92
0
        << "alignment must be a power of 2";
93
0
    if (size == 0) {
94
0
      return nullptr;
95
0
    }
96
0
    return arena->AllocateAligned(size, alignment);
97
0
  }
98
99
  // Attempts to deallocate memory previously allocated via `Allocate`, `size`
100
  // and `alignment` must match the values from the previous call to `Allocate`.
101
  // Returns `true` if the deallocation was successful and additional calls to
102
  // `Allocate` may re-use the memory, `false` otherwise. Returns `false` if
103
  // given `nullptr`.
104
  static bool Deallocate(google::protobuf::Arena* absl_nonnull, void*, size_t,
105
0
                         size_t alignment) noexcept {
106
0
    ABSL_DCHECK(absl::has_single_bit(alignment))
107
0
        << "alignment must be a power of 2";
108
0
    return false;
109
0
  }
110
111
  // Registers a custom destructor to be run upon destruction of the memory
112
  // management implementation. Return value is always `true`, indicating that
113
  // the destructor may be called at some point in the future.
114
  static bool OwnCustomDestructor(google::protobuf::Arena* absl_nonnull arena,
115
                                  void* object,
116
0
                                  void (*absl_nonnull destruct)(void*)) {
117
0
    ABSL_DCHECK(destruct != nullptr);
118
0
    arena->OwnCustomDestructor(object, destruct);
119
0
    return true;
120
0
  }
121
122
  template <typename T>
123
  static void DefaultDestructor(void* ptr) {
124
    static_assert(!std::is_trivially_destructible_v<T>);
125
    static_cast<T*>(ptr)->~T();
126
  }
127
128
  explicit PoolingMemoryManager() = default;
129
130
  friend class MemoryManager;
131
};
132
133
// `MemoryManager` is an abstraction for supporting automatic memory management.
134
// All objects created by the `MemoryManager` have a lifetime governed by the
135
// underlying memory management strategy. Currently `MemoryManager` is a
136
// composed type that holds either a reference to
137
// `ReferenceCountingMemoryManager` or owns a `PoolingMemoryManager`.
138
//
139
// ============================ Reference Counting ============================
140
// `Unique`: The object is valid until destruction of the `Unique`.
141
//
142
// `Shared`: The object is valid so long as one or more `Shared` managing the
143
// object exist.
144
//
145
// ================================= Pooling ==================================
146
// `Unique`: The object is valid until destruction of the underlying memory
147
// resources or of the `Unique`.
148
//
149
// `Shared`: The object is valid until destruction of the underlying memory
150
// resources.
151
class MemoryManager final {
152
 public:
153
  // Returns a `MemoryManager` which utilizes an arena.
154
  ABSL_MUST_USE_RESULT static MemoryManager Pooling(
155
0
      google::protobuf::Arena* absl_nonnull arena) {
156
0
    return MemoryManager(arena);
157
0
  }
158
159
0
  explicit MemoryManager(Allocator<> allocator) : arena_(allocator.arena()) {}
160
161
  MemoryManager() = delete;
162
  MemoryManager(const MemoryManager&) = default;
163
  MemoryManager& operator=(const MemoryManager&) = default;
164
165
0
  MemoryManagement memory_management() const noexcept {
166
0
    return arena_ == nullptr ? MemoryManagement::kReferenceCounting
167
0
                             : MemoryManagement::kPooling;
168
0
  }
169
170
  // Allocates memory directly from the allocator used by this memory manager.
171
  // If `memory_management()` returns `MemoryManagement::kReferenceCounting`,
172
  // this allocation *must* be explicitly deallocated at some point via
173
  // `Deallocate`. Otherwise deallocation is optional.
174
0
  ABSL_MUST_USE_RESULT void* Allocate(size_t size, size_t alignment) {
175
0
    if (arena_ == nullptr) {
176
0
      return ReferenceCountingMemoryManager::Allocate(size, alignment);
177
0
    } else {
178
0
      return PoolingMemoryManager::Allocate(arena_, size, alignment);
179
0
    }
180
0
  }
181
182
  // Attempts to deallocate memory previously allocated via `Allocate`, `size`
183
  // and `alignment` must match the values from the previous call to `Allocate`.
184
  // Returns `true` if the deallocation was successful and additional calls to
185
  // `Allocate` may re-use the memory, `false` otherwise. Returns `false` if
186
  // given `nullptr`.
187
0
  bool Deallocate(void* ptr, size_t size, size_t alignment) noexcept {
188
0
    if (arena_ == nullptr) {
189
0
      return ReferenceCountingMemoryManager::Deallocate(ptr, size, alignment);
190
0
    } else {
191
0
      return PoolingMemoryManager::Deallocate(arena_, ptr, size, alignment);
192
0
    }
193
0
  }
194
195
  // Registers a custom destructor to be run upon destruction of the memory
196
  // management implementation. A return of `true` indicates the destructor may
197
  // be called at some point in the future, `false` if will definitely not be
198
  // called. All pooling memory managers return `true` while the reference
199
  // counting memory manager returns `false`.
200
0
  bool OwnCustomDestructor(void* object, void (*absl_nonnull destruct)(void*)) {
201
0
    ABSL_DCHECK(destruct != nullptr);
202
0
    if (arena_ == nullptr) {
203
0
      return false;
204
0
    } else {
205
0
      return PoolingMemoryManager::OwnCustomDestructor(arena_, object,
206
0
                                                       destruct);
207
0
    }
208
0
  }
209
210
0
  google::protobuf::Arena* absl_nullable arena() const noexcept { return arena_; }
211
212
  template <typename T>
213
  // NOLINTNEXTLINE(google-explicit-constructor)
214
  operator Allocator<T>() const {
215
    return arena();
216
  }
217
218
0
  friend void swap(MemoryManager& lhs, MemoryManager& rhs) noexcept {
219
0
    using std::swap;
220
0
    swap(lhs.arena_, rhs.arena_);
221
0
  }
222
223
 private:
224
  friend class PoolingMemoryManager;
225
226
0
  explicit MemoryManager(std::nullptr_t) : arena_(nullptr) {}
227
228
0
  explicit MemoryManager(google::protobuf::Arena* absl_nonnull arena) : arena_(arena) {}
229
230
  // If `nullptr`, we are using reference counting. Otherwise we are using
231
  // Pooling. We use `UnreachablePooling()` as a sentinel to detect use after
232
  // move otherwise the moved-from `MemoryManager` would be in a valid state and
233
  // utilize reference counting.
234
  google::protobuf::Arena* absl_nullable arena_;
235
};
236
237
using MemoryManagerRef = MemoryManager;
238
239
}  // namespace cel
240
241
#endif  // THIRD_PARTY_CEL_CPP_COMMON_MEMORY_H_