Coverage Report

Created: 2024-07-18 11:41

src/zserio/AnyHolder.h
Line
Count
Source
1
#ifndef ZSERIO_ANY_HOLDER_H_INC
2
#define ZSERIO_ANY_HOLDER_H_INC
3
4
#include <cstddef>
5
#include <type_traits>
6
7
#include "zserio/AllocatorHolder.h"
8
#include "zserio/CppRuntimeException.h"
9
#include "zserio/NoInit.h"
10
#include "zserio/OptionalHolder.h"
11
#include "zserio/RebindAlloc.h"
12
#include "zserio/Traits.h"
13
#include "zserio/Types.h"
14
15
namespace zserio
16
{
17
18
namespace detail
19
{
20
21
class TypeIdHolder
22
{
23
public:
24
    using type_id = const int*;
25
26
    template <typename T>
27
    static type_id get()
28
150k
    {
29
150k
        static int currentTypeId;
30
31
150k
        return &currentTypeId;
32
150k
    }
33
};
34
35
// Interface for object holders
36
template <typename ALLOC>
37
class IHolder
38
{
39
public:
40
66.3k
    virtual ~IHolder() = default;
41
    virtual IHolder* clone(const ALLOC& allocator) const = 0;
42
    virtual IHolder* clone(NoInitT, const ALLOC& allocator) const = 0;
43
    virtual IHolder* clone(void* storage) const = 0;
44
    virtual IHolder* clone(NoInitT, void* storage) const = 0;
45
    virtual IHolder* move(const ALLOC& allocator) = 0;
46
    virtual IHolder* move(NoInitT, const ALLOC& allocator) = 0;
47
    virtual IHolder* move(void* storage) = 0;
48
    virtual IHolder* move(NoInitT, void* storage) = 0;
49
    virtual void destroy(const ALLOC& allocator) = 0;
50
    virtual bool isType(detail::TypeIdHolder::type_id typeId) const = 0;
51
};
52
53
// Base of object holders, holds a value in the inplace_optional_holder
54
template <typename T, typename ALLOC>
55
class HolderBase : public IHolder<ALLOC>
56
{
57
public:
58
    template <typename U>
59
    void set(U&& value)
60
50.1k
    {
61
50.1k
        m_typedHolder = std::forward<U>(value);
62
50.1k
    }
63
64
    template <typename U, typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
65
    void set(NoInitT, U&& value)
66
4
    {
67
        // inplace_optional_holder constructor to prevent it's implicit constructor
68
4
        m_typedHolder.assign(NoInit, inplace_optional_holder<T>(NoInit, std::forward<U>(value)));
69
4
    }
70
71
    template <typename U, typename std::enable_if<!std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
72
    void set(NoInitT, U&& value)
73
14
    {
74
14
        m_typedHolder = std::forward<U>(value);
75
14
    }
76
77
    void setHolder(const inplace_optional_holder<T>& value)
78
29
    {
79
29
        m_typedHolder = value;
80
29
    }
81
82
    void setHolder(inplace_optional_holder<T>&& value)
83
16.2k
    {
84
16.2k
        m_typedHolder = std::move(value);
85
16.2k
    }
86
87
    template <typename U, typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
88
    void setHolder(NoInitT, const inplace_optional_holder<U>& value)
89
6
    {
90
6
        m_typedHolder.assign(NoInit, value);
91
6
    }
92
93
    template <typename U, typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
94
    void setHolder(NoInitT, inplace_optional_holder<U>&& value)
95
3
    {
96
3
        m_typedHolder.assign(NoInit, std::move(value));
97
3
    }
98
99
    template <typename U, typename std::enable_if<!std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
100
    void setHolder(NoInitT, const inplace_optional_holder<U>& value)
101
19
    {
102
19
        m_typedHolder = value;
103
19
    }
104
105
    template <typename U, typename std::enable_if<!std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
106
    void setHolder(NoInitT, inplace_optional_holder<U>&& value)
107
19
    {
108
19
        m_typedHolder = std::move(value);
109
19
    }
110
111
    T& get()
112
740
    {
113
740
        return m_typedHolder.value();
114
740
    }
115
116
    const T& get() const
117
48.7k
    {
118
48.7k
        return m_typedHolder.value();
119
48.7k
    }
120
121
    bool isType(detail::TypeIdHolder::type_id typeId) const override
122
75.3k
    {
123
75.3k
        return detail::TypeIdHolder::get<T>() == typeId;
124
75.3k
    }
125
126
protected:
127
    inplace_optional_holder<T>& getHolder()
128
16.3k
    {
129
16.3k
        return m_typedHolder;
130
16.3k
    }
131
132
    const inplace_optional_holder<T>& getHolder() const
133
54
    {
134
54
        return m_typedHolder;
135
54
    }
136
137
private:
138
    inplace_optional_holder<T> m_typedHolder;
139
};
140
141
// Holder allocated on heap
142
template <typename T, typename ALLOC>
143
class HeapHolder : public HolderBase<T, ALLOC>
144
{
145
private:
146
    struct ConstructTag
147
    {};
148
149
public:
150
    using this_type = HeapHolder<T, ALLOC>;
151
152
    explicit HeapHolder(ConstructTag) noexcept
153
16.6k
    {}
154
155
    static this_type* create(const ALLOC& allocator)
156
16.6k
    {
157
16.6k
        using AllocType = RebindAlloc<ALLOC, this_type>;
158
16.6k
        using AllocTraits = std::allocator_traits<AllocType>;
159
160
16.6k
        AllocType typedAlloc = allocator;
161
16.6k
        typename AllocTraits::pointer ptr = AllocTraits::allocate(typedAlloc, 1);
162
        // this never throws because HeapHolder constructor never throws
163
16.6k
        AllocTraits::construct(typedAlloc, std::addressof(*ptr), ConstructTag{});
164
16.6k
        return ptr;
165
16.6k
    }
166
167
    IHolder<ALLOC>* clone(const ALLOC& allocator) const override
168
12
    {
169
12
        this_type* holder = create(allocator);
170
12
        holder->setHolder(this->getHolder());
171
12
        return holder;
172
12
    }
173
174
    IHolder<ALLOC>* clone(NoInitT, const ALLOC& allocator) const override
175
12
    {
176
12
        this_type* holder = create(allocator);
177
12
        holder->setHolder(NoInit, this->getHolder());
178
12
        return holder;
179
12
    }
180
181
    IHolder<ALLOC>* clone(void*) const override
182
1
    {
183
1
        throw CppRuntimeException("AnyHolder: Unexpected clone call.");
184
1
    }
185
186
    IHolder<ALLOC>* clone(NoInitT, void*) const override
187
1
    {
188
1
        throw CppRuntimeException("AnyHolder: Unexpected clone call.");
189
1
    }
190
191
    IHolder<ALLOC>* move(const ALLOC& allocator) override
192
6
    {
193
6
        this_type* holder = create(allocator);
194
6
        holder->setHolder(std::move(this->getHolder()));
195
6
        return holder;
196
6
    }
197
198
    IHolder<ALLOC>* move(NoInitT, const ALLOC& allocator) override
199
6
    {
200
6
        this_type* holder = create(allocator);
201
6
        holder->setHolder(NoInit, std::move(this->getHolder()));
202
6
        return holder;
203
6
    }
204
205
    IHolder<ALLOC>* move(void*) override
206
1
    {
207
1
        throw CppRuntimeException("AnyHolder: Unexpected move call.");
208
1
    }
209
210
    IHolder<ALLOC>* move(NoInitT, void*) override
211
1
    {
212
1
        throw CppRuntimeException("AnyHolder: Unexpected move call.");
213
1
    }
214
215
    void destroy(const ALLOC& allocator) override
216
16.6k
    {
217
16.6k
        using AllocType = RebindAlloc<ALLOC, this_type>;
218
16.6k
        using AllocTraits = std::allocator_traits<AllocType>;
219
220
16.6k
        AllocType typedAlloc = allocator;
221
16.6k
        AllocTraits::destroy(typedAlloc, this);
222
16.6k
        AllocTraits::deallocate(typedAlloc, this, 1);
223
16.6k
    }
224
};
225
226
// Holder allocated in the in-place storage
227
template <typename T, typename ALLOC>
228
class NonHeapHolder : public HolderBase<T, ALLOC>
229
{
230
public:
231
    using this_type = NonHeapHolder<T, ALLOC>;
232
233
    static this_type* create(void* storage)
234
33.3k
    {
235
33.3k
        return new (storage) this_type();
236
33.3k
    }
237
238
    IHolder<ALLOC>* clone(const ALLOC&) const override
239
1
    {
240
1
        throw CppRuntimeException("AnyHolder: Unexpected clone call.");
241
1
    }
242
243
    IHolder<ALLOC>* clone(NoInitT, const ALLOC&) const override
244
1
    {
245
1
        throw CppRuntimeException("AnyHolder: Unexpected clone call.");
246
1
    }
247
248
    IHolder<ALLOC>* clone(void* storage) const override
249
17
    {
250
17
        NonHeapHolder* holder = new (storage) NonHeapHolder();
251
17
        holder->setHolder(this->getHolder());
252
17
        return holder;
253
17
    }
254
255
    IHolder<ALLOC>* clone(NoInitT, void* storage) const override
256
13
    {
257
13
        NonHeapHolder* holder = new (storage) NonHeapHolder();
258
13
        holder->setHolder(NoInit, this->getHolder());
259
13
        return holder;
260
13
    }
261
262
    IHolder<ALLOC>* move(const ALLOC&) override
263
1
    {
264
1
        throw CppRuntimeException("AnyHolder: Unexpected move call.");
265
1
    }
266
267
    IHolder<ALLOC>* move(NoInitT, const ALLOC&) override
268
1
    {
269
1
        throw CppRuntimeException("AnyHolder: Unexpected move call.");
270
1
    }
271
272
    IHolder<ALLOC>* move(void* storage) override
273
16.2k
    {
274
16.2k
        NonHeapHolder* holder = new (storage) NonHeapHolder();
275
16.2k
        holder->setHolder(std::move(this->getHolder()));
276
16.2k
        return holder;
277
16.2k
    }
278
279
    IHolder<ALLOC>* move(NoInitT, void* storage) override
280
16
    {
281
16
        NonHeapHolder* holder = new (storage) NonHeapHolder();
282
16
        holder->setHolder(NoInit, std::move(this->getHolder()));
283
16
        return holder;
284
16
    }
285
286
    void destroy(const ALLOC&) override
287
49.6k
    {
288
49.6k
        this->~NonHeapHolder();
289
49.6k
    }
290
291
private:
292
49.6k
    NonHeapHolder() = default;
293
};
294
295
template <typename ALLOC>
296
union UntypedHolder
297
{
298
    // 2 * sizeof(void*) for T + sizeof(void*) for Holder's vptr
299
    using MaxInPlaceType = std::aligned_storage<3 * sizeof(void*), alignof(void*)>::type;
300
301
    detail::IHolder<ALLOC>* heap;
302
    MaxInPlaceType inPlace;
303
};
304
305
template <typename T, typename ALLOC>
306
using has_non_heap_holder = std::integral_constant<bool,
307
        sizeof(NonHeapHolder<T, ALLOC>) <= sizeof(typename UntypedHolder<ALLOC>::MaxInPlaceType) &&
308
                std::is_nothrow_move_constructible<T>::value &&
309
                alignof(T) <= alignof(typename UntypedHolder<ALLOC>::MaxInPlaceType)>;
310
311
} // namespace detail
312
313
/**
314
 * Type safe container for single values of any type which doesn't need RTTI.
315
 */
316
template <typename ALLOC = std::allocator<uint8_t>>
317
class AnyHolder : public AllocatorHolder<ALLOC>
318
{
319
    using AllocTraits = std::allocator_traits<ALLOC>;
320
    using AllocatorHolder<ALLOC>::get_allocator_ref;
321
    using AllocatorHolder<ALLOC>::set_allocator;
322
323
public:
324
    using AllocatorHolder<ALLOC>::get_allocator;
325
    using allocator_type = ALLOC;
326
327
    /**
328
     * Empty constructor.
329
     */
330
    AnyHolder() :
331
            AnyHolder(ALLOC())
332
375
    {}
333
334
    /**
335
     * Constructor from given allocator
336
     */
337
    explicit AnyHolder(const ALLOC& allocator) :
338
            AllocatorHolder<ALLOC>(allocator)
339
950
    {
340
950
        m_untypedHolder.heap = nullptr;
341
950
    }
342
343
    /**
344
     * Constructor from any value.
345
     *
346
     * \param value Value of any type to hold. Supports move semantic.
347
     */
348
    template <typename T,
349
            typename std::enable_if<!std::is_same<typename std::decay<T>::type, AnyHolder>::value &&
350
                            !std::is_same<typename std::decay<T>::type, ALLOC>::value,
351
                    int>::type = 0>
352
    explicit AnyHolder(T&& value, const ALLOC& allocator = ALLOC()) :
353
            AllocatorHolder<ALLOC>(allocator)
354
25.1k
    {
355
25.1k
        m_untypedHolder.heap = nullptr;
356
25.1k
        set(std::forward<T>(value));
357
25.1k
    }
358
359
    /**
360
     * Constructor from any value which prevents initialization.
361
     *
362
     * \param value Value of any type to hold. Supports move semantic.
363
     */
364
    template <typename T,
365
            typename std::enable_if<!std::is_same<typename std::decay<T>::type, AnyHolder>::value, int>::type =
366
                    0>
367
    explicit AnyHolder(NoInitT, T&& value, const ALLOC& allocator = ALLOC()) :
368
            AllocatorHolder<ALLOC>(allocator)
369
18
    {
370
18
        m_untypedHolder.heap = nullptr;
371
18
        set(NoInit, std::forward<T>(value));
372
18
    }
373
374
    /**
375
     * Destructor.
376
     */
377
    ~AnyHolder()
378
26.3k
    {
379
26.3k
        clearHolder();
380
26.3k
    }
381
382
    /**
383
     * Copy constructor.
384
     *
385
     * \param other Any holder to copy.
386
     */
387
    AnyHolder(const AnyHolder& other) :
388
            AllocatorHolder<ALLOC>(
389
                    AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
390
29
    {
391
29
        copy(other);
392
29
    }
393
394
    /**
395
     * Copy constructor which prevents initialization.
396
     *
397
     * \param other Any holder to copy.
398
     */
399
    AnyHolder(NoInitT, const AnyHolder& other) :
400
            AllocatorHolder<ALLOC>(
401
                    AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
402
24
    {
403
24
        copy(NoInit, other);
404
24
    }
405
406
    /**
407
     * Allocator-extended copy constructor.
408
     *
409
     * \param other Any holder to copy.
410
     * \param allocator Allocator to be used for dynamic memory allocations.
411
     */
412
    AnyHolder(const AnyHolder& other, const ALLOC& allocator) :
413
            AllocatorHolder<ALLOC>(allocator)
414
8
    {
415
8
        copy(other);
416
8
    }
417
418
    /**
419
     * Allocator-extended copy constructor which prevents initialization.
420
     *
421
     * \param other Any holder to copy.
422
     * \param allocator Allocator to be used for dynamic memory allocations.
423
     */
424
    AnyHolder(NoInitT, const AnyHolder& other, const ALLOC& allocator) :
425
            AllocatorHolder<ALLOC>(allocator)
426
8
    {
427
8
        copy(NoInit, other);
428
8
    }
429
430
    /**
431
     * Copy assignment operator.
432
     *
433
     * \param other Any holder to copy.
434
     *
435
     * \return Reference to this.
436
     */
437
    AnyHolder& operator=(const AnyHolder& other)
438
24
    {
439
24
        if (this != &other)
440
16
        {
441
            // TODO: do not dealloc unless necessary
442
16
            clearHolder();
443
16
            if (AllocTraits::propagate_on_container_copy_assignment::value)
444
8
            {
445
8
                set_allocator(other.get_allocator_ref());
446
8
            }
447
16
            copy(other);
448
16
        }
449
450
24
        return *this;
451
24
    }
452
453
    /**
454
     * Copy assignment operator which prevents initialization.
455
     *
456
     * \param other Any holder to copy.
457
     *
458
     * \return Reference to this.
459
     */
460
    AnyHolder& assign(NoInitT, const AnyHolder& other)
461
37
    {
462
37
        if (this != &other)
463
29
        {
464
            // TODO: do not dealloc unless necessary
465
29
            clearHolder();
466
29
            if (AllocTraits::propagate_on_container_copy_assignment::value)
467
8
            {
468
8
                set_allocator(other.get_allocator_ref());
469
8
            }
470
29
            copy(NoInit, other);
471
29
        }
472
473
37
        return *this;
474
37
    }
475
476
    /**
477
     * Move constructor.
478
     *
479
     * \param other Any holder to move from.
480
     */
481
    AnyHolder(AnyHolder&& other) noexcept :
482
            AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref()))
483
45
    {
484
45
        move(std::move(other));
485
45
    }
486
487
    /**
488
     * Move constructor which prevents initialization.
489
     *
490
     * \param other Any holder to move from.
491
     */
492
    AnyHolder(NoInitT, AnyHolder&& other) noexcept :
493
            AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref()))
494
29
    {
495
29
        move(NoInit, std::move(other));
496
29
    }
497
498
    /**
499
     * Allocator-extended move constructor.
500
     *
501
     * \param other Any holder to move from.
502
     * \param allocator Allocator to be used for dynamic memory allocations.
503
     */
504
    AnyHolder(AnyHolder&& other, const ALLOC& allocator) :
505
            AllocatorHolder<ALLOC>(allocator)
506
24
    {
507
24
        move(std::move(other));
508
24
    }
509
510
    /**
511
     * Allocator-extended move constructor which prevents initialization.
512
     *
513
     * \param other Any holder to move from.
514
     * \param allocator Allocator to be used for dynamic memory allocations.
515
     */
516
    AnyHolder(NoInitT, AnyHolder&& other, const ALLOC& allocator) :
517
            AllocatorHolder<ALLOC>(allocator)
518
24
    {
519
24
        move(NoInit, std::move(other));
520
24
    }
521
522
    /**
523
     * Move assignment operator.
524
     *
525
     * \param other Any holder to move from.
526
     *
527
     * \return Reference to this.
528
     */
529
    AnyHolder& operator=(AnyHolder&& other)
530
48.9k
    {
531
48.9k
        if (this != &other)
532
48.9k
        {
533
48.9k
            clearHolder();
534
48.9k
            if (AllocTraits::propagate_on_container_move_assignment::value)
535
48.9k
            {
536
48.9k
                set_allocator(std::move(other.get_allocator_ref()));
537
48.9k
            }
538
48.9k
            move(std::move(other));
539
48.9k
        }
540
541
48.9k
        return *this;
542
48.9k
    }
543
544
    /**
545
     * Move assignment operator which prevents initialization.
546
     *
547
     * \param other Any holder to move from.
548
     *
549
     * \return Reference to this.
550
     */
551
    AnyHolder& assign(NoInitT, AnyHolder&& other)
552
27
    {
553
27
        if (this != &other)
554
19
        {
555
19
            clearHolder();
556
19
            if (AllocTraits::propagate_on_container_move_assignment::value)
557
11
            {
558
11
                set_allocator(std::move(other.get_allocator_ref()));
559
11
            }
560
19
            move(NoInit, std::move(other));
561
19
        }
562
563
27
        return *this;
564
27
    }
565
566
    /**
567
     * Value assignment operator.
568
     *
569
     * \param value Any value to assign. Supports move semantic.
570
     *
571
     * \return Reference to this.
572
     */
573
    template <typename T,
574
            typename std::enable_if<!std::is_same<typename std::decay<T>::type, AnyHolder>::value, int>::type =
575
                    0>
576
    AnyHolder& operator=(T&& value)
577
72
    {
578
72
        set(std::forward<T>(value));
579
580
72
        return *this;
581
72
    }
582
583
    /**
584
     * Resets the holder.
585
     */
586
    void reset()
587
68
    {
588
68
        clearHolder();
589
68
    }
590
591
    /**
592
     * Sets any value to the holder.
593
     *
594
     * \param value Any value to set. Supports move semantic.
595
     */
596
    template <typename T>
597
    void set(T&& value)
598
50.1k
    {
599
50.1k
        createHolder<typename std::decay<T>::type>()->set(std::forward<T>(value));
600
50.1k
    }
601
602
    /**
603
     * Sets any value to the holder and prevent initialization.
604
     *
605
     * \param value Any value to set. Supports move semantic.
606
     */
607
    template <typename T>
608
    void set(NoInitT, T&& value)
609
18
    {
610
18
        createHolder<typename std::decay<T>::type>()->set(NoInit, std::forward<T>(value));
611
18
    }
612
613
    /**
614
     * Gets value of the given type.
615
     *
616
     * \return Reference to value of the requested type if the type match to the stored value.
617
     *
618
     * \throw CppRuntimeException if the requested type doesn't match to the stored value.
619
     */
620
    template <typename T>
621
    T& get()
622
772
    {
623
772
        checkType<T>();
624
772
        return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
625
772
    }
626
627
    /**
628
     * Gets value of the given type.
629
     *
630
     * \return Value of the requested type if the type match to the stored value.
631
     *
632
     * \throw CppRuntimeException if the requested type doesn't match to the stored value.
633
     */
634
    template <typename T>
635
    const T& get() const
636
48.7k
    {
637
48.7k
        checkType<T>();
638
48.7k
        return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
639
48.7k
    }
640
641
    /**
642
     * Check whether the holder holds the given type.
643
     *
644
     * \return True if the stored value is of the given type, false otherwise.
645
     */
646
    template <typename T>
647
    bool isType() const
648
50.6k
    {
649
50.6k
        return hasHolder() && 
getUntypedHolder()->isType(detail::TypeIdHolder::get<T>())50.6k
;
650
50.6k
    }
651
652
    /**
653
     * Checks whether the holder has any value.
654
     *
655
     * \return True if the holder has assigned any value, false otherwise.
656
     */
657
    bool hasValue() const
658
24.7k
    {
659
24.7k
        return hasHolder();
660
24.7k
    }
661
662
private:
663
    void copy(const AnyHolder& other)
664
53
    {
665
53
        if (other.m_isInPlace)
666
17
        {
667
17
            other.getUntypedHolder()->clone(&m_untypedHolder.inPlace);
668
17
            m_isInPlace = true;
669
17
        }
670
36
        else if (other.m_untypedHolder.heap != nullptr)
671
12
        {
672
12
            m_untypedHolder.heap = other.getUntypedHolder()->clone(get_allocator_ref());
673
12
        }
674
24
        else
675
24
        {
676
24
            m_untypedHolder.heap = nullptr;
677
24
        }
678
53
    }
679
680
    void copy(NoInitT, const AnyHolder& other)
681
61
    {
682
61
        if (other.m_isInPlace)
683
13
        {
684
13
            other.getUntypedHolder()->clone(NoInit, &m_untypedHolder.inPlace);
685
13
            m_isInPlace = true;
686
13
        }
687
48
        else if (other.m_untypedHolder.heap != nullptr)
688
12
        {
689
12
            m_untypedHolder.heap = other.getUntypedHolder()->clone(NoInit, get_allocator_ref());
690
12
        }
691
36
        else
692
36
        {
693
36
            m_untypedHolder.heap = nullptr;
694
36
        }
695
61
    }
696
697
    void move(AnyHolder&& other)
698
49.0k
    {
699
49.0k
        if (other.m_isInPlace)
700
16.2k
        {
701
16.2k
            other.getUntypedHolder()->move(&m_untypedHolder.inPlace);
702
16.2k
            m_isInPlace = true;
703
16.2k
            other.clearHolder();
704
16.2k
        }
705
32.7k
        else if (other.m_untypedHolder.heap != nullptr)
706
32.6k
        {
707
32.6k
            if (get_allocator_ref() == other.get_allocator_ref())
708
32.6k
            {
709
                // take over the other's storage
710
32.6k
                m_untypedHolder.heap = other.m_untypedHolder.heap;
711
32.6k
                other.m_untypedHolder.heap = nullptr;
712
32.6k
            }
713
6
            else
714
6
            {
715
                // cannot steal the storage, allocate our own and move the holder
716
6
                m_untypedHolder.heap = other.getUntypedHolder()->move(get_allocator_ref());
717
6
                other.clearHolder();
718
6
            }
719
32.6k
        }
720
36
        else
721
36
        {
722
36
            m_untypedHolder.heap = nullptr;
723
36
        }
724
49.0k
    }
725
726
    void move(NoInitT, AnyHolder&& other)
727
72
    {
728
72
        if (other.m_isInPlace)
729
16
        {
730
16
            other.getUntypedHolder()->move(NoInit, &m_untypedHolder.inPlace);
731
16
            m_isInPlace = true;
732
16
            other.clearHolder();
733
16
        }
734
56
        else if (other.m_untypedHolder.heap != nullptr)
735
17
        {
736
17
            if (get_allocator_ref() == other.get_allocator_ref())
737
11
            {
738
                // take over the other's storage
739
11
                m_untypedHolder.heap = other.m_untypedHolder.heap;
740
11
                other.m_untypedHolder.heap = nullptr;
741
11
            }
742
6
            else
743
6
            {
744
                // cannot steal the storage, allocate our own and move the holder
745
6
                m_untypedHolder.heap = other.getUntypedHolder()->move(NoInit, get_allocator_ref());
746
6
                other.clearHolder();
747
6
            }
748
17
        }
749
39
        else
750
39
        {
751
39
            m_untypedHolder.heap = nullptr;
752
39
        }
753
72
    }
754
755
    void clearHolder()
756
116k
    {
757
116k
        if (hasHolder())
758
66.3k
        {
759
66.3k
            getUntypedHolder()->destroy(get_allocator_ref());
760
66.3k
            m_isInPlace = false;
761
66.3k
            m_untypedHolder.heap = nullptr;
762
66.3k
        }
763
116k
    }
764
765
    bool hasHolder() const
766
241k
    {
767
241k
        return (m_isInPlace || 
m_untypedHolder.heap != nullptr142k
);
768
241k
    }
769
770
    template <typename T>
771
    detail::HolderBase<T, ALLOC>* createHolder()
772
50.1k
    {
773
50.1k
        if (hasHolder())
774
24.6k
        {
775
24.6k
            if (getUntypedHolder()->isType(detail::TypeIdHolder::get<T>()))
776
211
            {
777
211
                return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>());
778
211
            }
779
780
24.4k
            clearHolder();
781
24.4k
        }
782
783
49.9k
        return createHolderImpl<T>(detail::has_non_heap_holder<T, ALLOC>());
784
50.1k
    }
785
786
    template <typename T>
787
    detail::HolderBase<T, ALLOC>* createHolderImpl(std::true_type)
788
33.3k
    {
789
33.3k
        detail::NonHeapHolder<T, ALLOC>* holder =
790
33.3k
                detail::NonHeapHolder<T, ALLOC>::create(&m_untypedHolder.inPlace);
791
33.3k
        m_isInPlace = true;
792
33.3k
        return holder;
793
33.3k
    }
794
795
    template <typename T>
796
    detail::HolderBase<T, ALLOC>* createHolderImpl(std::false_type)
797
16.6k
    {
798
16.6k
        detail::HeapHolder<T, ALLOC>* holder = detail::HeapHolder<T, ALLOC>::create(get_allocator_ref());
799
16.6k
        m_untypedHolder.heap = holder;
800
16.6k
        return holder;
801
16.6k
    }
802
803
    template <typename T>
804
    void checkType() const
805
49.4k
    {
806
49.4k
        if (!isType<T>())
807
33
        {
808
33
            throwBadType();
809
33
        }
810
49.4k
    }
811
812
    /** Optimization which increases changes to inline checkType(). */
813
    void throwBadType() const
814
33
    {
815
33
        throw CppRuntimeException("Bad type in AnyHolder");
816
33
    }
817
818
    template <typename T>
819
    detail::HeapHolder<T, ALLOC>* getHeapHolder()
820
168
    {
821
168
        return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap);
822
168
    }
823
824
    template <typename T>
825
    const detail::HeapHolder<T, ALLOC>* getHeapHolder() const
826
16.4k
    {
827
16.4k
        return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap);
828
16.4k
    }
829
830
    template <typename T>
831
    detail::NonHeapHolder<T, ALLOC>* getInplaceHolder()
832
783
    {
833
783
        return reinterpret_cast<detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace);
834
783
    }
835
836
    template <typename T>
837
    const detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() const
838
32.2k
    {
839
32.2k
        return reinterpret_cast<const detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace);
840
32.2k
    }
841
842
    template <typename T>
843
    detail::HolderBase<T, ALLOC>* getHolder(std::true_type)
844
783
    {
845
783
        return static_cast<detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>());
846
783
    }
847
848
    template <typename T>
849
    detail::HolderBase<T, ALLOC>* getHolder(std::false_type)
850
168
    {
851
168
        return static_cast<detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>());
852
168
    }
853
854
    template <typename T>
855
    const detail::HolderBase<T, ALLOC>* getHolder(std::true_type) const
856
32.2k
    {
857
32.2k
        return static_cast<const detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>());
858
32.2k
    }
859
860
    template <typename T>
861
    const detail::HolderBase<T, ALLOC>* getHolder(std::false_type) const
862
16.4k
    {
863
16.4k
        return static_cast<const detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>());
864
16.4k
    }
865
866
    detail::IHolder<ALLOC>* getUntypedHolder()
867
107k
    {
868
107k
        return (m_isInPlace)
869
107k
                ? 
reinterpret_cast<detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace)74.3k
870
107k
                : 
m_untypedHolder.heap32.9k
;
871
107k
    }
872
873
    const detail::IHolder<ALLOC>* getUntypedHolder() const
874
50.7k
    {
875
50.7k
        return (m_isInPlace)
876
50.7k
                ? 
reinterpret_cast<const detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace)33.4k
877
50.7k
                : 
m_untypedHolder.heap17.3k
;
878
50.7k
    }
879
880
    detail::UntypedHolder<ALLOC> m_untypedHolder;
881
    bool m_isInPlace = false;
882
};
883
884
} // namespace zserio
885
886
#endif // ifndef ZSERIO_ANY_HOLDER_H_INC