Coverage Report

Created: 2025-06-13 06:26

/src/boost/boost/json/storage_ptr.hpp
Line
Count
Source (jump to first uncovered line)
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
    get_shared() const noexcept
95
0
    {
96
0
        return static_cast<shared_resource*>(
97
0
            reinterpret_cast<container::pmr::memory_resource*>(
98
0
                i_ & ~3));
99
0
    }
100
101
    void
102
    addref() const noexcept
103
14.0M
    {
104
14.0M
        if(is_shared())
105
0
            get_shared()->refs.fetch_add(
106
0
                1, std::memory_order_relaxed);
107
14.0M
    }
108
109
    void
110
    release() const noexcept
111
37.6M
    {
112
37.6M
        if(is_shared())
113
0
        {
114
0
            auto const p = get_shared();
115
0
            if(p->refs.fetch_sub(1,
116
0
                    std::memory_order_acq_rel) == 1)
117
0
                delete p;
118
0
        }
119
37.6M
    }
120
121
    template<class T>
122
    storage_ptr(
123
        detail::shared_resource_impl<T>* p) noexcept
124
        : i_(reinterpret_cast<std::uintptr_t>(
125
                static_cast<container::pmr::memory_resource*>(p)) + 1 +
126
            (json::is_deallocate_trivial<T>::value ? 2 : 0))
127
    {
128
        BOOST_ASSERT(p);
129
    }
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
    ~storage_ptr() noexcept
146
37.6M
    {
147
37.6M
        release();
148
37.6M
    }
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
    storage_ptr() noexcept
164
        : i_(0)
165
    {
166
    }
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
    storage_ptr(T* r) noexcept
198
        : i_(reinterpret_cast<std::uintptr_t>(
199
                static_cast<container::pmr::memory_resource *>(r)) +
200
            (json::is_deallocate_trivial<T>::value ? 2 : 0))
201
    {
202
        BOOST_ASSERT(r);
203
    }
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
    storage_ptr(
225
        container::pmr::polymorphic_allocator<T> const& alloc) noexcept
226
        : i_(reinterpret_cast<std::uintptr_t>(
227
            alloc.resource()))
228
    {
229
    }
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
    storage_ptr(
257
        storage_ptr&& other) noexcept
258
29.3M
        : i_(detail::exchange(other.i_, 0))
259
29.3M
    {
260
29.3M
    }
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
    storage_ptr(
283
        storage_ptr const& other) noexcept
284
14.0M
        : i_(other.i_)
285
14.0M
    {
286
14.0M
        addref();
287
14.0M
    }
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
    operator=(
318
        storage_ptr&& other) noexcept
319
9.48k
    {
320
9.48k
        release();
321
9.48k
        i_ = detail::exchange(other.i_, 0);
322
9.48k
        return *this;
323
9.48k
    }
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
    operator=(
350
        storage_ptr const& other) noexcept
351
    {
352
        other.addref();
353
        release();
354
        i_ = other.i_;
355
        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
    is_shared() const noexcept
365
51.6M
    {
366
51.6M
        return (i_ & 1) != 0;
367
51.6M
    }
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
    is_deallocate_trivial() const noexcept
379
    {
380
        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
    is_not_shared_and_deallocate_is_trivial() const noexcept
393
1.37M
    {
394
1.37M
        return (i_ & 3) == 2;
395
1.37M
    }
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
    get() const noexcept
410
3.15M
    {
411
3.15M
        if(i_ != 0)
412
59.7k
            return reinterpret_cast<
413
59.7k
                container::pmr::memory_resource*>(i_ & ~3);
414
3.09M
        return default_resource::get();
415
3.15M
    }
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
    operator->() const noexcept
430
3.15M
    {
431
3.15M
        return get();
432
3.15M
    }
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
    operator*() const noexcept
449
0
    {
450
0
        return *get();
451
0
    }
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
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
    return storage_ptr(new
497
        detail::shared_resource_impl<U>(
498
            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
operator==(
513
    storage_ptr const& lhs,
514
    storage_ptr const& rhs) noexcept
515
{
516
    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