GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: storage_ptr.hpp
Date: 2026-01-06 06:25:57
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/core/detail/static_assert.hpp>
14 #include <boost/container/pmr/polymorphic_allocator.hpp>
15 #include <boost/json/detail/config.hpp>
16 #include <boost/json/detail/shared_resource.hpp>
17 #include <boost/json/detail/default_resource.hpp>
18 #include <boost/json/is_deallocate_trivial.hpp>
19 #include <new>
20 #include <type_traits>
21 #include <utility>
22
23 namespace boost {
24 namespace json {
25
26 /** A smart pointer to a memory resource.
27
28 This class is used to hold a pointer to a memory resource. The pointed-to
29 resource is always valid. Depending on the means of construction, the
30 ownership will be either:
31
32 @li Non-owning, when constructing from a raw pointer to
33 @ref boost::container::pmr::memory_resource or from a
34 @ref boost::container::pmr::polymorphic_allocator. In this case the caller
35 is responsible for ensuring that the lifetime of the memory resource
36 extends until there are no more calls to allocate or deallocate.
37
38 @li Owning, when constructing using the function @ref make_shared_resource.
39 In this case ownership is shared; the lifetime of the memory resource
40 extends until the last copy of the `storage_ptr` is destroyed.
41
42 @par Examples
43 These statements create a memory resource on the stack and construct
44 a pointer from it without taking ownership:
45
46 @code
47 monotonic_resource mr; // Create our memory resource on the stack
48 storage_ptr sp( &mr ); // Construct a non-owning pointer to the resource
49 @endcode
50
51 This function creates a pointer to a memory resource using shared ownership
52 and returns it. The lifetime of the memory resource extends until the last
53 copy of the pointer is destroyed:
54
55 @code
56 // Create a counted memory resource and return it
57 storage_ptr make_storage()
58 {
59 return make_shared_resource< monotonic_resource >();
60 }
61 @endcode
62
63 @par Thread Safety
64 Instances of this type provide the default level of thread safety for all
65 C++ objects. Specifically, it conforms to
66 [16.4.6.10 Data race avoidance](http://eel.is/c++draft/res.on.data.races).
67
68 @see
69 @ref make_shared_resource,
70 @ref boost::container::pmr::polymorphic_allocator,
71 @ref boost::container::pmr::memory_resource.
72
73 */
74 class storage_ptr
75 {
76 #ifndef BOOST_JSON_DOCS
77 // VFALCO doc toolchain shows this when it shouldn't
78 friend struct detail::shared_resource;
79 #endif
80 using shared_resource =
81 detail::shared_resource;
82
83 using default_resource =
84 detail::default_resource;
85
86 std::uintptr_t i_;
87
88 shared_resource*
89 302 get_shared() const noexcept
90 {
91 return static_cast<shared_resource*>(
92 reinterpret_cast<container::pmr::memory_resource*>(
93 302 i_ & ~3));
94 }
95
96 void
97 6353124 addref() const noexcept
98 {
99
2/2
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 6352983 times.
6353124 if(is_shared())
100 141 get_shared()->refs.fetch_add(
101 1, std::memory_order_relaxed);
102 6353124 }
103
104 void
105 43991884 release() const noexcept
106 {
107
2/2
✓ Branch 1 taken 161 times.
✓ Branch 2 taken 43991723 times.
43991884 if(is_shared())
108 {
109 161 auto const p = get_shared();
110 161 if(p->refs.fetch_sub(1,
111
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 141 times.
161 std::memory_order_acq_rel) == 1)
112
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 delete p;
113 }
114 43991884 }
115
116 template<class T>
117 33 storage_ptr(
118 detail::shared_resource_impl<T>* p) noexcept
119 33 : i_(reinterpret_cast<std::uintptr_t>(
120 33 static_cast<container::pmr::memory_resource*>(p)) + 1 +
121 (json::is_deallocate_trivial<T>::value ? 2 : 0))
122 {
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
33 BOOST_ASSERT(p);
124 33 }
125
126 public:
127 /** Destructor.
128
129 If the pointer has shared ownership of the resource, the shared
130 ownership is released. If this is the last owned copy, the memory
131 resource is destroyed.
132
133 @par Complexity
134 Constant.
135
136 @par Exception Safety
137 No-throw guarantee.
138 */
139 41915221 ~storage_ptr() noexcept
140 {
141 41915221 release();
142 41915221 }
143
144 /** Constructors.
145
146 @li **(1)** constructs a non-owning pointer that refers to the
147 \<\<default_memory_resource,default memory resource\>\>.
148
149 @li **(2)** constructs a non-owning pointer that points to the memory
150 resource `r`.
151
152 @li **(3)** constructs a non-owning pointer that points to the same
153 memory resource as `alloc`, obtained by calling `alloc.resource()`.
154
155 @li **(4)**, **(5)** construct a pointer to the same memory resource as
156 `other`, with the same ownership.
157
158 After **(4)** and **(5)** if `other` was owning, then the constructed
159 pointer is also owning. In particular, **(4)** transfers ownership to
160 the constructed pointer while **(5)** causes it to share ownership with
161 `other`. Otherwise, and with other overloads the constructed pointer
162 doesn't own its memory resource and the caller is responsible for
163 maintaining the lifetime of the pointed-to
164 @ref boost::container::pmr::memory_resource.
165
166 After **(4)**, `other` will point to the default memory resource.
167
168 @par Constraints
169 @code
170 std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
171 @endcode
172
173 @pre
174 @code
175 r != nullptr
176 @endcode
177
178 @par Complexity
179 Constant.
180
181 @par Exception Safety
182 No-throw guarantee.
183
184 @{
185 */
186 14653628 storage_ptr() noexcept
187 14653628 : i_(0)
188 {
189 14653628 }
190
191 /** Overload
192
193 @tparam T The type of memory resource.
194 @param r A non-null pointer to the memory resource to use.
195 */
196 template<class T
197 #ifndef BOOST_JSON_DOCS
198 , class = typename std::enable_if<
199 std::is_convertible<T*,
200 container::pmr::memory_resource*>::value>::type
201 #endif
202 >
203 86807 storage_ptr(T* r) noexcept
204 86807 : i_(reinterpret_cast<std::uintptr_t>(
205
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
27 static_cast<container::pmr::memory_resource *>(r)) +
206 (json::is_deallocate_trivial<T>::value ? 2 : 0))
207 {
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57222 times.
86807 BOOST_ASSERT(r);
209 86807 }
210
211 /** Overload
212
213 @tparam V Any type.
214 @param alloc A @ref boost::container::pmr::polymorphic_allocator to
215 construct from.
216 */
217 template<class V>
218 10 storage_ptr(
219 container::pmr::polymorphic_allocator<V> const& alloc) noexcept
220 10 : i_(reinterpret_cast<std::uintptr_t>(
221 10 alloc.resource()))
222 {
223 10 }
224
225 /** Overload
226
227 @param other Another pointer.
228 */
229 22966567 storage_ptr(
230 storage_ptr&& other) noexcept
231 22966567 : i_(detail::exchange(other.i_, 0))
232 {
233 22966567 }
234
235 /** Overload
236
237 @param other
238 */
239 6353123 storage_ptr(
240 storage_ptr const& other) noexcept
241 6353123 : i_(other.i_)
242 {
243 6353123 addref();
244 6353123 }
245 /// @}
246
247 /** Assignment operators.
248
249 This function assigns a pointer that points to the same memory resource
250 as `other`, with the same ownership:
251
252 @li If `other` is non-owning, then the assigned-to pointer will be be
253 non-owning.
254
255 @li If `other` has shared ownership, then **(1)** transfers ownership
256 to the assigned-to pointer, while after **(2)** it shares the ownership
257 with `other`.
258
259 If the assigned-to pointer previously had shared ownership, it is
260 released before the function returns.
261
262 After **(1)**, `other` will point to the
263 \<\<default_memory_resource,default memory resource\>\>.
264
265 @par Complexity
266 Constant.
267
268 @par Exception Safety
269 No-throw guarantee.
270
271 @param other Another pointer.
272
273 @{
274 */
275 storage_ptr&
276 2076662 operator=(
277 storage_ptr&& other) noexcept
278 {
279 2076662 release();
280 2076662 i_ = detail::exchange(other.i_, 0);
281 2076662 return *this;
282 }
283
284 storage_ptr&
285 1 operator=(
286 storage_ptr const& other) noexcept
287 {
288 1 other.addref();
289 1 release();
290 1 i_ = other.i_;
291 1 return *this;
292 }
293 /// @}
294
295 /** Check if ownership of the memory resource is shared.
296
297 This function returns true for memory resources created using @ref
298 make_shared_resource.
299 */
300 bool
301 50345009 is_shared() const noexcept
302 {
303 50345009 return (i_ & 1) != 0;
304 }
305
306 /** Check if calling `deallocate` on the memory resource has no effect.
307
308 This function is used to determine if the deallocate function of the
309 pointed to memory resource is trivial. The value of @ref
310 is_deallocate_trivial is evaluated and saved when the memory resource
311 is constructed and the type is known, before the type is erased.
312 */
313 bool
314 1 is_deallocate_trivial() const noexcept
315 {
316 1 return (i_ & 2) != 0;
317 }
318
319 /** Check if ownership of the memory resource is not shared and deallocate is trivial.
320
321 This function is used to determine if calls to deallocate can
322 effectively be skipped. Equivalent to `! is_shared() &&
323 is_deallocate_trivial()`.
324 */
325 bool
326 4324295 is_not_shared_and_deallocate_is_trivial() const noexcept
327 {
328 4324295 return (i_ & 3) == 2;
329 }
330
331 /** Return a pointer to the memory resource.
332
333 This function returns a pointer to the
334 referenced @ref boost::container::pmr::memory_resource.
335
336 @par Complexity
337 Constant.
338
339 @par Exception Safety
340 No-throw guarantee.
341 */
342 container::pmr::memory_resource*
343 654203 get() const noexcept
344 {
345
2/2
✓ Branch 0 taken 122565 times.
✓ Branch 1 taken 531638 times.
654203 if(i_ != 0)
346 return reinterpret_cast<
347 122565 container::pmr::memory_resource*>(i_ & ~3);
348 531638 return default_resource::get();
349 }
350
351 /** Return a pointer to the memory resource.
352
353 This function returns a pointer to the referenced @ref
354 boost::container::pmr::memory_resource.
355
356 @par Complexity
357 Constant.
358
359 @par Exception Safety
360 No-throw guarantee.
361 */
362 container::pmr::memory_resource*
363 650715 operator->() const noexcept
364 {
365 650715 return get();
366 }
367
368 /** Return a reference to the memory resource.
369
370 This function returns a reference to the pointed-to @ref
371 boost::container::pmr::memory_resource.
372
373 @par Complexity
374
375 Constant.
376
377 @par Exception Safety
378
379 No-throw guarantee.
380 */
381 container::pmr::memory_resource&
382 3456 operator*() const noexcept
383 {
384 3456 return *get();
385 }
386
387 template<class U, class... Args>
388 friend
389 storage_ptr
390 make_shared_resource(Args&&... args);
391 };
392
393 #if defined(_MSC_VER)
394 # pragma warning( push )
395 # if !defined(__clang__) && _MSC_VER <= 1900
396 # pragma warning( disable : 4702 )
397 # endif
398 #endif
399
400 /** Return a pointer that owns a new, dynamically allocated memory resource.
401
402 This function dynamically allocates a new memory resource as if by
403 `operator new` that uses shared ownership. The lifetime of the memory
404 resource will be extended until the last @ref storage_ptr which points to
405 it is destroyed.
406
407 @par Constraints
408 @code
409 std::is_base_of< boost::container::pmr::memory_resource, U >::value == true
410 @endcode
411
412 @par Complexity
413 Same as `new U( std::forward<Args>(args)... )`.
414
415 @par Exception Safety
416 Strong guarantee.
417
418 @tparam U The type of memory resource to create.
419
420 @param args Parameters forwarded to the constructor of `U`.
421 */
422 template<class U, class... Args>
423 storage_ptr
424 28 make_shared_resource(Args&&... args)
425 {
426 // If this generates an error, it means that
427 // `T` is not a memory resource.
428 BOOST_CORE_STATIC_ASSERT((
429 std::is_base_of<container::pmr::memory_resource, U>::value));
430 31 return storage_ptr(new
431 detail::shared_resource_impl<U>(
432
2/3
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
29 std::forward<Args>(args)...));
433 }
434 #if defined(_MSC_VER)
435 # pragma warning( pop )
436 #endif
437
438 /// Overload
439 inline
440 bool
441 5 operator==(
442 storage_ptr const& lhs,
443 storage_ptr const& rhs) noexcept
444 {
445 5 return lhs.get() == rhs.get();
446 }
447
448 /// Overload
449 inline
450 bool
451 operator!=(
452 storage_ptr const& lhs,
453 storage_ptr const& rhs) noexcept
454 {
455 return lhs.get() != rhs.get();
456 }
457
458 } // namespace json
459 } // namespace boost
460
461 #endif
462