Coverage Report

Created: 2025-09-05 06:51

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