GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/zserio/ZserioTreeCreator.h Lines: 272 272 100.0 %
Date: 2023-12-13 14:51:09 Branches: 762 3928 19.4 %

Line Branch Exec Source
1
#ifndef ZSERIO_ZSERIO_TREE_CREATOR_H_INC
2
#define ZSERIO_ZSERIO_TREE_CREATOR_H_INC
3
4
#include <limits>
5
#include <type_traits>
6
#include <cerrno>
7
#include <cstdlib>
8
9
#include "zserio/BitBuffer.h"
10
#include "zserio/CppRuntimeException.h"
11
#include "zserio/IReflectable.h"
12
#include "zserio/ITypeInfo.h"
13
#include "zserio/StringView.h"
14
#include "zserio/TypeInfoUtil.h"
15
#include "zserio/Traits.h"
16
#include "zserio/Vector.h"
17
#include "zserio/Types.h"
18
19
namespace zserio
20
{
21
22
namespace detail
23
{
24
25
template <typename T, typename ALLOC>
26
AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>& typeInfo, T&& value, const ALLOC& allocator);
27
28
template <typename T, typename U,
29
        typename std::enable_if<std::is_unsigned<typename std::decay<U>::type>::value, int>::type = 0>
30
53
bool checkArithmeticValueRanges(U value)
31
{
32
    // value is unsigned
33
53
    return (value <= static_cast<U>(std::numeric_limits<T>::max()));
34
}
35
36
template <typename T, typename U,
37
        typename std::enable_if<std::is_signed<typename std::decay<U>::type>::value &&
38
                std::is_signed<typename std::decay<T>::type>::value, int>::type = 0>
39
17
bool checkArithmeticValueRanges(U value)
40
{
41
    // value is signed and it is converted to signed value
42
17
    return (static_cast<int64_t>(value) >= static_cast<int64_t>(std::numeric_limits<T>::min()) &&
43












18
            static_cast<int64_t>(value) <= static_cast<int64_t>(std::numeric_limits<T>::max()));
44
}
45
46
template <typename T, typename U,
47
        typename std::enable_if<std::is_signed<typename std::decay<U>::type>::value &&
48
                std::is_unsigned<typename std::decay<T>::type>::value, int>::type = 0>
49
18
bool checkArithmeticValueRanges(U value)
50
{
51
    // value is signed and it is converted to unsigned value
52












18
    return (value >= 0 && static_cast<uint64_t>(value) <= static_cast<uint64_t>(std::numeric_limits<T>::max()));
53
}
54
55
template <typename T, typename ALLOC>
56
3
AnyHolder<ALLOC> makeAnyBoolValue(bool value, const ALLOC& allocator)
57
{
58

3
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
59
}
60
61
template <typename T, typename U, typename ALLOC>
62
4
AnyHolder<ALLOC> makeAnyBoolValue(const U& value, const ALLOC&)
63
{
64


































8
    throw CppRuntimeException("ZserioTreeCreator: Value '") << value <<
65
8
            "' cannot be converted to bool value!";
66
}
67
68
template <typename T, typename ALLOC>
69
8
AnyHolder<ALLOC> makeAnyIntegralValue(bool value, const ALLOC&)
70
{
71
























16
    throw CppRuntimeException("ZserioTreeCreator: Bool value '") << value <<
72
16
            "' cannot be converted to integral type!";
73
}
74
75
template <typename T, typename U, typename ALLOC,
76
        typename std::enable_if<std::is_integral<typename std::decay<U>::type>::value, int>::type = 0>
77
88
AnyHolder<ALLOC> makeAnyIntegralValue(U value, const ALLOC& allocator)
78
{
79
    // check ranges of integers
80




















88
    if (!checkArithmeticValueRanges<T>(value))
81
    {
82
















































































45
        throw CppRuntimeException("ZserioTreeCreator: Integral value '") << value << "' overflow (<" <<
83




























































45
                std::numeric_limits<T>::min() << ", " << std::numeric_limits<T>::max() << ">)!";
84
    }
85
86




















73
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
87
}
88
89
template <typename T, typename U, typename ALLOC,
90
        typename std::enable_if<!std::is_integral<typename std::decay<U>::type>::value, int>::type = 0>
91
27
AnyHolder<ALLOC> makeAnyIntegralValue(const U& value, const ALLOC&)
92
{
93
























































































































































































































54
    throw CppRuntimeException("ZserioTreeCreator: Value '") << value <<
94
54
            "' cannot be converted to integral value!";
95
}
96
97
template <typename T, typename ALLOC>
98
2
AnyHolder<ALLOC> makeAnyFloatingValue(bool value, const ALLOC&)
99
{
100






4
    throw CppRuntimeException("ZserioTreeCreator: Bool value '") << value <<
101
4
            "' cannot be converted to floating type!";
102
}
103
104
template <typename T, typename U, typename ALLOC,
105
        typename std::enable_if<std::is_arithmetic<typename std::decay<U>::type>::value, int>::type = 0>
106
6
AnyHolder<ALLOC> makeAnyFloatingValue(U value, const ALLOC& allocator)
107
{
108
    // allow conversion integers to floats
109







6
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
110
}
111
112
template <typename T, typename U, typename ALLOC,
113
        typename std::enable_if<!std::is_arithmetic<typename std::decay<U>::type>::value, int>::type = 0>
114
4
AnyHolder<ALLOC> makeAnyFloatingValue(const U& value, const ALLOC&)
115
{
116
















































8
    throw CppRuntimeException("ZserioTreeCreator: Value '") << value <<
117
8
            "' cannot be converted to floating value!";
118
}
119
120
template <typename ALLOC>
121
2
AnyHolder<ALLOC> makeAnyStringValue(const string<ALLOC>& value, const ALLOC& allocator)
122
{
123
2
    return AnyHolder<ALLOC>(value, allocator);
124
}
125
126
template <typename ALLOC>
127
2
AnyHolder<ALLOC> makeAnyStringValue(string<ALLOC>&& value, const ALLOC& allocator)
128
{
129
2
    return AnyHolder<ALLOC>(std::move(value), allocator);
130
}
131
132
template <typename ALLOC>
133
62
AnyHolder<ALLOC> makeAnyStringValue(StringView value, const ALLOC& allocator)
134
{
135

62
    return AnyHolder<ALLOC>(toString(value, allocator), allocator);
136
}
137
138
template <typename ALLOC>
139
7
AnyHolder<ALLOC> makeAnyStringValue(const char* value, const ALLOC& allocator)
140
{
141
7
    return makeAnyStringValue(StringView(value), allocator);
142
}
143
144
template <typename T, typename ALLOC>
145
5
AnyHolder<ALLOC> makeAnyStringValue(const T&, const ALLOC&)
146
{
147






5
    throw CppRuntimeException("ZserioTreeCreator: Trying to make any string value from unsupported type!");
148
}
149
150
template <typename ALLOC>
151
11
AnyHolder<ALLOC> parseEnumStringValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
152
        const ALLOC& allocator)
153
{
154

30
    for (const auto& itemInfo : typeInfo.getEnumItems())
155
    {
156

26
        if (itemInfo.schemaName == stringValue)
157
        {
158

7
            if (TypeInfoUtil::isSigned(typeInfo.getUnderlyingType().getCppType()))
159
            {
160

10
                return makeAnyValue(typeInfo.getUnderlyingType(), static_cast<int64_t>(itemInfo.value),
161

10
                        allocator);
162
            }
163
            else
164
            {
165
2
                return makeAnyValue(typeInfo.getUnderlyingType(), itemInfo.value, allocator);
166
            }
167
        }
168
    }
169
170
4
    return AnyHolder<ALLOC>(allocator);
171
}
172
173
template <typename ALLOC>
174
24
AnyHolder<ALLOC> makeAnyEnumValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
175
        const ALLOC& allocator)
176
{
177

24
    if (!stringValue.empty())
178
    {
179
22
        const char firstChar = stringValue[0];
180





22
        if ((firstChar >= 'A' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'z') ||
181
                firstChar == '_')
182
        {
183

15
            AnyHolder<ALLOC> anyValue = parseEnumStringValue(stringValue, typeInfo, allocator);
184

11
            if (anyValue.hasValue())
185

14
                return anyValue;
186
        }
187
        // else it's a no match
188
    }
189
190




34
    throw CppRuntimeException("ZserioTreeCreator: Cannot create enum '") << typeInfo.getSchemaName() <<
191


34
            "' from string value '" << stringValue << "'!";
192
}
193
194
template <typename ALLOC>
195
2
AnyHolder<ALLOC> makeAnyEnumValue(const string<ALLOC>& stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
196
        const ALLOC& allocator)
197
{
198
2
    return makeAnyEnumValue(StringView(stringValue), typeInfo, allocator);
199
}
200
201
template <typename ALLOC>
202
3
AnyHolder<ALLOC> makeAnyEnumValue(const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
203
        const ALLOC& allocator)
204
{
205
3
    return makeAnyEnumValue(StringView(stringValue), typeInfo, allocator);
206
}
207
208
template <typename T, typename ALLOC,
209
        typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
210
3
AnyHolder<ALLOC> makeAnyEnumValue(T enumValue, const IBasicTypeInfo<ALLOC>&, const ALLOC& allocator)
211
{
212
3
    return AnyHolder<ALLOC>(enumValue, allocator);
213
}
214
215
template <typename T, typename ALLOC,
216
        typename std::enable_if<!std::is_enum<T>::value, int>::type = 0>
217
8
AnyHolder<ALLOC> makeAnyEnumValue(T enumRawValue, const IBasicTypeInfo<ALLOC>& typeInfo,
218
        const ALLOC& allocator)
219
{
220
8
    return makeAnyValue(typeInfo.getUnderlyingType(), enumRawValue, allocator);
221
}
222
223
template <typename ALLOC>
224
26
AnyHolder<ALLOC> parseBitmaskStringValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
225
        const ALLOC& allocator)
226
{
227
26
    uint64_t value = 0;
228
26
    size_t pos = 0;
229

62
    while (pos < stringValue.size())
230
    {
231
38
        bool match = false;
232
38
        const size_t available = stringValue.size() - pos;
233


73
        for (const auto& itemInfo : typeInfo.getBitmaskValues())
234
        {
235



127
            if (available >= itemInfo.schemaName.size() &&
236

63
                    stringValue.substr(pos, itemInfo.schemaName.size()) == itemInfo.schemaName)
237
            {
238
44
                const size_t newPos = pos + itemInfo.schemaName.size();
239
                // check that the identifier really ends here
240




44
                if (newPos == stringValue.size() || stringValue[newPos] == ' ' || stringValue[newPos] == '|' )
241
                {
242
29
                    value |= itemInfo.value;
243

29
                    if (newPos == stringValue.size())
244


11
                        return makeAnyValue(typeInfo.getUnderlyingType(), value, allocator); // end of string
245
18
                    match = true;
246
18
                    pos += itemInfo.schemaName.size();
247
18
                    break;
248
                }
249
            }
250
        }
251
252

27
        if (!match)
253
9
            break;
254
255



48
        while (pos < stringValue.size() && stringValue[pos] == ' ')
256
15
            ++pos;
257
258



18
        if (pos < stringValue.size() && stringValue[pos] == '|')
259
15
            ++pos;
260
261



40
        while (pos < stringValue.size() && stringValue[pos] == ' ')
262
11
            ++pos;
263
    }
264
265
    // invalid format or identifier
266
15
    return AnyHolder<ALLOC>(allocator);
267
}
268
269
template <typename ALLOC>
270
10
AnyHolder<ALLOC> parseBitmaskNumericStringValue(const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
271
        const ALLOC& allocator)
272
{
273
10
    char *pEnd = nullptr;
274
10
    errno = 0;
275
10
    uint64_t value = std::strtoull(stringValue, &pEnd, 10);
276

10
    if (errno == ERANGE)
277
1
        return AnyHolder<ALLOC>(allocator);
278


9
    return makeAnyValue(typeInfo.getUnderlyingType(), value, allocator);
279
}
280
281
template <typename ALLOC>
282
32
AnyHolder<ALLOC> makeAnyBitmaskValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
283
        const ALLOC& allocator)
284
{
285

32
    if (!stringValue.empty())
286
    {
287
30
        const char firstChar = stringValue[0];
288





30
        if ((firstChar >= 'A' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'z') ||
289
                firstChar == '_')
290
        {
291

25
            AnyHolder<ALLOC> anyValue = parseBitmaskStringValue(stringValue, typeInfo, allocator);
292

15
            if (anyValue.hasValue())
293

15
                return anyValue;
294
        }
295


15
        else if (firstChar >= '0' && firstChar <= '9') // bitmask can be only unsigned
296
        {
297
            // ensure zero-terminated string
298

11
            const string<ALLOC> numericStringValue = toString(stringValue, allocator);
299
            AnyHolder<ALLOC> anyValue = parseBitmaskNumericStringValue(numericStringValue.c_str(), typeInfo,
300


11
                    allocator);
301

10
            if (anyValue.hasValue())
302

9
                return anyValue;
303
        }
304
    }
305
306




36
    throw CppRuntimeException("ZserioTreeCreator: Cannot create bitmask '") << typeInfo.getSchemaName() <<
307


36
            "' from string value '" << stringValue << "'!";
308
}
309
310
template <typename ALLOC>
311
2
AnyHolder<ALLOC> makeAnyBitmaskValue(const string<ALLOC>& stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
312
        const ALLOC& allocator)
313
{
314
2
    return makeAnyBitmaskValue(StringView(stringValue), typeInfo, allocator);
315
}
316
317
template <typename ALLOC>
318
1
AnyHolder<ALLOC> makeAnyBitmaskValue(const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
319
        const ALLOC& allocator)
320
{
321
1
    return makeAnyBitmaskValue(StringView(stringValue), typeInfo, allocator);
322
}
323
324
template <typename T, typename ALLOC,
325
        typename std::enable_if<is_bitmask<T>::value, int>::type = 0>
326
3
AnyHolder<ALLOC> makeAnyBitmaskValue(T bitmaskValue, const IBasicTypeInfo<ALLOC>&, const ALLOC& allocator)
327
{
328
3
    return AnyHolder<ALLOC>(bitmaskValue, allocator);
329
}
330
331
template <typename T, typename ALLOC,
332
        typename std::enable_if<!is_bitmask<T>::value, int>::type = 0>
333
7
AnyHolder<ALLOC> makeAnyBitmaskValue(T bitmaskRawValue, const IBasicTypeInfo<ALLOC>& typeInfo,
334
        const ALLOC& allocator)
335
{
336
7
    return makeAnyValue(typeInfo.getUnderlyingType(), bitmaskRawValue, allocator);
337
}
338
339
template <typename T, typename ALLOC>
340
295
AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>& typeInfo, T&& value, const ALLOC& allocator)
341
{
342














































































































































295
    switch (typeInfo.getCppType())
343
    {
344
    case CppType::BOOL:
345
7
        return makeAnyBoolValue<bool>(std::forward<T>(value), allocator);
346
    case CppType::UINT8:
347
37
        return makeAnyIntegralValue<uint8_t>(std::forward<T>(value), allocator);
348
    case CppType::UINT16:
349
7
        return makeAnyIntegralValue<uint16_t>(std::forward<T>(value), allocator);
350
    case CppType::UINT32:
351
34
        return makeAnyIntegralValue<uint32_t>(std::forward<T>(value), allocator);
352
    case CppType::UINT64:
353
6
        return makeAnyIntegralValue<uint64_t>(std::forward<T>(value), allocator);
354
    case CppType::INT8:
355
19
        return makeAnyIntegralValue<int8_t>(std::forward<T>(value), allocator);
356
    case CppType::INT16:
357
7
        return makeAnyIntegralValue<int16_t>(std::forward<T>(value), allocator);
358
    case CppType::INT32:
359
7
        return makeAnyIntegralValue<int32_t>(std::forward<T>(value), allocator);
360
    case CppType::INT64:
361
6
        return makeAnyIntegralValue<int64_t>(std::forward<T>(value), allocator);
362
    case CppType::FLOAT:
363
6
        return makeAnyFloatingValue<float>(std::forward<T>(value), allocator);
364
    case CppType::DOUBLE:
365
6
        return makeAnyFloatingValue<double>(std::forward<T>(value), allocator);
366
    case CppType::STRING:
367
71
        return makeAnyStringValue(std::forward<T>(value), allocator);
368
    case CppType::ENUM:
369


35
        return makeAnyEnumValue(std::forward<T>(value), typeInfo, allocator);
370
    case CppType::BITMASK:
371


42
        return makeAnyBitmaskValue(std::forward<T>(value), typeInfo, allocator);
372
    default:
373
5
        return AnyHolder<ALLOC>(std::forward<T>(value), allocator);
374
    }
375
}
376
377
// overload for values which are already in AnyHolder
378
template <typename ALLOC>
379
17
AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>&, AnyHolder<ALLOC>&& anyValue, const ALLOC&)
380
{
381
17
    return std::move(anyValue);
382
}
383
384
enum class CreatorState : uint8_t
385
{
386
    BEFORE_ROOT,
387
    IN_COMPOUND,
388
    IN_ARRAY
389
};
390
391
} // namespace detail
392
393
/**
394
 * Allows to append detail::CreatorState to CppRuntimeException.
395
 *
396
 * \param exception Exception to modify.
397
 * \param state Creator state to append.
398
 *
399
 * \return Reference to the exception to allow operator chaining.
400
 */
401
CppRuntimeException& operator<<(CppRuntimeException& exception, detail::CreatorState state);
402
403
/**
404
 * Allows to build zserio object tree defined by the given type info.
405
 */
406
template <typename ALLOC>
407
235
class BasicZserioTreeCreator : AllocatorHolder<ALLOC>
408
{
409
public:
410
    /**
411
     * Constructor.
412
     *
413
     * \param typeInfo Type info defining the tree.
414
     */
415
    explicit BasicZserioTreeCreator(const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator = ALLOC());
416
417
    /**
418
     * Creates the top level compound element and move to state of building its children.
419
     */
420
    void beginRoot();
421
422
    /**
423
     * Finishes building and returns the created tree.
424
     *
425
     * \return Zserio object tree.
426
     *
427
     * \throw CppRuntimeException When the creator is not in state of building the root object.
428
     */
429
    IBasicReflectablePtr<ALLOC> endRoot();
430
431
    /**
432
     * Creates an array field within the current compound.
433
     *
434
     * \param name Name of the array field.
435
     *
436
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
437
     */
438
    void beginArray(const string<ALLOC>& name);
439
440
    /**
441
     * Finishes the array field.
442
     *
443
     * \throw CppRuntimeException When the creator is not in an array field.
444
     */
445
    void endArray();
446
447
    /**
448
     * Creates a compound field within the current compound.
449
     *
450
     * \param name Name of the compound field.
451
     *
452
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
453
     */
454
    void beginCompound(const string<ALLOC>& name);
455
456
    /**
457
     * Finishes the compound.
458
     *
459
     * \throw CppRuntimeException When the creator is not in a compound.
460
     */
461
    void endCompound();
462
463
    /**
464
     * Sets field value within the current compound.
465
     *
466
     * \param name Name of the field.
467
     * \param value Value to set.
468
     *
469
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
470
     */
471
    template <typename T>
472
    void setValue(const string<ALLOC>& name, T&& value);
473
474
    /**
475
     * Overload for setting of a null value.
476
     *
477
     * Note that this does nothing in C++ since non-optional fields are always present (default initialized).
478
     *
479
     * \param name Name of the field.
480
     * \param nullValue Null value.
481
     *
482
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
483
     */
484
    void setValue(const string<ALLOC>& name, std::nullptr_t nullValue);
485
486
    /**
487
     * Gets type info of the expected field.
488
     *
489
     * \param name Field name.
490
     *
491
     * \return Type info of the expected field.
492
     *
493
     * \throw CppRuntimeException When the creator is not in a compound.
494
     */
495
    const IBasicTypeInfo<ALLOC>& getFieldType(const string<ALLOC>& name) const;
496
497
    /**
498
     * Creates compound array element within the current array.
499
     *
500
     * \throw CppRuntimeException When the creator is not in an array of compounds.
501
     */
502
    void beginCompoundElement();
503
504
    /**
505
     * Finishes the compound element.
506
     *
507
     * \throw CppRuntimeException When the creator is not in a compound element.
508
     */
509
    void endCompoundElement();
510
511
    /**
512
     * Adds the value to the array.
513
     *
514
     * \param value Value to add.
515
     *
516
     * \throw CppRuntimeException When the creator is not in an array of simple values.
517
     */
518
    template <typename T>
519
    void addValueElement(T&& value);
520
521
    /**
522
     * Gets type info of the expected array element.
523
     *
524
     * \return Type info of the expected array element.
525
     *
526
     * \throw CppRuntimeException When the creator is not in an array.
527
     */
528
    const IBasicTypeInfo<ALLOC>& getElementType() const;
529
530
private:
531
    using AllocatorHolder<ALLOC>::get_allocator;
532
533
    const IBasicTypeInfo<ALLOC>& getTypeInfo() const;
534
    const BasicFieldInfo<ALLOC>& findFieldInfo(const IBasicTypeInfo<ALLOC>& typeInfo, StringView name) const;
535
536
    template <typename T>
537
    AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>& typeInfo, T&& value) const;
538
539
    const IBasicTypeInfo<ALLOC>& m_typeInfo;
540
    vector<std::reference_wrapper<const BasicFieldInfo<ALLOC>>, ALLOC> m_fieldInfoStack;
541
    vector<IBasicReflectablePtr<ALLOC>, ALLOC> m_valueStack;
542
    detail::CreatorState m_state = detail::CreatorState::BEFORE_ROOT;
543
};
544
545
/** Typedef provided for convenience - using default std::allocator<uint8_t>. */
546
using ZserioTreeCreator = BasicZserioTreeCreator<std::allocator<uint8_t>>;
547
548
template <typename ALLOC>
549
85
BasicZserioTreeCreator<ALLOC>::BasicZserioTreeCreator(const IBasicTypeInfo<ALLOC>& typeInfo,
550
        const ALLOC& allocator) :
551
        AllocatorHolder<ALLOC>(allocator),
552
85
        m_typeInfo(typeInfo), m_fieldInfoStack(allocator), m_valueStack(allocator)
553
85
{}
554
555
template <typename ALLOC>
556
87
void BasicZserioTreeCreator<ALLOC>::beginRoot()
557
{
558

87
    if (m_state != detail::CreatorState::BEFORE_ROOT)
559



5
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin root in state '") << m_state << "'!";
560
561


82
    m_valueStack.push_back(m_typeInfo.createInstance(get_allocator()));
562
82
    m_state = detail::CreatorState::IN_COMPOUND;
563
82
}
564
565
template <typename ALLOC>
566
54
IBasicReflectablePtr<ALLOC> BasicZserioTreeCreator<ALLOC>::endRoot()
567
{
568



54
    if (m_state != detail::CreatorState::IN_COMPOUND || m_valueStack.size() != 1)
569



5
        throw CppRuntimeException("ZserioTreeCreator: Cannot end root in state '") << m_state << "'!";
570
571
49
    m_state = detail::CreatorState::BEFORE_ROOT;
572
49
    auto value = m_valueStack.back();
573
49
    m_valueStack.pop_back();
574
49
    return value;
575
}
576
577
template <typename ALLOC>
578
23
void BasicZserioTreeCreator<ALLOC>::beginArray(const string<ALLOC>& name)
579
{
580

23
    if (m_state != detail::CreatorState::IN_COMPOUND)
581



3
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin array in state '") << m_state << "'!";
582
583
20
    const auto& parentTypeInfo = getTypeInfo();
584

23
    const auto& fieldInfo = findFieldInfo(parentTypeInfo, name);
585

17
    if (!fieldInfo.isArray)
586
    {
587



4
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName <<
588
4
                 "' is not an array!";
589
    }
590
591

15
    m_fieldInfoStack.push_back(fieldInfo);
592
593
    // note that we cannot just call getField() in case that the array is not optional like we do it in
594
    // setValue() and beginCompound() methods because in case of arrays we would join multiple arrays together
595
    // when this method is called multiple times with the same name - thus we will just create a new array
596
    //
597
    // moreover we need to properly initialize arrays of dynamic bit fields
598
    // see https://github.com/ndsev/zserio/issues/414
599


15
    m_valueStack.push_back(m_valueStack.back()->createField(name));
600
601
15
    m_state = detail::CreatorState::IN_ARRAY;
602
15
}
603
604
template <typename ALLOC>
605
16
void BasicZserioTreeCreator<ALLOC>::endArray()
606
{
607

16
    if (m_state != detail::CreatorState::IN_ARRAY)
608



4
        throw CppRuntimeException("ZserioTreeCreator: Cannot end array in state '") << m_state << "'!";
609
610
12
    m_fieldInfoStack.pop_back();
611
12
    m_valueStack.pop_back();
612
12
    m_state = detail::CreatorState::IN_COMPOUND;
613
12
}
614
615
template <typename ALLOC>
616
44
void BasicZserioTreeCreator<ALLOC>::beginCompound(const string<ALLOC>& name)
617
{
618

44
    if (m_state != detail::CreatorState::IN_COMPOUND)
619



3
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin compound in state '") << m_state << "'!";
620
621
41
    const auto& parentTypeInfo = getTypeInfo();
622

44
    const auto& fieldInfo = findFieldInfo(parentTypeInfo, name);
623

38
    if (fieldInfo.isArray)
624



1
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName << "' is an array!";
625
626

37
    if (!TypeInfoUtil::isCompound(fieldInfo.typeInfo.getCppType()))
627
    {
628



2
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName <<
629
2
                "' is not a compound!";
630
    }
631
632

36
    m_fieldInfoStack.push_back(fieldInfo);
633



36
    if (TypeInfoUtil::hasChoice(parentTypeInfo.getCppType()) || fieldInfo.isOptional)
634
    {
635
        // optional field, or field within choice or union -> create the new compound
636


1
        m_valueStack.push_back(m_valueStack.back()->createField(name));
637
    }
638
    else
639
    {
640


35
        m_valueStack.push_back(m_valueStack.back()->getField(name));
641
    }
642
643
36
    m_state = detail::CreatorState::IN_COMPOUND;
644
36
}
645
646
template <typename ALLOC>
647
17
void BasicZserioTreeCreator<ALLOC>::endCompound()
648
{
649



17
    if (m_state != detail::CreatorState::IN_COMPOUND || m_fieldInfoStack.empty())
650
    {
651



12
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound in state '") << m_state <<
652


12
                "'" << (m_fieldInfoStack.empty() ? ", expecting endRoot!" : "!'");
653
    }
654
655
13
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
656

13
    if (fieldInfo.isArray)
657

1
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound, it's an array element!");
658
659
12
    m_fieldInfoStack.pop_back();
660
12
    m_valueStack.pop_back();
661
12
}
662
663
template <typename ALLOC>
664
template <typename T>
665
136
void BasicZserioTreeCreator<ALLOC>::setValue(const string<ALLOC>& name, T&& value)
666
{
667






136
    if (m_state != detail::CreatorState::IN_COMPOUND)
668


















3
        throw CppRuntimeException("ZserioTreeCreator: Cannot set value in state '") << m_state << "'!";
669
670






137
    const BasicFieldInfo<ALLOC>& fieldInfo = findFieldInfo(getTypeInfo(), name);
671






129
    if (fieldInfo.isArray)
672
    {
673












2
        throw CppRuntimeException("ZserioTreeCreator: Expecting array in member '") <<
674






2
                fieldInfo.schemaName << "'!";
675
    }
676
677






256
    m_valueStack.back()->setField(fieldInfo.schemaName,
678
128
            makeAnyValue(fieldInfo.typeInfo, std::forward<T>(value)));
679
113
}
680
681
template <typename ALLOC>
682
3
void BasicZserioTreeCreator<ALLOC>::setValue(const string<ALLOC>& name, std::nullptr_t nullValue)
683
{
684

3
    if (m_state != detail::CreatorState::IN_COMPOUND)
685
    {
686



2
        throw CppRuntimeException("ZserioTreeCreator: Cannot set value (null) in state '") << m_state <<
687
2
                "'!";
688
    }
689
690

2
    const BasicFieldInfo<ALLOC>& fieldInfo = findFieldInfo(getTypeInfo(), name);
691

2
    if (fieldInfo.isOptional)
692
    {
693
        // reset an optional field
694


1
        m_valueStack.back()->setField(fieldInfo.schemaName, AnyHolder<ALLOC>(nullValue, get_allocator()));
695
    }
696
    else
697
    {
698
        // reset non-optional field with default-constructed value
699
        // (classes generated in C++ do not support null values)
700
1
        m_valueStack.back()->createField(fieldInfo.schemaName);
701
    }
702
2
}
703
704
template <typename ALLOC>
705
59
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getFieldType(const string<ALLOC>& name) const
706
{
707

59
    if (m_state != detail::CreatorState::IN_COMPOUND)
708



1
        throw CppRuntimeException("ZserioTreeCreator: Cannot get field type in state '") << m_state << "'!";
709
710

58
    return findFieldInfo(getTypeInfo(), name).typeInfo;
711
}
712
713
template <typename ALLOC>
714
9
void BasicZserioTreeCreator<ALLOC>::beginCompoundElement()
715
{
716

9
    if (m_state != detail::CreatorState::IN_ARRAY)
717
    {
718


8
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin compound element in state '") <<
719

8
                m_state << "'!";
720
    }
721
722
5
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
723



5
    if (!TypeInfoUtil::isCompound(fieldInfo.typeInfo.getCppType()))
724
    {
725



2
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName <<
726
2
                "' is not a compound!";
727
    }
728
729
8
    auto compoundArray = m_valueStack.back();
730


4
    compoundArray->resize(compoundArray->size() + 1);
731



4
    m_valueStack.push_back(compoundArray->at(compoundArray->size() - 1));
732
4
    m_state = detail::CreatorState::IN_COMPOUND;
733
4
}
734
735
template <typename ALLOC>
736
8
void BasicZserioTreeCreator<ALLOC>::endCompoundElement()
737
{
738



8
    if (m_state != detail::CreatorState::IN_COMPOUND || m_fieldInfoStack.empty())
739
    {
740


12
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound element in state '") <<
741


12
                m_state << (m_fieldInfoStack.empty() ? ", expecting endRoot!" : "'!");
742
    }
743
744
4
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
745

4
    if (!fieldInfo.isArray)
746

1
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound element, not in array!");
747
748
3
    m_valueStack.pop_back();
749
3
    m_state = detail::CreatorState::IN_ARRAY;
750
3
}
751
752
template <typename ALLOC>
753
template <typename T>
754
24
void BasicZserioTreeCreator<ALLOC>::addValueElement(T&& value)
755
{
756







24
    if (m_state != detail::CreatorState::IN_ARRAY)
757
    {
758














8
        throw CppRuntimeException("ZserioTreeCreator: Cannot add value element in state '") <<
759







8
                m_state << "'!";
760
    }
761
762
20
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
763







21
    m_valueStack.back()->append(makeAnyValue(fieldInfo.typeInfo, std::forward<T>(value)));
764
18
}
765
766
template <typename ALLOC>
767
7
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getElementType() const
768
{
769

7
    if (m_state != detail::CreatorState::IN_ARRAY)
770
    {
771



2
        throw CppRuntimeException("ZserioTreeCreator: Cannot get element type in state '") << m_state <<
772
2
                "'!";
773
    }
774
775
6
    return m_fieldInfoStack.back().get().typeInfo;
776
}
777
778
template <typename ALLOC>
779
254
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getTypeInfo() const
780
{
781

254
    return m_fieldInfoStack.empty() ? m_typeInfo : m_fieldInfoStack.back().get().typeInfo;
782
}
783
784
template <typename ALLOC>
785
254
const BasicFieldInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::findFieldInfo(
786
        const IBasicTypeInfo<ALLOC>& typeInfo, StringView name) const
787
{
788

254
    Span<const BasicFieldInfo<ALLOC>> fields = typeInfo.getFields();
789
254
    auto found_it = std::find_if(fields.begin(), fields.end(),
790

1016
            [name](const BasicFieldInfo<ALLOC>& field){ return field.schemaName == name; });
791

254
    if (found_it == fields.end())
792
    {
793





20
        throw CppRuntimeException("ZserioTreeCreator: Member '") << name <<  "' not found in '" <<
794

20
                typeInfo.getSchemaName() << "'!";
795
    }
796
797
244
    return *found_it;
798
}
799
800
template <typename ALLOC>
801
template <typename T>
802
148
AnyHolder<ALLOC> BasicZserioTreeCreator<ALLOC>::makeAnyValue(
803
        const IBasicTypeInfo<ALLOC>& typeInfo, T&& value) const
804
{
805









148
    return detail::makeAnyValue(typeInfo, std::forward<T>(value), get_allocator());
806
}
807
808
} // namespace zserio
809
810
#endif // ZSERIO_ZSERIO_TREE_CREATOR_H_INC