/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 <cstdint> |
20 | | #include <memory> |
21 | | #include <ostream> |
22 | | #include <type_traits> |
23 | | #include <utility> |
24 | | |
25 | | #include "absl/base/attributes.h" |
26 | | #include "absl/base/macros.h" |
27 | | #include "absl/base/nullability.h" |
28 | | #include "absl/base/optimization.h" |
29 | | #include "absl/log/absl_check.h" |
30 | | #include "absl/numeric/bits.h" |
31 | | #include "common/allocator.h" |
32 | | #include "common/arena.h" |
33 | | #include "common/data.h" |
34 | | #include "common/internal/metadata.h" |
35 | | #include "common/internal/reference_count.h" |
36 | | #include "common/reference_count.h" |
37 | | #include "internal/exceptions.h" |
38 | | #include "internal/to_address.h" // IWYU pragma: keep |
39 | | #include "google/protobuf/arena.h" |
40 | | |
41 | | namespace cel { |
42 | | |
43 | | // Obtain the address of the underlying element from a raw pointer or "fancy" |
44 | | // pointer. |
45 | | using internal::to_address; |
46 | | |
47 | | // MemoryManagement is an enumeration of supported memory management forms |
48 | | // underlying `cel::MemoryManager`. |
49 | | enum class MemoryManagement { |
50 | | // Region-based (a.k.a. arena). Memory is allocated in fixed size blocks and |
51 | | // deallocated all at once upon destruction of the `cel::MemoryManager`. |
52 | | kPooling = 1, |
53 | | // Reference counting. Memory is allocated with an associated reference |
54 | | // counter. When the reference counter hits 0, it is deallocated. |
55 | | kReferenceCounting, |
56 | | }; |
57 | | |
58 | | std::ostream& operator<<(std::ostream& out, MemoryManagement memory_management); |
59 | | |
60 | | class ABSL_ATTRIBUTE_TRIVIAL_ABI [[nodiscard]] Owner; |
61 | | class Borrower; |
62 | | template <typename T> |
63 | | class ABSL_ATTRIBUTE_TRIVIAL_ABI [[nodiscard]] Unique; |
64 | | template <typename T> |
65 | | class ABSL_ATTRIBUTE_TRIVIAL_ABI [[nodiscard]] Owned; |
66 | | template <typename T> |
67 | | class Borrowed; |
68 | | template <typename T> |
69 | | struct Ownable; |
70 | | template <typename T> |
71 | | struct Borrowable; |
72 | | |
73 | | class MemoryManager; |
74 | | class ReferenceCountingMemoryManager; |
75 | | class PoolingMemoryManager; |
76 | | |
77 | | namespace common_internal { |
78 | | template <typename T> |
79 | | inline constexpr bool kNotMessageLiteAndNotData = |
80 | | std::conjunction_v<std::negation<std::is_base_of<google::protobuf::MessageLite, T>>, |
81 | | std::negation<std::is_base_of<Data, T>>>; |
82 | | template <typename To, typename From> |
83 | | inline constexpr bool kIsPointerConvertible = std::is_convertible_v<From*, To*>; |
84 | | template <typename To, typename From> |
85 | | inline constexpr bool kNotSameAndIsPointerConvertible = |
86 | | std::conjunction_v<std::negation<std::is_same<To, From>>, |
87 | | std::bool_constant<kIsPointerConvertible<To, From>>>; |
88 | | |
89 | | // Clears the contents of `owner`, and returns the reference count if in use. |
90 | | const ReferenceCount* absl_nullable OwnerRelease(Owner owner) noexcept; |
91 | | const ReferenceCount* absl_nullable BorrowerRelease(Borrower borrower) noexcept; |
92 | | template <typename T> |
93 | | Owned<const T> WrapEternal(const T* value); |
94 | | |
95 | | // Pointer tag used by `cel::Unique` to indicate that the destructor needs to be |
96 | | // registered with the arena, but it has not been done yet. Must be done when |
97 | | // releasing. |
98 | | inline constexpr uintptr_t kUniqueArenaUnownedBit = uintptr_t{1} << 0; |
99 | | inline constexpr uintptr_t kUniqueArenaBits = kUniqueArenaUnownedBit; |
100 | | inline constexpr uintptr_t kUniqueArenaPointerMask = ~kUniqueArenaBits; |
101 | | } // namespace common_internal |
102 | | |
103 | | template <typename T, typename... Args> |
104 | | Owned<T> AllocateShared(Allocator<> allocator, Args&&... args); |
105 | | |
106 | | template <typename T> |
107 | | Owned<T> WrapShared(T* object, Allocator<> allocator); |
108 | | |
109 | | // `Owner` represents a reference to some co-owned data, of which this owner is |
110 | | // one of the co-owners. When using reference counting, `Owner` performs |
111 | | // increment/decrement where appropriate similar to `std::shared_ptr`. |
112 | | // `Borrower` is similar to `Owner`, except that it is always trivially |
113 | | // copyable/destructible. In that sense, `Borrower` is similar to |
114 | | // `std::reference_wrapper<const Owner>`. |
115 | | class ABSL_ATTRIBUTE_TRIVIAL_ABI [[nodiscard]] Owner final { |
116 | | private: |
117 | | static constexpr uintptr_t kNone = common_internal::kMetadataOwnerNone; |
118 | | static constexpr uintptr_t kReferenceCountBit = |
119 | | common_internal::kMetadataOwnerReferenceCountBit; |
120 | | static constexpr uintptr_t kArenaBit = |
121 | | common_internal::kMetadataOwnerArenaBit; |
122 | | static constexpr uintptr_t kBits = common_internal::kMetadataOwnerBits; |
123 | | static constexpr uintptr_t kPointerMask = |
124 | | common_internal::kMetadataOwnerPointerMask; |
125 | | |
126 | | public: |
127 | 0 | static Owner None() noexcept { return Owner(); } |
128 | | |
129 | 0 | static Owner Allocator(Allocator<> allocator) noexcept { |
130 | 0 | auto* arena = allocator.arena(); |
131 | 0 | return arena != nullptr ? Arena(arena) : None(); |
132 | 0 | } |
133 | | |
134 | | static Owner Arena(google::protobuf::Arena* absl_nonnull arena |
135 | 0 | ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { |
136 | 0 | ABSL_DCHECK(arena != nullptr); |
137 | 0 | return Owner(reinterpret_cast<uintptr_t>(arena) | kArenaBit); |
138 | 0 | } |
139 | | |
140 | | static Owner Arena(std::nullptr_t) = delete; |
141 | | |
142 | | static Owner ReferenceCount(const ReferenceCount* absl_nonnull reference_count |
143 | 0 | ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { |
144 | 0 | ABSL_DCHECK(reference_count != nullptr); |
145 | 0 | common_internal::StrongRef(*reference_count); |
146 | 0 | return Owner(reinterpret_cast<uintptr_t>(reference_count) | |
147 | 0 | kReferenceCountBit); |
148 | 0 | } |
149 | | |
150 | | static Owner ReferenceCount(std::nullptr_t) = delete; |
151 | | |
152 | | Owner() = default; |
153 | | |
154 | 0 | Owner(const Owner& other) noexcept : Owner(CopyFrom(other.ptr_)) {} |
155 | | |
156 | 0 | Owner(Owner&& other) noexcept : Owner(MoveFrom(other.ptr_)) {} |
157 | | |
158 | | template <typename T> |
159 | | // NOLINTNEXTLINE(google-explicit-constructor) |
160 | | Owner(const Owned<T>& owned) noexcept; |
161 | | |
162 | | template <typename T> |
163 | | // NOLINTNEXTLINE(google-explicit-constructor) |
164 | | Owner(Owned<T>&& owned) noexcept; |
165 | | |
166 | | explicit Owner(Borrower borrower) noexcept; |
167 | | |
168 | | template <typename T> |
169 | | explicit Owner(Borrowed<T> borrowed) noexcept; |
170 | | |
171 | 0 | ~Owner() { Destroy(ptr_); } |
172 | | |
173 | 0 | Owner& operator=(const Owner& other) noexcept { |
174 | 0 | if (ptr_ != other.ptr_) { |
175 | 0 | Destroy(ptr_); |
176 | 0 | ptr_ = CopyFrom(other.ptr_); |
177 | 0 | } |
178 | 0 | return *this; |
179 | 0 | } |
180 | | |
181 | 0 | Owner& operator=(Owner&& other) noexcept { |
182 | 0 | if (ABSL_PREDICT_TRUE(this != &other)) { |
183 | 0 | Destroy(ptr_); |
184 | 0 | ptr_ = MoveFrom(other.ptr_); |
185 | 0 | } |
186 | 0 | return *this; |
187 | 0 | } |
188 | | |
189 | | template <typename T> |
190 | | // NOLINTNEXTLINE(google-explicit-constructor) |
191 | | Owner& operator=(const Owned<T>& owned) noexcept; |
192 | | |
193 | | template <typename T> |
194 | | // NOLINTNEXTLINE(google-explicit-constructor) |
195 | | Owner& operator=(Owned<T>&& owned) noexcept; |
196 | | |
197 | 0 | explicit operator bool() const noexcept { return !IsNone(ptr_); } |
198 | | |
199 | 0 | google::protobuf::Arena* absl_nullable arena() const noexcept { |
200 | 0 | return (ptr_ & Owner::kBits) == Owner::kArenaBit |
201 | 0 | ? reinterpret_cast<google::protobuf::Arena*>(ptr_ & Owner::kPointerMask) |
202 | 0 | : nullptr; |
203 | 0 | } |
204 | | |
205 | 0 | void reset() noexcept { |
206 | 0 | Destroy(ptr_); |
207 | 0 | ptr_ = 0; |
208 | 0 | } |
209 | | |
210 | | // Tests whether two owners have ownership over the same data, that is they |
211 | | // are co-owners. |
212 | 0 | friend bool operator==(const Owner& lhs, const Owner& rhs) noexcept { |
213 | 0 | // A reference count and arena can never occupy the same memory address, so |
214 | 0 | // we can compare for equality without masking off the bits. |
215 | 0 | return lhs.ptr_ == rhs.ptr_; |
216 | 0 | } |
217 | | |
218 | | private: |
219 | | template <typename T> |
220 | | friend class Unique; |
221 | | friend class Borrower; |
222 | | template <typename T, typename... Args> |
223 | | friend Owned<T> AllocateShared(cel::Allocator<> allocator, Args&&... args); |
224 | | template <typename T> |
225 | | friend Owned<T> WrapShared(T* object, cel::Allocator<> allocator); |
226 | | template <typename U> |
227 | | friend struct Ownable; |
228 | | friend const common_internal::ReferenceCount* absl_nullable |
229 | | common_internal::OwnerRelease(Owner owner) noexcept; |
230 | | friend const common_internal::ReferenceCount* absl_nullable |
231 | | common_internal::BorrowerRelease(Borrower borrower) noexcept; |
232 | | friend struct ArenaTraits<Owner>; |
233 | | |
234 | 0 | constexpr explicit Owner(uintptr_t ptr) noexcept : ptr_(ptr) {} |
235 | | |
236 | 0 | static constexpr bool IsNone(uintptr_t ptr) noexcept { return ptr == kNone; } |
237 | | |
238 | 0 | static constexpr bool IsArena(uintptr_t ptr) noexcept { |
239 | 0 | return (ptr & kArenaBit) != kNone; |
240 | 0 | } |
241 | | |
242 | 0 | static constexpr bool IsReferenceCount(uintptr_t ptr) noexcept { |
243 | 0 | return (ptr & kReferenceCountBit) != kNone; |
244 | 0 | } |
245 | | |
246 | | ABSL_ATTRIBUTE_RETURNS_NONNULL |
247 | 0 | static google::protobuf::Arena* absl_nonnull AsArena(uintptr_t ptr) noexcept { |
248 | 0 | ABSL_ASSERT(IsArena(ptr)); |
249 | 0 | return reinterpret_cast<google::protobuf::Arena*>(ptr & kPointerMask); |
250 | 0 | } |
251 | | |
252 | | ABSL_ATTRIBUTE_RETURNS_NONNULL |
253 | | static const common_internal::ReferenceCount* absl_nonnull AsReferenceCount( |
254 | 0 | uintptr_t ptr) noexcept { |
255 | 0 | ABSL_ASSERT(IsReferenceCount(ptr)); |
256 | 0 | return reinterpret_cast<const common_internal::ReferenceCount*>( |
257 | 0 | ptr & kPointerMask); |
258 | 0 | } |
259 | | |
260 | 0 | static uintptr_t CopyFrom(uintptr_t other) noexcept { return Own(other); } |
261 | | |
262 | 0 | static uintptr_t MoveFrom(uintptr_t& other) noexcept { |
263 | 0 | return std::exchange(other, kNone); |
264 | 0 | } |
265 | | |
266 | 0 | static void Destroy(uintptr_t ptr) noexcept { Unown(ptr); } |
267 | | |
268 | 0 | static uintptr_t Own(uintptr_t ptr) noexcept { |
269 | 0 | if (IsReferenceCount(ptr)) { |
270 | 0 | const auto* refcount = Owner::AsReferenceCount(ptr); |
271 | 0 | ABSL_ASSUME(refcount != nullptr); |
272 | 0 | common_internal::StrongRef(refcount); |
273 | 0 | } |
274 | 0 | return ptr; |
275 | 0 | } |
276 | | |
277 | 0 | static void Unown(uintptr_t ptr) noexcept { |
278 | 0 | if (IsReferenceCount(ptr)) { |
279 | 0 | const auto* reference_count = AsReferenceCount(ptr); |
280 | 0 | ABSL_ASSUME(reference_count != nullptr); |
281 | 0 | common_internal::StrongUnref(reference_count); |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | | uintptr_t ptr_ = kNone; |
286 | | }; |
287 | | |
288 | 0 | inline bool operator!=(const Owner& lhs, const Owner& rhs) noexcept { |
289 | 0 | return !operator==(lhs, rhs); |
290 | 0 | } |
291 | | |
292 | | namespace common_internal { |
293 | | |
294 | 0 | inline const ReferenceCount* absl_nullable OwnerRelease(Owner owner) noexcept { |
295 | 0 | uintptr_t ptr = std::exchange(owner.ptr_, kMetadataOwnerNone); |
296 | 0 | if (Owner::IsReferenceCount(ptr)) { |
297 | 0 | return Owner::AsReferenceCount(ptr); |
298 | 0 | } |
299 | 0 | return nullptr; |
300 | 0 | } |
301 | | |
302 | | } // namespace common_internal |
303 | | |
304 | | template <> |
305 | | struct ArenaTraits<Owner> { |
306 | 0 | static bool trivially_destructible(const Owner& owner) { |
307 | 0 | return !Owner::IsReferenceCount(owner.ptr_); |
308 | 0 | } |
309 | | }; |
310 | | |
311 | | // `Borrower` represents a reference to some borrowed data, where the data has |
312 | | // at least one owner. When using reference counting, `Borrower` does not |
313 | | // participate in incrementing/decrementing the reference count. Thus `Borrower` |
314 | | // will not keep the underlying data alive. |
315 | | class Borrower final { |
316 | | public: |
317 | 0 | static Borrower None() noexcept { return Borrower(); } |
318 | | |
319 | 0 | static Borrower Allocator(Allocator<> allocator) noexcept { |
320 | 0 | auto* arena = allocator.arena(); |
321 | 0 | return arena != nullptr ? Arena(arena) : None(); |
322 | 0 | } |
323 | | |
324 | | static Borrower Arena(google::protobuf::Arena* absl_nonnull arena |
325 | 128 | ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { |
326 | 128 | ABSL_DCHECK(arena != nullptr); |
327 | 128 | return Borrower(reinterpret_cast<uintptr_t>(arena) | Owner::kArenaBit); |
328 | 128 | } |
329 | | |
330 | | static Borrower Arena(std::nullptr_t) = delete; |
331 | | |
332 | | static Borrower ReferenceCount( |
333 | | const ReferenceCount* absl_nonnull reference_count |
334 | 0 | ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { |
335 | 0 | ABSL_DCHECK(reference_count != nullptr); |
336 | 0 | return Borrower(reinterpret_cast<uintptr_t>(reference_count) | |
337 | 0 | Owner::kReferenceCountBit); |
338 | 0 | } |
339 | | |
340 | | static Borrower ReferenceCount(std::nullptr_t) = delete; |
341 | | |
342 | | Borrower() = default; |
343 | | Borrower(const Borrower&) = default; |
344 | | Borrower(Borrower&&) = default; |
345 | | Borrower& operator=(const Borrower&) = default; |
346 | | Borrower& operator=(Borrower&&) = default; |
347 | | |
348 | | template <typename T> |
349 | | // NOLINTNEXTLINE(google-explicit-constructor) |
350 | | Borrower(const Owned<T>& owned ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept; |
351 | | |
352 | | template <typename T> |
353 | | // NOLINTNEXTLINE(google-explicit-constructor) |
354 | | Borrower(Borrowed<T> borrowed) noexcept; |
355 | | |
356 | | // NOLINTNEXTLINE(google-explicit-constructor) |
357 | | Borrower(const Owner& owner ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept |
358 | 0 | : ptr_(owner.ptr_) {} |
359 | | |
360 | | // NOLINTNEXTLINE(google-explicit-constructor) |
361 | | Borrower& operator=( |
362 | 0 | const Owner& owner ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { |
363 | 0 | ptr_ = owner.ptr_; |
364 | 0 | return *this; |
365 | 0 | } |
366 | | |
367 | | Borrower& operator=(Owner&&) = delete; |
368 | | |
369 | | template <typename T> |
370 | | Borrower& operator=( |
371 | | const Owned<T>& owned ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept; |
372 | | |
373 | | template <typename T> |
374 | | Borrower& operator=(Owned<T>&&) = delete; |
375 | | |
376 | | template <typename T> |
377 | | // NOLINTNEXTLINE(google-explicit-constructor) |
378 | | Borrower& operator=(Borrowed<T> borrowed) noexcept; |
379 | | |
380 | 0 | explicit operator bool() const noexcept { return !Owner::IsNone(ptr_); } |
381 | | |
382 | 128 | google::protobuf::Arena* absl_nullable arena() const noexcept { |
383 | 128 | return (ptr_ & Owner::kBits) == Owner::kArenaBit |
384 | 128 | ? reinterpret_cast<google::protobuf::Arena*>(ptr_ & Owner::kPointerMask) |
385 | 128 | : nullptr; |
386 | 128 | } |
387 | | |
388 | 0 | void reset() noexcept { ptr_ = 0; } |
389 | | |
390 | | // Tests whether two borrowers are borrowing the same data. |
391 | 0 | friend bool operator==(Borrower lhs, Borrower rhs) noexcept { |
392 | 0 | // A reference count and arena can never occupy the same memory address, so |
393 | 0 | // we can compare for equality without masking off the bits. |
394 | 0 | return lhs.ptr_ == rhs.ptr_; |
395 | 0 | } |
396 | | |
397 | | private: |
398 | | friend class Owner; |
399 | | template <typename U> |
400 | | friend struct Borrowable; |
401 | | friend const common_internal::ReferenceCount* absl_nullable |
402 | | common_internal::BorrowerRelease(Borrower borrower) noexcept; |
403 | | |
404 | 128 | constexpr explicit Borrower(uintptr_t ptr) noexcept : ptr_(ptr) {} |
405 | | |
406 | | uintptr_t ptr_ = Owner::kNone; |
407 | | }; |
408 | | |
409 | 0 | inline bool operator!=(Borrower lhs, Borrower rhs) noexcept { |
410 | 0 | return !operator==(lhs, rhs); |
411 | 0 | } |
412 | | |
413 | 0 | inline bool operator==(Borrower lhs, const Owner& rhs) noexcept { |
414 | 0 | return operator==(lhs, Borrower(rhs)); |
415 | 0 | } |
416 | | |
417 | 0 | inline bool operator==(const Owner& lhs, Borrower rhs) noexcept { |
418 | 0 | return operator==(Borrower(lhs), rhs); |
419 | 0 | } |
420 | | |
421 | 0 | inline bool operator!=(Borrower lhs, const Owner& rhs) noexcept { |
422 | 0 | return !operator==(lhs, rhs); |
423 | 0 | } |
424 | | |
425 | 0 | inline bool operator!=(const Owner& lhs, Borrower rhs) noexcept { |
426 | 0 | return !operator==(lhs, rhs); |
427 | 0 | } |
428 | | |
429 | | inline Owner::Owner(Borrower borrower) noexcept |
430 | | : ptr_(Owner::Own(borrower.ptr_)) {} |
431 | | |
432 | | namespace common_internal { |
433 | | |
434 | | inline const ReferenceCount* absl_nullable BorrowerRelease( |
435 | 0 | Borrower borrower) noexcept { |
436 | 0 | uintptr_t ptr = borrower.ptr_; |
437 | 0 | if (Owner::IsReferenceCount(ptr)) { |
438 | 0 | return Owner::AsReferenceCount(ptr); |
439 | 0 | } |
440 | 0 | return nullptr; |
441 | 0 | } |
442 | | |
443 | | } // namespace common_internal |
444 | | |
445 | | template <typename T, typename... Args> |
446 | | Unique<T> AllocateUnique(Allocator<> allocator, Args&&... args); |
447 | | |
448 | | // Wrap an already created `T` in `Unique`. Requires that `T` is not const, |
449 | | // otherwise `GetArena()` may return slightly unexpected results depending on if |
450 | | // it is the default value. |
451 | | template <typename T> |
452 | | std::enable_if_t<!std::is_const_v<T>, Unique<T>> WrapUnique(T* object); |
453 | | |
454 | | template <typename T> |
455 | | Unique<T> WrapUnique(T* object, Allocator<> allocator); |
456 | | |
457 | | // `Unique<T>` points to an object which was allocated using `Allocator<>` or |
458 | | // `Allocator<T>`. It has ownership over the object, and will perform any |
459 | | // destruction and deallocation required. `Unique` must not outlive the |
460 | | // underlying arena, if any. Unlike `Owned` and `Borrowed`, `Unique` supports |
461 | | // arena incompatible objects. It is very similar to `std::unique_ptr` when |
462 | | // using a custom deleter. |
463 | | // |
464 | | // IMPLEMENTATION NOTES: |
465 | | // When utilizing arenas, we optionally perform a risky optimization via |
466 | | // `AllocateUnique`. We do not use `Arena::Create`, instead we directly allocate |
467 | | // the bytes and construct it in place ourselves. This avoids registering the |
468 | | // destructor when required. Instead we register the destructor ourselves, if |
469 | | // required, during `Unique::release`. This allows us to avoid deferring |
470 | | // destruction of the object until the arena is destroyed, avoiding the cost |
471 | | // involved in doing so. |
472 | | template <typename T> |
473 | | class ABSL_ATTRIBUTE_TRIVIAL_ABI [[nodiscard]] Unique final { |
474 | | public: |
475 | | using element_type = T; |
476 | | |
477 | | static_assert(!std::is_array_v<T>, "T must not be an array"); |
478 | | static_assert(!std::is_reference_v<T>, "T must not be a reference"); |
479 | | static_assert(!std::is_volatile_v<T>, "T must not be volatile qualified"); |
480 | | |
481 | 0 | Unique() = default; |
482 | | Unique(const Unique&) = delete; |
483 | | Unique& operator=(const Unique&) = delete; |
484 | | |
485 | | explicit Unique(T* ptr) noexcept |
486 | | : Unique(ptr, common_internal::GetArena(ptr)) {} |
487 | | |
488 | | // NOLINTNEXTLINE(google-explicit-constructor) |
489 | | Unique(std::nullptr_t) noexcept : Unique() {} |
490 | | |
491 | 0 | Unique(Unique&& other) noexcept : Unique(other.ptr_, other.arena_) { |
492 | 0 | other.ptr_ = nullptr; |
493 | 0 | } |
494 | | |
495 | | template <typename U, |
496 | | typename = std::enable_if_t< |
497 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
498 | | // NOLINTNEXTLINE(google-explicit-constructor) |
499 | | Unique(Unique<U>&& other) noexcept : Unique(other.ptr_, other.arena_) { |
500 | | other.ptr_ = nullptr; |
501 | | } |
502 | | |
503 | 0 | ~Unique() { Delete(); } |
504 | | |
505 | 0 | Unique& operator=(Unique&& other) noexcept { |
506 | 0 | if (ABSL_PREDICT_TRUE(this != &other)) { |
507 | 0 | Delete(); |
508 | 0 | ptr_ = other.ptr_; |
509 | 0 | arena_ = other.arena_; |
510 | 0 | other.ptr_ = nullptr; |
511 | 0 | } |
512 | 0 | return *this; |
513 | 0 | } |
514 | | |
515 | | template <typename U, |
516 | | typename = std::enable_if_t< |
517 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
518 | | // NOLINTNEXTLINE(google-explicit-constructor) |
519 | | Unique& operator=(U* other) noexcept { |
520 | | reset(other); |
521 | | return *this; |
522 | | } |
523 | | |
524 | | template <typename U, |
525 | | typename = std::enable_if_t< |
526 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
527 | | // NOLINTNEXTLINE(google-explicit-constructor) |
528 | | Unique& operator=(Unique<U>&& other) noexcept { |
529 | | Delete(); |
530 | | ptr_ = other.ptr_; |
531 | | arena_ = other.arena_; |
532 | | other.ptr_ = nullptr; |
533 | | return *this; |
534 | | } |
535 | | |
536 | | // NOLINTNEXTLINE(google-explicit-constructor) |
537 | | Unique& operator=(std::nullptr_t) noexcept { |
538 | | reset(); |
539 | | return *this; |
540 | | } |
541 | | |
542 | 0 | T& operator*() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { |
543 | 0 | ABSL_DCHECK(static_cast<bool>(*this)); |
544 | 0 | return *get(); |
545 | 0 | } |
546 | | |
547 | 0 | T* absl_nonnull operator->() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { |
548 | 0 | ABSL_DCHECK(static_cast<bool>(*this)); |
549 | 0 | return get(); |
550 | 0 | } |
551 | | |
552 | | // Relinquishes ownership of `T*`, returning it. If `T` was allocated and |
553 | | // constructed using an arena, no further action is required. If `T` was |
554 | | // allocated and constructed without an arena, the caller must eventually call |
555 | | // `delete`. |
556 | 0 | ABSL_MUST_USE_RESULT T* release() noexcept { |
557 | 0 | PreRelease(); |
558 | 0 | return std::exchange(ptr_, nullptr); |
559 | 0 | } |
560 | | |
561 | | void reset() noexcept { reset(nullptr); } |
562 | | |
563 | | void reset(T* ptr) noexcept { |
564 | | Delete(); |
565 | | ptr_ = ptr; |
566 | | arena_ = reinterpret_cast<uintptr_t>(common_internal::GetArena(ptr)); |
567 | | } |
568 | | |
569 | | void reset(std::nullptr_t) noexcept { |
570 | | Delete(); |
571 | | ptr_ = nullptr; |
572 | | arena_ = 0; |
573 | | } |
574 | | |
575 | 0 | explicit operator bool() const noexcept { return get() != nullptr; } |
576 | | |
577 | 0 | google::protobuf::Arena* absl_nullable arena() const noexcept { |
578 | 0 | return reinterpret_cast<google::protobuf::Arena*>( |
579 | 0 | arena_ & common_internal::kUniqueArenaPointerMask); |
580 | 0 | } |
581 | | |
582 | | friend void swap(Unique& lhs, Unique& rhs) noexcept { |
583 | | using std::swap; |
584 | | swap(lhs.ptr_, rhs.ptr_); |
585 | | swap(lhs.arena_, rhs.arena_); |
586 | | } |
587 | | |
588 | | private: |
589 | | template <typename U> |
590 | | friend class Unique; |
591 | | template <typename U> |
592 | | friend class Owned; |
593 | | template <typename U, typename... Args> |
594 | | friend Unique<U> AllocateUnique(Allocator<> allocator, Args&&... args); |
595 | | template <typename U> |
596 | | friend Unique<U> WrapUnique(U* object, Allocator<> allocator); |
597 | | friend class ReferenceCountingMemoryManager; |
598 | | friend class PoolingMemoryManager; |
599 | | friend struct std::pointer_traits<Unique<T>>; |
600 | | friend struct ArenaTraits<Unique<T>>; |
601 | | |
602 | 0 | Unique(T* ptr, uintptr_t arena) noexcept : ptr_(ptr), arena_(arena) {} |
603 | | |
604 | | Unique(T* ptr, google::protobuf::Arena* arena, bool unowned = false) noexcept |
605 | 0 | : Unique(ptr, |
606 | 0 | reinterpret_cast<uintptr_t>(arena) | |
607 | 0 | (unowned ? common_internal::kUniqueArenaUnownedBit : 0)) { |
608 | 0 | ABSL_ASSERT(!unowned || (unowned && arena != nullptr)); |
609 | 0 | } |
610 | | |
611 | | Unique(google::protobuf::Arena* arena, T* ptr, bool unowned = false) noexcept |
612 | | : Unique(ptr, arena, unowned) {} |
613 | | |
614 | 0 | T* get() const noexcept { return ptr_; } |
615 | | |
616 | 0 | void Delete() const noexcept { |
617 | 0 | if (static_cast<bool>(*this)) { |
618 | 0 | if (arena_ != 0) { |
619 | 0 | if ((arena_ & common_internal::kUniqueArenaBits) == |
620 | 0 | common_internal::kUniqueArenaUnownedBit) { |
621 | | // We never registered the destructor, call it if necessary. |
622 | | if constexpr (!std::is_trivially_destructible_v<T> && |
623 | 0 | !google::protobuf::Arena::is_destructor_skippable<T>::value) { |
624 | 0 | std::destroy_at(ptr_); |
625 | 0 | } |
626 | 0 | } |
627 | 0 | } else { |
628 | 0 | delete ptr_; |
629 | 0 | } |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | 0 | void PreRelease() noexcept { |
634 | | if constexpr (!std::is_trivially_destructible_v<T> && |
635 | 0 | !google::protobuf::Arena::is_destructor_skippable<T>::value) { |
636 | 0 | if (static_cast<bool>(*this) && |
637 | 0 | (arena_ & common_internal::kUniqueArenaBits) == |
638 | 0 | common_internal::kUniqueArenaUnownedBit) { |
639 | | // We never registered the destructor, call it if necessary. |
640 | 0 | arena()->OwnDestructor(const_cast<std::remove_const_t<T>*>(ptr_)); |
641 | 0 | arena_ &= common_internal::kUniqueArenaPointerMask; |
642 | 0 | } |
643 | 0 | } |
644 | 0 | } |
645 | | |
646 | | void Release(T** ptr, Owner* owner) noexcept { |
647 | | if (ptr_ == nullptr) { |
648 | | *ptr = nullptr; |
649 | | return; |
650 | | } |
651 | | PreRelease(); |
652 | | *ptr = std::exchange(ptr_, nullptr); |
653 | | if (arena_ == 0) { |
654 | | owner->ptr_ = reinterpret_cast<uintptr_t>( |
655 | | common_internal::MakeDeletingReferenceCount(*ptr)) | |
656 | | common_internal::kMetadataOwnerReferenceCountBit; |
657 | | } else { |
658 | | owner->ptr_ = reinterpret_cast<uintptr_t>(arena()) | |
659 | | common_internal::kMetadataOwnerArenaBit; |
660 | | } |
661 | | } |
662 | | |
663 | | T* ptr_ = nullptr; |
664 | | // Potentially tagged pointer to `google::protobuf::Arena`. The tag is used to determine |
665 | | // whether we still need to register the destructor with the `google::protobuf::Arena`. |
666 | | uintptr_t arena_ = 0; |
667 | | }; |
668 | | |
669 | | template <typename T> |
670 | | Unique(T*) -> Unique<T>; |
671 | | |
672 | | template <typename T, typename... Args> |
673 | | Unique<T> AllocateUnique(Allocator<> allocator, Args&&... args) { |
674 | | using U = std::remove_cv_t<T>; |
675 | | static_assert(!std::is_reference_v<U>, "T must not be a reference"); |
676 | | static_assert(!std::is_array_v<U>, "T must not be an array"); |
677 | | |
678 | | U* object; |
679 | | google::protobuf::Arena* absl_nullable arena = allocator.arena(); |
680 | | bool unowned; |
681 | | if constexpr (google::protobuf::Arena::is_arena_constructable<U>::value) { |
682 | | object = google::protobuf::Arena::Create<U>(arena, std::forward<Args>(args)...); |
683 | | // For arena-compatible proto types, let the Arena::Create handle |
684 | | // registering the destructor call. |
685 | | // Otherwise, Unique<T> retains a pointer to the owning arena so it may |
686 | | // conditionally register T::~T depending on usage. |
687 | | unowned = false; |
688 | | } else { |
689 | | void* p = allocator.allocate_bytes(sizeof(U), alignof(U)); |
690 | | CEL_INTERNAL_TRY { |
691 | | if constexpr (ArenaTraits<>::constructible<U>()) { |
692 | | object = ::new (p) U(arena, std::forward<Args>(args)...); |
693 | | } else { |
694 | | object = ::new (p) U(std::forward<Args>(args)...); |
695 | | } |
696 | | } |
697 | | CEL_INTERNAL_CATCH_ANY { |
698 | | allocator.deallocate_bytes(p, sizeof(U), alignof(U)); |
699 | | CEL_INTERNAL_RETHROW; |
700 | | } |
701 | | unowned = |
702 | | arena != nullptr && !ArenaTraits<>::trivially_destructible(*object); |
703 | | } |
704 | | return Unique<T>(object, arena, unowned); |
705 | | } |
706 | | |
707 | | template <typename T> |
708 | | std::enable_if_t<!std::is_const_v<T>, Unique<T>> WrapUnique(T* object) { |
709 | | return Unique<T>(object); |
710 | | } |
711 | | |
712 | | template <typename T> |
713 | 0 | Unique<T> WrapUnique(T* object, Allocator<> allocator) { |
714 | 0 | return Unique<T>(object, allocator.arena()); |
715 | 0 | } |
716 | | |
717 | | template <typename T> |
718 | | inline bool operator==(const Unique<T>& lhs, std::nullptr_t) { |
719 | | return !static_cast<bool>(lhs); |
720 | | } |
721 | | |
722 | | template <typename T> |
723 | | inline bool operator==(std::nullptr_t, const Unique<T>& rhs) { |
724 | | return !static_cast<bool>(rhs); |
725 | | } |
726 | | |
727 | | template <typename T> |
728 | 0 | inline bool operator!=(const Unique<T>& lhs, std::nullptr_t) { |
729 | 0 | return static_cast<bool>(lhs); |
730 | 0 | } |
731 | | |
732 | | template <typename T> |
733 | | inline bool operator!=(std::nullptr_t, const Unique<T>& rhs) { |
734 | | return static_cast<bool>(rhs); |
735 | | } |
736 | | |
737 | | } // namespace cel |
738 | | |
739 | | namespace std { |
740 | | |
741 | | template <typename T> |
742 | | struct pointer_traits<cel::Unique<T>> { |
743 | | using pointer = cel::Unique<T>; |
744 | | using element_type = typename cel::Unique<T>::element_type; |
745 | | using difference_type = ptrdiff_t; |
746 | | |
747 | | template <typename U> |
748 | | using rebind = cel::Unique<U>; |
749 | | |
750 | 0 | static element_type* to_address(const pointer& p) noexcept { return p.ptr_; } |
751 | | }; |
752 | | |
753 | | } // namespace std |
754 | | |
755 | | namespace cel { |
756 | | |
757 | | template <typename T> |
758 | | struct ArenaTraits<Unique<T>> { |
759 | | static bool trivially_destructible(const Unique<T>& unique) { |
760 | | return unique.arena_ != 0 && |
761 | | (unique.arena_ & common_internal::kUniqueArenaBits) == 0; |
762 | | } |
763 | | }; |
764 | | |
765 | | // `Owned<T>` points to an object which was allocated using `Allocator<>` or |
766 | | // `Allocator<T>`. It has co-ownership over the object. `T` must meet the named |
767 | | // requirement `ArenaConstructable`. |
768 | | template <typename T> |
769 | | class ABSL_ATTRIBUTE_TRIVIAL_ABI [[nodiscard]] Owned final { |
770 | | public: |
771 | | using element_type = T; |
772 | | |
773 | | static_assert(!std::is_array_v<T>, "T must not be an array"); |
774 | | static_assert(!std::is_reference_v<T>, "T must not be a reference"); |
775 | | static_assert(!std::is_volatile_v<T>, "T must not be volatile qualified"); |
776 | | static_assert(!std::is_void_v<T>, "T must not be void"); |
777 | | |
778 | | Owned() = default; |
779 | | Owned(const Owned&) = default; |
780 | | Owned& operator=(const Owned&) = default; |
781 | | |
782 | | Owned(Owned&& other) noexcept |
783 | | : Owned(std::exchange(other.value_, nullptr), std::move(other.owner_)) {} |
784 | | |
785 | | template <typename U, |
786 | | typename = std::enable_if_t< |
787 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
788 | | // NOLINTNEXTLINE(google-explicit-constructor) |
789 | | Owned(const Owned<U>& other) noexcept : Owned(other.value_, other.owner_) {} |
790 | | |
791 | | template <typename U, |
792 | | typename = std::enable_if_t< |
793 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
794 | | // NOLINTNEXTLINE(google-explicit-constructor) |
795 | | Owned(Owned<U>&& other) noexcept |
796 | | : Owned(std::exchange(other.value_, nullptr), std::move(other.owner_)) {} |
797 | | |
798 | | template <typename U, typename = std::enable_if_t< |
799 | | common_internal::kIsPointerConvertible<T, U>>> |
800 | | explicit Owned(Borrowed<U> other) noexcept; |
801 | | |
802 | | template <typename U, typename = std::enable_if_t< |
803 | | common_internal::kIsPointerConvertible<T, U>>> |
804 | | // NOLINTNEXTLINE(google-explicit-constructor) |
805 | | Owned(Unique<U>&& other) : Owned() { |
806 | | other.Release(&value_, &owner_); |
807 | | } |
808 | | |
809 | | Owned(Owner owner, T* value ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept |
810 | | : Owned(value, std::move(owner)) {} |
811 | | |
812 | | // NOLINTNEXTLINE(google-explicit-constructor) |
813 | | Owned(std::nullptr_t) noexcept : Owned() {} |
814 | | |
815 | | Owned& operator=(Owned&& other) noexcept { |
816 | | if (ABSL_PREDICT_TRUE(this != &other)) { |
817 | | value_ = std::exchange(other.value_, nullptr); |
818 | | owner_ = std::move(other.owner_); |
819 | | } |
820 | | return *this; |
821 | | } |
822 | | |
823 | | template <typename U, |
824 | | typename = std::enable_if_t< |
825 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
826 | | // NOLINTNEXTLINE(google-explicit-constructor) |
827 | | Owned& operator=(const Owned<U>& other) noexcept { |
828 | | value_ = other.value_; |
829 | | owner_ = other.owner_; |
830 | | return *this; |
831 | | } |
832 | | |
833 | | template <typename U, |
834 | | typename = std::enable_if_t< |
835 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
836 | | // NOLINTNEXTLINE(google-explicit-constructor) |
837 | | Owned& operator=(Owned<U>&& other) noexcept { |
838 | | value_ = std::exchange(other.value_, nullptr); |
839 | | owner_ = std::move(other.owner_); |
840 | | return *this; |
841 | | } |
842 | | |
843 | | template <typename U, typename = std::enable_if_t< |
844 | | common_internal::kIsPointerConvertible<T, U>>> |
845 | | // NOLINTNEXTLINE(google-explicit-constructor) |
846 | | Owned& operator=(Borrowed<U> other) noexcept; |
847 | | |
848 | | template <typename U, typename = std::enable_if_t< |
849 | | common_internal::kIsPointerConvertible<T, U>>> |
850 | | // NOLINTNEXTLINE(google-explicit-constructor) |
851 | | Owned& operator=(Unique<U>&& other) { |
852 | | owner_.reset(); |
853 | | other.Release(&value_, &owner_); |
854 | | return *this; |
855 | | } |
856 | | |
857 | | // NOLINTNEXTLINE(google-explicit-constructor) |
858 | | Owned& operator=(std::nullptr_t) noexcept { |
859 | | reset(); |
860 | | return *this; |
861 | | } |
862 | | |
863 | | T& operator*() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { |
864 | | ABSL_DCHECK(static_cast<bool>(*this)); |
865 | | return *get(); |
866 | | } |
867 | | |
868 | | T* absl_nonnull operator->() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { |
869 | | ABSL_DCHECK(static_cast<bool>(*this)); |
870 | | return get(); |
871 | | } |
872 | | |
873 | | void reset() noexcept { |
874 | | value_ = nullptr; |
875 | | owner_.reset(); |
876 | | } |
877 | | |
878 | | google::protobuf::Arena* absl_nullable arena() const noexcept { return owner_.arena(); } |
879 | | |
880 | | explicit operator bool() const noexcept { return get() != nullptr; } |
881 | | |
882 | | friend void swap(Owned& lhs, Owned& rhs) noexcept { |
883 | | using std::swap; |
884 | | swap(lhs.value_, rhs.value_); |
885 | | swap(lhs.owner_, rhs.owner_); |
886 | | } |
887 | | |
888 | | private: |
889 | | friend class Owner; |
890 | | friend class Borrower; |
891 | | template <typename U> |
892 | | friend class Owned; |
893 | | template <typename U> |
894 | | friend class Borrowed; |
895 | | template <typename U> |
896 | | friend struct Ownable; |
897 | | template <typename U, typename... Args> |
898 | | friend Owned<U> AllocateShared(Allocator<> allocator, Args&&... args); |
899 | | template <typename U> |
900 | | friend Owned<U> WrapShared(U* object, Allocator<> allocator); |
901 | | template <typename U> |
902 | | friend Owned<const U> common_internal::WrapEternal(const U* value); |
903 | | friend struct std::pointer_traits<Owned<T>>; |
904 | | friend struct ArenaTraits<Owned<T>>; |
905 | | |
906 | | Owned(T* value, Owner owner) noexcept |
907 | | : value_(value), owner_(std::move(owner)) {} |
908 | | |
909 | | T* get() const noexcept { return value_; } |
910 | | |
911 | | T* value_ = nullptr; |
912 | | Owner owner_; |
913 | | }; |
914 | | |
915 | | template <typename T> |
916 | | Owned(T*) -> Owned<T>; |
917 | | template <typename T> |
918 | | Owned(Unique<T>) -> Owned<T>; |
919 | | template <typename T> |
920 | | Owned(Owner, T*) -> Owned<T>; |
921 | | template <typename T> |
922 | | Owned(Borrowed<T>) -> Owned<T>; |
923 | | |
924 | | } // namespace cel |
925 | | |
926 | | namespace std { |
927 | | |
928 | | template <typename T> |
929 | | struct pointer_traits<cel::Owned<T>> { |
930 | | using pointer = cel::Owned<T>; |
931 | | using element_type = typename cel::Owned<T>::element_type; |
932 | | using difference_type = ptrdiff_t; |
933 | | |
934 | | template <typename U> |
935 | | using rebind = cel::Owned<U>; |
936 | | |
937 | | static element_type* to_address(const pointer& p) noexcept { |
938 | | return p.value_; |
939 | | } |
940 | | }; |
941 | | |
942 | | } // namespace std |
943 | | |
944 | | namespace cel { |
945 | | |
946 | | template <typename T> |
947 | | struct ArenaTraits<Owned<T>> { |
948 | | static bool trivially_destructible(const Owned<T>& owned) { |
949 | | return ArenaTraits<>::trivially_destructible(owned.owner_); |
950 | | } |
951 | | }; |
952 | | |
953 | | template <typename T> |
954 | | Owner::Owner(const Owned<T>& owned) noexcept : Owner(owned.owner_) {} |
955 | | |
956 | | template <typename T> |
957 | | Owner::Owner(Owned<T>&& owned) noexcept : Owner(std::move(owned.owner_)) { |
958 | | owned.value_ = nullptr; |
959 | | } |
960 | | |
961 | | template <typename T> |
962 | | Owner& Owner::operator=(const Owned<T>& owned) noexcept { |
963 | | *this = owned.owner_; |
964 | | return *this; |
965 | | } |
966 | | |
967 | | template <typename T> |
968 | | Owner& Owner::operator=(Owned<T>&& owned) noexcept { |
969 | | *this = std::move(owned.owner_); |
970 | | owned.value_ = nullptr; |
971 | | return *this; |
972 | | } |
973 | | |
974 | | template <typename T> |
975 | | bool operator==(const Owned<T>& lhs, std::nullptr_t) noexcept { |
976 | | return !static_cast<bool>(lhs); |
977 | | } |
978 | | |
979 | | template <typename T> |
980 | | bool operator==(std::nullptr_t, const Owned<T>& rhs) noexcept { |
981 | | return rhs == nullptr; |
982 | | } |
983 | | |
984 | | template <typename T> |
985 | | bool operator!=(const Owned<T>& lhs, std::nullptr_t) noexcept { |
986 | | return !operator==(lhs, nullptr); |
987 | | } |
988 | | |
989 | | template <typename T> |
990 | | bool operator!=(std::nullptr_t, const Owned<T>& rhs) noexcept { |
991 | | return !operator==(nullptr, rhs); |
992 | | } |
993 | | |
994 | | template <typename T, typename... Args> |
995 | | Owned<T> AllocateShared(Allocator<> allocator, Args&&... args) { |
996 | | using U = std::remove_cv_t<T>; |
997 | | static_assert(!std::is_reference_v<U>, "T must not be a reference"); |
998 | | static_assert(!std::is_array_v<U>, "T must not be an array"); |
999 | | |
1000 | | U* object; |
1001 | | Owner owner; |
1002 | | if (google::protobuf::Arena* absl_nullable arena = allocator.arena(); |
1003 | | arena != nullptr) { |
1004 | | object = ArenaAllocator(arena).template new_object<U>( |
1005 | | std::forward<Args>(args)...); |
1006 | | owner.ptr_ = reinterpret_cast<uintptr_t>(arena) | |
1007 | | common_internal::kMetadataOwnerArenaBit; |
1008 | | } else { |
1009 | | const common_internal::ReferenceCount* refcount; |
1010 | | std::tie(object, refcount) = common_internal::MakeEmplacedReferenceCount<U>( |
1011 | | std::forward<Args>(args)...); |
1012 | | owner.ptr_ = reinterpret_cast<uintptr_t>(refcount) | |
1013 | | common_internal::kMetadataOwnerReferenceCountBit; |
1014 | | } |
1015 | | return Owned<U>(object, std::move(owner)); |
1016 | | } |
1017 | | |
1018 | | template <typename T> |
1019 | | Owned<T> WrapShared(T* object, Allocator<> allocator) { |
1020 | | Owner owner; |
1021 | | if (object == nullptr) { |
1022 | | } else if (allocator.arena() != nullptr) { |
1023 | | owner.ptr_ = reinterpret_cast<uintptr_t>( |
1024 | | static_cast<google::protobuf::Arena*>(allocator.arena())) | |
1025 | | common_internal::kMetadataOwnerArenaBit; |
1026 | | } else { |
1027 | | owner.ptr_ = reinterpret_cast<uintptr_t>( |
1028 | | common_internal::MakeDeletingReferenceCount(object)) | |
1029 | | common_internal::kMetadataOwnerReferenceCountBit; |
1030 | | } |
1031 | | return Owned<T>(object, std::move(owner)); |
1032 | | } |
1033 | | |
1034 | | template <typename T> |
1035 | | std::enable_if_t<!std::is_const_v<T>, Owned<T>> WrapShared(T* object) { |
1036 | | return WrapShared(object, object->GetArena()); |
1037 | | } |
1038 | | |
1039 | | namespace common_internal { |
1040 | | |
1041 | | template <typename T> |
1042 | | Owned<const T> WrapEternal(const T* value) { |
1043 | | return Owned<const T>(value, Owner::None()); |
1044 | | } |
1045 | | |
1046 | | } // namespace common_internal |
1047 | | |
1048 | | // `Borrowed<T>` points to an object which was allocated using `Allocator<>` or |
1049 | | // `Allocator<T>`. It has no ownership over the object, and is only valid so |
1050 | | // long as one or more owners of the object exist. `T` must meet the named |
1051 | | // requirement `ArenaConstructable`. |
1052 | | template <typename T> |
1053 | | class Borrowed final { |
1054 | | public: |
1055 | | using element_type = T; |
1056 | | |
1057 | | static_assert(!std::is_array_v<T>, "T must not be an array"); |
1058 | | static_assert(!std::is_reference_v<T>, "T must not be a reference"); |
1059 | | static_assert(!std::is_volatile_v<T>, "T must not be volatile qualified"); |
1060 | | static_assert(!std::is_void_v<T>, "T must not be void"); |
1061 | | |
1062 | | Borrowed() = default; |
1063 | | Borrowed(const Borrowed&) = default; |
1064 | | Borrowed(Borrowed&&) = default; |
1065 | | Borrowed& operator=(const Borrowed&) = default; |
1066 | | Borrowed& operator=(Borrowed&&) = default; |
1067 | | |
1068 | | template <typename U, |
1069 | | typename = std::enable_if_t< |
1070 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
1071 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1072 | | Borrowed(const Borrowed<U>& other) noexcept |
1073 | | : Borrowed(other.value_, other.borrower_) {} |
1074 | | |
1075 | | template <typename U, |
1076 | | typename = std::enable_if_t< |
1077 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
1078 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1079 | | Borrowed(Borrowed<U>&& other) noexcept |
1080 | | : Borrowed(other.value_, other.borrower_) {} |
1081 | | |
1082 | | template <typename U, typename = std::enable_if_t< |
1083 | | common_internal::kIsPointerConvertible<T, U>>> |
1084 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1085 | | Borrowed(const Owned<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept |
1086 | | : Borrowed(other.value_, other.owner_) {} |
1087 | | |
1088 | | Borrowed(Borrower borrower, T* ptr) noexcept : Borrowed(ptr, borrower) {} |
1089 | | |
1090 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1091 | | Borrowed(std::nullptr_t) noexcept : Borrowed() {} |
1092 | | |
1093 | | template <typename U, |
1094 | | typename = std::enable_if_t< |
1095 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
1096 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1097 | | Borrowed& operator=(const Borrowed<U>& other) noexcept { |
1098 | | value_ = other.value_; |
1099 | | borrower_ = other.borrower_; |
1100 | | return *this; |
1101 | | } |
1102 | | |
1103 | | template <typename U, |
1104 | | typename = std::enable_if_t< |
1105 | | common_internal::kNotSameAndIsPointerConvertible<T, U>>> |
1106 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1107 | | Borrowed& operator=(Borrowed<U>&& other) noexcept { |
1108 | | value_ = other.value_; |
1109 | | borrower_ = other.borrower_; |
1110 | | return *this; |
1111 | | } |
1112 | | |
1113 | | template <typename U, typename = std::enable_if_t< |
1114 | | common_internal::kIsPointerConvertible<T, U>>> |
1115 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1116 | | Borrowed& operator=( |
1117 | | const Owned<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { |
1118 | | value_ = other.value_; |
1119 | | borrower_ = other.borrower_; |
1120 | | return *this; |
1121 | | } |
1122 | | |
1123 | | template <typename U, typename = std::enable_if_t< |
1124 | | common_internal::kIsPointerConvertible<T, U>>> |
1125 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1126 | | Borrowed& operator=(Owned<U>&&) = delete; |
1127 | | |
1128 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1129 | | Borrowed& operator=(std::nullptr_t) noexcept { |
1130 | | reset(); |
1131 | | return *this; |
1132 | | } |
1133 | | |
1134 | | T& operator*() const noexcept { |
1135 | | ABSL_DCHECK(static_cast<bool>(*this)); |
1136 | | return *get(); |
1137 | | } |
1138 | | |
1139 | | T* absl_nonnull operator->() const noexcept { |
1140 | | ABSL_DCHECK(static_cast<bool>(*this)); |
1141 | | return get(); |
1142 | | } |
1143 | | |
1144 | | void reset() noexcept { |
1145 | | value_ = nullptr; |
1146 | | borrower_.reset(); |
1147 | | } |
1148 | | |
1149 | | google::protobuf::Arena* absl_nullable arena() const noexcept { |
1150 | | return borrower_.arena(); |
1151 | | } |
1152 | | |
1153 | | explicit operator bool() const noexcept { return get() != nullptr; } |
1154 | | |
1155 | | private: |
1156 | | friend class Owner; |
1157 | | friend class Borrower; |
1158 | | template <typename U> |
1159 | | friend class Owned; |
1160 | | template <typename U> |
1161 | | friend class Borrowed; |
1162 | | template <typename U> |
1163 | | friend struct Borrowable; |
1164 | | friend struct std::pointer_traits<Borrowed<T>>; |
1165 | | |
1166 | | constexpr Borrowed(T* value, Borrower borrower) noexcept |
1167 | | : value_(value), borrower_(borrower) {} |
1168 | | |
1169 | | T* get() const noexcept { return value_; } |
1170 | | |
1171 | | T* value_ = nullptr; |
1172 | | Borrower borrower_; |
1173 | | }; |
1174 | | |
1175 | | template <typename T> |
1176 | | Borrowed(T*) -> Borrowed<T>; |
1177 | | template <typename T> |
1178 | | Borrowed(Borrower, T*) -> Borrowed<T>; |
1179 | | template <typename T> |
1180 | | Borrowed(Owned<T>) -> Borrowed<T>; |
1181 | | |
1182 | | } // namespace cel |
1183 | | |
1184 | | namespace std { |
1185 | | |
1186 | | template <typename T> |
1187 | | struct pointer_traits<cel::Borrowed<T>> { |
1188 | | using pointer = cel::Borrowed<T>; |
1189 | | using element_type = typename cel::Borrowed<T>::element_type; |
1190 | | using difference_type = ptrdiff_t; |
1191 | | |
1192 | | template <typename U> |
1193 | | using rebind = cel::Borrowed<U>; |
1194 | | |
1195 | | static element_type* to_address(pointer p) noexcept { return p.value_; } |
1196 | | }; |
1197 | | |
1198 | | } // namespace std |
1199 | | |
1200 | | namespace cel { |
1201 | | |
1202 | | template <typename T> |
1203 | | Owner::Owner(Borrowed<T> borrowed) noexcept : Owner(borrowed.borrower_) {} |
1204 | | |
1205 | | template <typename T> |
1206 | | Borrower::Borrower(const Owned<T>& owned ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept |
1207 | | : Borrower(owned.owner_) {} |
1208 | | |
1209 | | template <typename T> |
1210 | | Borrower::Borrower(Borrowed<T> borrowed) noexcept |
1211 | | : Borrower(borrowed.borrower_) {} |
1212 | | |
1213 | | template <typename T> |
1214 | | Borrower& Borrower::operator=( |
1215 | | const Owned<T>& owned ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { |
1216 | | *this = owned.owner_; |
1217 | | return *this; |
1218 | | } |
1219 | | |
1220 | | template <typename T> |
1221 | | Borrower& Borrower::operator=(Borrowed<T> borrowed) noexcept { |
1222 | | *this = borrowed.borrower_; |
1223 | | return *this; |
1224 | | } |
1225 | | |
1226 | | template <typename T> |
1227 | | bool operator==(Borrowed<T> lhs, std::nullptr_t) noexcept { |
1228 | | return !static_cast<bool>(lhs); |
1229 | | } |
1230 | | |
1231 | | template <typename T> |
1232 | | bool operator==(std::nullptr_t, Borrowed<T> rhs) noexcept { |
1233 | | return rhs == nullptr; |
1234 | | } |
1235 | | |
1236 | | template <typename T> |
1237 | | bool operator!=(Borrowed<T> lhs, std::nullptr_t) noexcept { |
1238 | | return !operator==(lhs, nullptr); |
1239 | | } |
1240 | | |
1241 | | template <typename T> |
1242 | | bool operator!=(std::nullptr_t, Borrowed<T> rhs) noexcept { |
1243 | | return !operator==(nullptr, rhs); |
1244 | | } |
1245 | | |
1246 | | template <typename T> |
1247 | | template <typename U, typename> |
1248 | | Owned<T>::Owned(Borrowed<U> other) noexcept |
1249 | | : Owned(other.value_, Owner(other.borrower_)) {} |
1250 | | |
1251 | | template <typename T> |
1252 | | template <typename U, typename> |
1253 | | Owned<T>& Owned<T>::operator=(Borrowed<U> other) noexcept { |
1254 | | value_ = other.value_; |
1255 | | owner_ = Owner(other.borrower_); |
1256 | | return *this; |
1257 | | } |
1258 | | |
1259 | | // `Ownable<T>` is a mixin for enabling the ability to get `Owned` that refer to |
1260 | | // this. |
1261 | | template <typename T> |
1262 | | struct Ownable { |
1263 | | protected: |
1264 | | Owned<const T> Own() const noexcept { |
1265 | | static_assert(std::is_base_of_v<Data, T>, "T must be derived from Data"); |
1266 | | const T* const that = static_cast<const T*>(this); |
1267 | | return Owned<const T>( |
1268 | | Owner(Owner::Own(static_cast<const Data*>(that)->owner_)), that); |
1269 | | } |
1270 | | |
1271 | | Owned<T> Own() noexcept { |
1272 | | static_assert(std::is_base_of_v<Data, T>, "T must be derived from Data"); |
1273 | | T* const that = static_cast<T*>(this); |
1274 | | return Owned<T>(Owner(Owner::Own(static_cast<Data*>(that)->owner_)), that); |
1275 | | } |
1276 | | |
1277 | | ABSL_DEPRECATED("Use Own") |
1278 | | Owned<const T> shared_from_this() const noexcept { return Own(); } |
1279 | | |
1280 | | ABSL_DEPRECATED("Use Own") |
1281 | | Owned<T> shared_from_this() noexcept { return Own(); } |
1282 | | }; |
1283 | | |
1284 | | // `Borrowable<T>` is a mixin for enabling the ability to get `Borrowed` that |
1285 | | // refer to this. |
1286 | | template <typename T> |
1287 | | struct Borrowable { |
1288 | | protected: |
1289 | | Borrowed<const T> Borrow() const noexcept { |
1290 | | static_assert(std::is_base_of_v<Data, T>, "T must be derived from Data"); |
1291 | | const T* const that = static_cast<const T*>(this); |
1292 | | return Borrowed<const T>(Borrower(static_cast<const Data*>(that)->owner_), |
1293 | | that); |
1294 | | } |
1295 | | |
1296 | | Borrowed<T> Borrow() noexcept { |
1297 | | static_assert(std::is_base_of_v<Data, T>, "T must be derived from Data"); |
1298 | | T* const that = static_cast<T*>(this); |
1299 | | return Borrowed<T>(Borrower(static_cast<Data*>(that)->owner_), that); |
1300 | | } |
1301 | | }; |
1302 | | |
1303 | | // `ReferenceCountingMemoryManager` is a `MemoryManager` which employs automatic |
1304 | | // memory management through reference counting. |
1305 | | class ReferenceCountingMemoryManager final { |
1306 | | public: |
1307 | | ReferenceCountingMemoryManager(const ReferenceCountingMemoryManager&) = |
1308 | | delete; |
1309 | | ReferenceCountingMemoryManager(ReferenceCountingMemoryManager&&) = delete; |
1310 | | ReferenceCountingMemoryManager& operator=( |
1311 | | const ReferenceCountingMemoryManager&) = delete; |
1312 | | ReferenceCountingMemoryManager& operator=(ReferenceCountingMemoryManager&&) = |
1313 | | delete; |
1314 | | |
1315 | | private: |
1316 | | static void* Allocate(size_t size, size_t alignment); |
1317 | | |
1318 | | static bool Deallocate(void* ptr, size_t size, size_t alignment) noexcept; |
1319 | | |
1320 | | explicit ReferenceCountingMemoryManager() = default; |
1321 | | |
1322 | | friend class MemoryManager; |
1323 | | }; |
1324 | | |
1325 | | // `PoolingMemoryManager` is a `MemoryManager` which employs automatic |
1326 | | // memory management through memory pooling. |
1327 | | class PoolingMemoryManager final { |
1328 | | public: |
1329 | | PoolingMemoryManager(const PoolingMemoryManager&) = delete; |
1330 | | PoolingMemoryManager(PoolingMemoryManager&&) = delete; |
1331 | | PoolingMemoryManager& operator=(const PoolingMemoryManager&) = delete; |
1332 | | PoolingMemoryManager& operator=(PoolingMemoryManager&&) = delete; |
1333 | | |
1334 | | private: |
1335 | | // Allocates memory directly from the allocator used by this memory manager. |
1336 | | // If `memory_management()` returns `MemoryManagement::kReferenceCounting`, |
1337 | | // this allocation *must* be explicitly deallocated at some point via |
1338 | | // `Deallocate`. Otherwise deallocation is optional. |
1339 | | ABSL_MUST_USE_RESULT static void* Allocate(google::protobuf::Arena* absl_nonnull arena, |
1340 | 0 | size_t size, size_t alignment) { |
1341 | 0 | ABSL_DCHECK(absl::has_single_bit(alignment)) |
1342 | 0 | << "alignment must be a power of 2"; |
1343 | 0 | if (size == 0) { |
1344 | 0 | return nullptr; |
1345 | 0 | } |
1346 | 0 | return arena->AllocateAligned(size, alignment); |
1347 | 0 | } |
1348 | | |
1349 | | // Attempts to deallocate memory previously allocated via `Allocate`, `size` |
1350 | | // and `alignment` must match the values from the previous call to `Allocate`. |
1351 | | // Returns `true` if the deallocation was successful and additional calls to |
1352 | | // `Allocate` may re-use the memory, `false` otherwise. Returns `false` if |
1353 | | // given `nullptr`. |
1354 | | static bool Deallocate(google::protobuf::Arena* absl_nonnull, void*, size_t, |
1355 | 0 | size_t alignment) noexcept { |
1356 | 0 | ABSL_DCHECK(absl::has_single_bit(alignment)) |
1357 | 0 | << "alignment must be a power of 2"; |
1358 | 0 | return false; |
1359 | 0 | } |
1360 | | |
1361 | | // Registers a custom destructor to be run upon destruction of the memory |
1362 | | // management implementation. Return value is always `true`, indicating that |
1363 | | // the destructor may be called at some point in the future. |
1364 | | static bool OwnCustomDestructor(google::protobuf::Arena* absl_nonnull arena, |
1365 | | void* object, |
1366 | 0 | void (*absl_nonnull destruct)(void*)) { |
1367 | 0 | ABSL_DCHECK(destruct != nullptr); |
1368 | 0 | arena->OwnCustomDestructor(object, destruct); |
1369 | 0 | return true; |
1370 | 0 | } |
1371 | | |
1372 | | template <typename T> |
1373 | | static void DefaultDestructor(void* ptr) { |
1374 | | static_assert(!std::is_trivially_destructible_v<T>); |
1375 | | static_cast<T*>(ptr)->~T(); |
1376 | | } |
1377 | | |
1378 | | explicit PoolingMemoryManager() = default; |
1379 | | |
1380 | | friend class MemoryManager; |
1381 | | }; |
1382 | | |
1383 | | // `MemoryManager` is an abstraction for supporting automatic memory management. |
1384 | | // All objects created by the `MemoryManager` have a lifetime governed by the |
1385 | | // underlying memory management strategy. Currently `MemoryManager` is a |
1386 | | // composed type that holds either a reference to |
1387 | | // `ReferenceCountingMemoryManager` or owns a `PoolingMemoryManager`. |
1388 | | // |
1389 | | // ============================ Reference Counting ============================ |
1390 | | // `Unique`: The object is valid until destruction of the `Unique`. |
1391 | | // |
1392 | | // `Shared`: The object is valid so long as one or more `Shared` managing the |
1393 | | // object exist. |
1394 | | // |
1395 | | // ================================= Pooling ================================== |
1396 | | // `Unique`: The object is valid until destruction of the underlying memory |
1397 | | // resources or of the `Unique`. |
1398 | | // |
1399 | | // `Shared`: The object is valid until destruction of the underlying memory |
1400 | | // resources. |
1401 | | class MemoryManager final { |
1402 | | public: |
1403 | | // Returns a `MemoryManager` which utilizes an arena but never frees its |
1404 | | // memory. It is effectively a memory leak and should only be used for limited |
1405 | | // use cases, such as initializing singletons which live for the life of the |
1406 | | // program. |
1407 | | static MemoryManager Unmanaged(); |
1408 | | |
1409 | | // Returns a `MemoryManager` which utilizes reference counting. |
1410 | 0 | ABSL_MUST_USE_RESULT static MemoryManager ReferenceCounting() { |
1411 | 0 | return MemoryManager(nullptr); |
1412 | 0 | } |
1413 | | |
1414 | | // Returns a `MemoryManager` which utilizes an arena. |
1415 | | ABSL_MUST_USE_RESULT static MemoryManager Pooling( |
1416 | 3.59k | google::protobuf::Arena* absl_nonnull arena) { |
1417 | 3.59k | return MemoryManager(arena); |
1418 | 3.59k | } |
1419 | | |
1420 | 0 | explicit MemoryManager(Allocator<> allocator) : arena_(allocator.arena()) {} |
1421 | | |
1422 | | MemoryManager() = delete; |
1423 | | MemoryManager(const MemoryManager&) = default; |
1424 | | MemoryManager& operator=(const MemoryManager&) = default; |
1425 | | |
1426 | 0 | MemoryManagement memory_management() const noexcept { |
1427 | 0 | return arena_ == nullptr ? MemoryManagement::kReferenceCounting |
1428 | 0 | : MemoryManagement::kPooling; |
1429 | 0 | } |
1430 | | |
1431 | | // Allocates memory directly from the allocator used by this memory manager. |
1432 | | // If `memory_management()` returns `MemoryManagement::kReferenceCounting`, |
1433 | | // this allocation *must* be explicitly deallocated at some point via |
1434 | | // `Deallocate`. Otherwise deallocation is optional. |
1435 | 0 | ABSL_MUST_USE_RESULT void* Allocate(size_t size, size_t alignment) { |
1436 | 0 | if (arena_ == nullptr) { |
1437 | 0 | return ReferenceCountingMemoryManager::Allocate(size, alignment); |
1438 | 0 | } else { |
1439 | 0 | return PoolingMemoryManager::Allocate(arena_, size, alignment); |
1440 | 0 | } |
1441 | 0 | } |
1442 | | |
1443 | | // Attempts to deallocate memory previously allocated via `Allocate`, `size` |
1444 | | // and `alignment` must match the values from the previous call to `Allocate`. |
1445 | | // Returns `true` if the deallocation was successful and additional calls to |
1446 | | // `Allocate` may re-use the memory, `false` otherwise. Returns `false` if |
1447 | | // given `nullptr`. |
1448 | 0 | bool Deallocate(void* ptr, size_t size, size_t alignment) noexcept { |
1449 | 0 | if (arena_ == nullptr) { |
1450 | 0 | return ReferenceCountingMemoryManager::Deallocate(ptr, size, alignment); |
1451 | 0 | } else { |
1452 | 0 | return PoolingMemoryManager::Deallocate(arena_, ptr, size, alignment); |
1453 | 0 | } |
1454 | 0 | } |
1455 | | |
1456 | | // Registers a custom destructor to be run upon destruction of the memory |
1457 | | // management implementation. A return of `true` indicates the destructor may |
1458 | | // be called at some point in the future, `false` if will definitely not be |
1459 | | // called. All pooling memory managers return `true` while the reference |
1460 | | // counting memory manager returns `false`. |
1461 | 0 | bool OwnCustomDestructor(void* object, void (*absl_nonnull destruct)(void*)) { |
1462 | 0 | ABSL_DCHECK(destruct != nullptr); |
1463 | 0 | if (arena_ == nullptr) { |
1464 | 0 | return false; |
1465 | 0 | } else { |
1466 | 0 | return PoolingMemoryManager::OwnCustomDestructor(arena_, object, |
1467 | 0 | destruct); |
1468 | 0 | } |
1469 | 0 | } |
1470 | | |
1471 | 5.24k | google::protobuf::Arena* absl_nullable arena() const noexcept { return arena_; } |
1472 | | |
1473 | | template <typename T> |
1474 | | // NOLINTNEXTLINE(google-explicit-constructor) |
1475 | | operator Allocator<T>() const { |
1476 | | return arena(); |
1477 | | } |
1478 | | |
1479 | 0 | friend void swap(MemoryManager& lhs, MemoryManager& rhs) noexcept { |
1480 | 0 | using std::swap; |
1481 | 0 | swap(lhs.arena_, rhs.arena_); |
1482 | 0 | } |
1483 | | |
1484 | | private: |
1485 | | friend class PoolingMemoryManager; |
1486 | | |
1487 | 0 | explicit MemoryManager(std::nullptr_t) : arena_(nullptr) {} |
1488 | | |
1489 | 3.59k | explicit MemoryManager(google::protobuf::Arena* absl_nonnull arena) : arena_(arena) {} |
1490 | | |
1491 | | // If `nullptr`, we are using reference counting. Otherwise we are using |
1492 | | // Pooling. We use `UnreachablePooling()` as a sentinel to detect use after |
1493 | | // move otherwise the moved-from `MemoryManager` would be in a valid state and |
1494 | | // utilize reference counting. |
1495 | | google::protobuf::Arena* absl_nullable arena_; |
1496 | | }; |
1497 | | |
1498 | | using MemoryManagerRef = MemoryManager; |
1499 | | |
1500 | | } // namespace cel |
1501 | | |
1502 | | #endif // THIRD_PARTY_CEL_CPP_COMMON_MEMORY_H_ |