GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: storage_ptr.hpp
Date: 2025-12-23 17:20:53
Exec Total Coverage
Lines: 68 68 100.0%
Functions: 29 30 96.7%
Branches: 14 19 73.7%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/json
8 //
9
10 #ifndef BOOST_JSON_STORAGE_PTR_HPP
11 #define BOOST_JSON_STORAGE_PTR_HPP
12
13 #include <boost/container/pmr/polymorphic_allocator.hpp>
14 #include <boost/json/detail/config.hpp>
15 #include <boost/json/detail/shared_resource.hpp>
16 #include <boost/json/detail/default_resource.hpp>
17 #include <boost/json/is_deallocate_trivial.hpp>
18 #include <new>
19 #include <type_traits>
20 #include <utility>
21
22 namespace boost {
23 namespace json {
24
25 /** A smart pointer to a memory resource.
26
27 This container is used to hold a pointer to a memory resource. The
28 pointed-to resource is always valid. Depending on the means of
29 construction, the ownership will be either:
30
31 @li Non-owning, when constructing from a raw pointer to
32 `boost::container::pmr::memory_resource` or from a
33 `boost::container::pmr::polymorphic_allocator`. In this case the caller is
34 responsible for ensuring that the lifetime of the memory resource extends
35 until there are no more calls to allocate or deallocate.
36
37 @li Owning, when constructing using the function
38 @ref make_shared_resource. In this case
39 ownership is shared; the lifetime of the memory
40 resource extends until the last copy of the
41 @ref storage_ptr is destroyed.
42
43 @par Examples
44
45 These statements create a memory resource on the
46 stack and construct a pointer from it without
47 taking ownership:
48 @code
49 monotonic_resource mr; // Create our memory resource on the stack
50 storage_ptr sp( &mr ); // Construct a non-owning pointer to the resource
51 @endcode
52
53 This function creates a pointer to a memory
54 resource using shared ownership and returns it.
55 The lifetime of the memory resource extends until
56 the last copy of the pointer is destroyed:
57 @code
58 // Create a counted memory resource and return it
59 storage_ptr make_storage()
60 {
61 return make_shared_resource< monotonic_resource >();
62 }
63 @endcode
64
65 @par Thread Safety
66
67 Instances of this type provide the default level of
68 thread safety for all C++ objects. Specifically, it
69 conforms to
70 <a href="http://eel.is/c++draft/res.on.data.races">
71 16.4.6.10 Data race avoidance</a>.
72
73 @see
74 @ref make_shared_resource,
75 @ref boost::container::pmr::polymorphic_allocator,
76 @ref boost::container::pmr::memory_resource.
77
78 */
79 class storage_ptr
80 {
81 #ifndef BOOST_JSON_DOCS
82 // VFALCO doc toolchain shows this when it shouldn't
83 friend struct detail::shared_resource;
84 #endif
85 using shared_resource =
86 detail::shared_resource;
87
88 using default_resource =
89 detail::default_resource;
90
91 std::uintptr_t i_;
92
93 shared_resource*
94 302 get_shared() const noexcept
95 {
96 return static_cast<shared_resource*>(
97 reinterpret_cast<container::pmr::memory_resource*>(
98 302 i_ & ~3));
99 }
100
101 void
102 6352922 addref() const noexcept
103 {
104
2/2
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 6352781 times.
6352922 if(is_shared())
105 141 get_shared()->refs.fetch_add(
106 1, std::memory_order_relaxed);
107 6352922 }
108
109 void
110 43991462 release() const noexcept
111 {
112
2/2
✓ Branch 1 taken 161 times.
✓ Branch 2 taken 43991301 times.
43991462 if(is_shared())
113 {
114 161 auto const p = get_shared();
115 161 if(p->refs.fetch_sub(1,
116
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 141 times.
161 std::memory_order_acq_rel) == 1)
117
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 delete p;
118 }
119 43991462 }
120
121 template<class T>
122 33 storage_ptr(
123 detail::shared_resource_impl<T>* p) noexcept
124 33 : i_(reinterpret_cast<std::uintptr_t>(
125 33 static_cast<container::pmr::memory_resource*>(p)) + 1 +
126 (json::is_deallocate_trivial<T>::value ? 2 : 0))
127 {
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
33 BOOST_ASSERT(p);
129 33 }
130
131 public:
132 /** Destructor
133
134 If the pointer has shared ownership of the
135 resource, the shared ownership is released.
136 If this is the last owned copy, the memory
137 resource is destroyed.
138
139 @par Complexity
140 Constant.
141
142 @par Exception Safety
143 No-throw guarantee.
144 */
145 41914812 ~storage_ptr() noexcept
146 {
147 41914812 release();
148 41914812 }
149
150 /** Constructor
151
152 This constructs a non-owning pointer that refers
153 to the [default memory resource].
154
155 @par Complexity
156 Constant.
157
158 @par Exception Safety
159 No-throw guarantee.
160
161 [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
162 */
163 14653596 storage_ptr() noexcept
164 14653596 : i_(0)
165 {
166 14653596 }
167
168 /** Constructor
169
170 This constructs a non-owning pointer that points to the memory resource
171 `r`. The caller is responsible for maintaining the lifetime of the
172 pointed-to `boost::container::pmr::memory_resource`.
173
174 @par Constraints
175 @code
176 std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
177 @endcode
178
179 @par Preconditions
180 @code
181 r != nullptr
182 @endcode
183
184 @par Exception Safety
185 No-throw guarantee.
186
187 @param r A pointer to the memory resource to use.
188 This may not be null.
189 */
190 template<class T
191 #ifndef BOOST_JSON_DOCS
192 , class = typename std::enable_if<
193 std::is_convertible<T*,
194 container::pmr::memory_resource*>::value>::type
195 #endif
196 >
197 86807 storage_ptr(T* r) noexcept
198 86807 : i_(reinterpret_cast<std::uintptr_t>(
199
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
27 static_cast<container::pmr::memory_resource *>(r)) +
200 (json::is_deallocate_trivial<T>::value ? 2 : 0))
201 {
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57222 times.
86807 BOOST_ASSERT(r);
203 86807 }
204
205 /** Constructor
206
207 This constructs a non-owning pointer that points to the same memory
208 resource as `alloc`, obtained by calling `alloc.resource()`. The caller
209 is responsible for maintaining the lifetime of the
210 pointed-to `boost::container::pmr::memory_resource`.
211
212 @par Constraints
213 @code
214 std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
215 @endcode
216
217 @par Exception Safety
218 No-throw guarantee.
219
220 @param alloc A `boost::container::pmr::polymorphic_allocator` to
221 construct from.
222 */
223 template<class T>
224 10 storage_ptr(
225 container::pmr::polymorphic_allocator<T> const& alloc) noexcept
226 10 : i_(reinterpret_cast<std::uintptr_t>(
227 10 alloc.resource()))
228 {
229 10 }
230
231 /** Move constructor
232
233 This function constructs a pointer that
234 points to the same memory resource as `other`,
235 with the same ownership:
236
237 @li If `other` is non-owning, then `*this`
238 will be be non-owning.
239
240 @li If `other` has shared ownership, then
241 ownership will be transferred to `*this`.
242
243 After construction, `other` will point
244 to the [default memory resource].
245
246 @par Complexity
247 Constant.
248
249 @par Exception Safety
250 No-throw guarantee.
251
252 @param other The pointer to construct from.
253
254 [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
255 */
256 22966351 storage_ptr(
257 storage_ptr&& other) noexcept
258 22966351 : i_(detail::exchange(other.i_, 0))
259 {
260 22966351 }
261
262 /** Copy constructor
263
264 This function constructs a pointer that
265 points to the same memory resource as `other`,
266 with the same ownership:
267
268 @li If `other` is non-owning, then `*this`
269 will be be non-owning.
270
271 @li If `other` has shared ownership, then
272 `*this` will acquire shared ownership.
273
274 @par Complexity
275 Constant.
276
277 @par Exception Safety
278 No-throw guarantee.
279
280 @param other The pointer to construct from.
281 */
282 6352921 storage_ptr(
283 storage_ptr const& other) noexcept
284 6352921 : i_(other.i_)
285 {
286 6352921 addref();
287 6352921 }
288
289 /** Move assignment
290
291 This function assigns a pointer that
292 points to the same memory resource as `other`,
293 with the same ownership:
294
295 @li If `other` is non-owning, then `*this`
296 will be be non-owning.
297
298 @li If `other` has shared ownership, then
299 ownership will be transferred to `*this`.
300
301 After assignment, `other` will point
302 to the [default memory resource].
303 If `*this` previously had shared ownership,
304 it is released before the function returns.
305
306 @par Complexity
307 Constant.
308
309 @par Exception Safety
310 No-throw guarantee.
311
312 @param other The storage pointer to move.
313
314 [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
315 */
316 storage_ptr&
317 2076649 operator=(
318 storage_ptr&& other) noexcept
319 {
320 2076649 release();
321 2076649 i_ = detail::exchange(other.i_, 0);
322 2076649 return *this;
323 }
324
325 /** Copy assignment.
326
327 This function assigns a pointer that
328 points to the same memory resource as `other`,
329 with the same ownership:
330
331 @li If `other` is non-owning, then `*this`
332 will be be non-owning.
333
334 @li If `other` has shared ownership, then
335 `*this` will acquire shared ownership.
336
337 If `*this` previously had shared ownership,
338 it is released before the function returns.
339
340 @par Complexity
341 Constant.
342
343 @par Exception Safety
344 No-throw guarantee.
345
346 @param other The storage pointer to copy.
347 */
348 storage_ptr&
349 1 operator=(
350 storage_ptr const& other) noexcept
351 {
352 1 other.addref();
353 1 release();
354 1 i_ = other.i_;
355 1 return *this;
356 }
357
358 /** Return `true` if ownership of the memory resource is shared.
359
360 This function returns true for memory resources
361 created using @ref make_shared_resource.
362 */
363 bool
364 50344385 is_shared() const noexcept
365 {
366 50344385 return (i_ & 1) != 0;
367 }
368
369 /** Return `true` if calling `deallocate` on the memory resource has no effect.
370
371 This function is used to determine if the deallocate
372 function of the pointed to memory resource is trivial.
373 The value of @ref is_deallocate_trivial is evaluated
374 and saved when the memory resource is constructed
375 and the type is known, before the type is erased.
376 */
377 bool
378 1 is_deallocate_trivial() const noexcept
379 {
380 1 return (i_ & 2) != 0;
381 }
382
383 /** Return `true` if ownership of the memory resource is not shared and deallocate is trivial.
384
385 This function is used to determine if calls to deallocate
386 can effectively be skipped.
387
388 @par Effects
389 Returns `! this->is_shared() && this->is_deallocate_trivial()`
390 */
391 bool
392 4323571 is_not_shared_and_deallocate_is_trivial() const noexcept
393 {
394 4323571 return (i_ & 3) == 2;
395 }
396
397 /** Return a pointer to the memory resource.
398
399 This function returns a pointer to the
400 referenced `boost::container::pmr::memory_resource`.
401
402 @par Complexity
403 Constant.
404
405 @par Exception Safety
406 No-throw guarantee.
407 */
408 container::pmr::memory_resource*
409 653617 get() const noexcept
410 {
411
2/2
✓ Branch 0 taken 122565 times.
✓ Branch 1 taken 531052 times.
653617 if(i_ != 0)
412 return reinterpret_cast<
413 122565 container::pmr::memory_resource*>(i_ & ~3);
414 531052 return default_resource::get();
415 }
416
417 /** Return a pointer to the memory resource.
418
419 This function returns a pointer to the
420 referenced `boost::container::pmr::memory_resource`.
421
422 @par Complexity
423 Constant.
424
425 @par Exception Safety
426 No-throw guarantee.
427 */
428 container::pmr::memory_resource*
429 649823 operator->() const noexcept
430 {
431 649823 return get();
432 }
433
434 /** Return a reference to the memory resource.
435
436 This function returns a reference to the
437 pointed-to `boost::container::pmr::memory_resource`.
438
439 @par Complexity
440
441 Constant.
442
443 @par Exception Safety
444
445 No-throw guarantee.
446 */
447 container::pmr::memory_resource&
448 3762 operator*() const noexcept
449 {
450 3762 return *get();
451 }
452
453 template<class U, class... Args>
454 friend
455 storage_ptr
456 make_shared_resource(Args&&... args);
457 };
458
459 #if defined(_MSC_VER)
460 # pragma warning( push )
461 # if !defined(__clang__) && _MSC_VER <= 1900
462 # pragma warning( disable : 4702 )
463 # endif
464 #endif
465 /** Return shared ownership of a new, dynamically allocated memory resource.
466
467 This function dynamically allocates a new memory resource
468 as if by `operator new` that uses shared ownership. The
469 lifetime of the memory resource will be extended until
470 the last @ref storage_ptr which points to it is destroyed.
471
472 @par Mandates
473 @code
474 std::is_base_of< boost::container::pmr::memory_resource, U >::value == true
475 @endcode
476
477 @par Complexity
478 Same as `new U( std::forward<Args>(args)... )`.
479
480 @par Exception Safety
481 Strong guarantee.
482
483 @tparam U The type of memory resource to create.
484
485 @param args Parameters forwarded to the constructor of `U`.
486 */
487 template<class U, class... Args>
488 storage_ptr
489 28 make_shared_resource(Args&&... args)
490 {
491 // If this generates an error, it means that
492 // `T` is not a memory resource.
493 BOOST_STATIC_ASSERT(
494 std::is_base_of<
495 container::pmr::memory_resource, U>::value);
496 31 return storage_ptr(new
497 detail::shared_resource_impl<U>(
498
2/3
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
29 std::forward<Args>(args)...));
499 }
500 #if defined(_MSC_VER)
501 # pragma warning( pop )
502 #endif
503
504 /** Return true if two storage pointers point to the same memory resource.
505
506 This function returns `true` if the
507 `boost::container::pmr::memory_resource` objects pointed to by `lhs` and
508 `rhs` have the same address.
509 */
510 inline
511 bool
512 5 operator==(
513 storage_ptr const& lhs,
514 storage_ptr const& rhs) noexcept
515 {
516 5 return lhs.get() == rhs.get();
517 }
518
519 /** Return true if two storage pointers point to different memory resources.
520
521 This function returns `true` if the
522 `boost::container::pmr::memory_resource` objects pointed to by `lhs` and
523 `rhs` have different addresses.
524 */
525 inline
526 bool
527 operator!=(
528 storage_ptr const& lhs,
529 storage_ptr const& rhs) noexcept
530 {
531 return lhs.get() != rhs.get();
532 }
533
534 } // namespace json
535 } // namespace boost
536
537 #endif
538