Coverage Report

Created: 2023-12-13 14:58

src/zserio/ZserioTreeCreator.h
Line
Count
Source (jump to first uncovered line)
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
bool checkArithmeticValueRanges(U value)
31
53
{
32
    // value is unsigned
33
53
    return (value <= static_cast<U>(std::numeric_limits<T>::max()));
34
53
}
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
bool checkArithmeticValueRanges(U value)
40
17
{
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
17
            
static_cast<int64_t>(value) <= static_cast<int64_t>(std::numeric_limits<T>::max())14
);
44
17
}
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
bool checkArithmeticValueRanges(U value)
50
18
{
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())14
);
53
18
}
54
55
template <typename T, typename ALLOC>
56
AnyHolder<ALLOC> makeAnyBoolValue(bool value, const ALLOC& allocator)
57
3
{
58
3
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
59
3
}
60
61
template <typename T, typename U, typename ALLOC>
62
AnyHolder<ALLOC> makeAnyBoolValue(const U& value, const ALLOC&)
63
4
{
64
4
    throw CppRuntimeException("ZserioTreeCreator: Value '") << value <<
65
4
            "' cannot be converted to bool value!";
66
4
}
67
68
template <typename T, typename ALLOC>
69
AnyHolder<ALLOC> makeAnyIntegralValue(bool value, const ALLOC&)
70
8
{
71
8
    throw CppRuntimeException("ZserioTreeCreator: Bool value '") << value <<
72
8
            "' cannot be converted to integral type!";
73
8
}
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
AnyHolder<ALLOC> makeAnyIntegralValue(U value, const ALLOC& allocator)
78
88
{
79
    // check ranges of integers
80
88
    if (!checkArithmeticValueRanges<T>(value))
81
15
    {
82
15
        throw CppRuntimeException("ZserioTreeCreator: Integral value '") << value << "' overflow (<" <<
83
15
                std::numeric_limits<T>::min() << ", " << std::numeric_limits<T>::max() << ">)!";
84
15
    }
85
86
73
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
87
88
}
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
AnyHolder<ALLOC> makeAnyIntegralValue(const U& value, const ALLOC&)
92
27
{
93
27
    throw CppRuntimeException("ZserioTreeCreator: Value '") << value <<
94
27
            "' cannot be converted to integral value!";
95
27
}
96
97
template <typename T, typename ALLOC>
98
AnyHolder<ALLOC> makeAnyFloatingValue(bool value, const ALLOC&)
99
2
{
100
2
    throw CppRuntimeException("ZserioTreeCreator: Bool value '") << value <<
101
2
            "' cannot be converted to floating type!";
102
2
}
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
AnyHolder<ALLOC> makeAnyFloatingValue(U value, const ALLOC& allocator)
107
6
{
108
    // allow conversion integers to floats
109
6
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
110
6
}
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
AnyHolder<ALLOC> makeAnyFloatingValue(const U& value, const ALLOC&)
115
4
{
116
4
    throw CppRuntimeException("ZserioTreeCreator: Value '") << value <<
117
4
            "' cannot be converted to floating value!";
118
4
}
119
120
template <typename ALLOC>
121
AnyHolder<ALLOC> makeAnyStringValue(const string<ALLOC>& value, const ALLOC& allocator)
122
2
{
123
2
    return AnyHolder<ALLOC>(value, allocator);
124
2
}
125
126
template <typename ALLOC>
127
AnyHolder<ALLOC> makeAnyStringValue(string<ALLOC>&& value, const ALLOC& allocator)
128
2
{
129
2
    return AnyHolder<ALLOC>(std::move(value), allocator);
130
2
}
131
132
template <typename ALLOC>
133
AnyHolder<ALLOC> makeAnyStringValue(StringView value, const ALLOC& allocator)
134
62
{
135
62
    return AnyHolder<ALLOC>(toString(value, allocator), allocator);
136
62
}
137
138
template <typename ALLOC>
139
AnyHolder<ALLOC> makeAnyStringValue(const char* value, const ALLOC& allocator)
140
7
{
141
7
    return makeAnyStringValue(StringView(value), allocator);
142
7
}
143
144
template <typename T, typename ALLOC>
145
AnyHolder<ALLOC> makeAnyStringValue(const T&, const ALLOC&)
146
5
{
147
5
    throw CppRuntimeException("ZserioTreeCreator: Trying to make any string value from unsupported type!");
148
5
}
149
150
template <typename ALLOC>
151
AnyHolder<ALLOC> parseEnumStringValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
152
        const ALLOC& allocator)
153
11
{
154
11
    for (const auto& itemInfo : typeInfo.getEnumItems())
155
26
    {
156
26
        if (itemInfo.schemaName == stringValue)
157
7
        {
158
7
            if (TypeInfoUtil::isSigned(typeInfo.getUnderlyingType().getCppType()))
159
5
            {
160
5
                return makeAnyValue(typeInfo.getUnderlyingType(), static_cast<int64_t>(itemInfo.value),
161
5
                        allocator);
162
5
            }
163
2
            else
164
2
            {
165
2
                return makeAnyValue(typeInfo.getUnderlyingType(), itemInfo.value, allocator);
166
2
            }
167
7
        }
168
26
    }
169
170
4
    return AnyHolder<ALLOC>(allocator);
171
11
}
172
173
template <typename ALLOC>
174
AnyHolder<ALLOC> makeAnyEnumValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
175
        const ALLOC& allocator)
176
24
{
177
24
    if (!stringValue.empty())
178
22
    {
179
22
        const char firstChar = stringValue[0];
180
22
        if ((firstChar >= 'A' && 
firstChar <= 'Z'12
) ||
(13
firstChar >= 'a'13
&&
firstChar <= 'z'2
) ||
181
22
                
firstChar == '_'12
)
182
11
        {
183
11
            AnyHolder<ALLOC> anyValue = parseEnumStringValue(stringValue, typeInfo, allocator);
184
11
            if (anyValue.hasValue())
185
7
                return anyValue;
186
11
        }
187
        // else it's a no match
188
22
    }
189
190
17
    throw CppRuntimeException("ZserioTreeCreator: Cannot create enum '") << typeInfo.getSchemaName() <<
191
17
            "' from string value '" << stringValue << "'!";
192
24
}
193
194
template <typename ALLOC>
195
AnyHolder<ALLOC> makeAnyEnumValue(const string<ALLOC>& stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
196
        const ALLOC& allocator)
197
2
{
198
2
    return makeAnyEnumValue(StringView(stringValue), typeInfo, allocator);
199
2
}
200
201
template <typename ALLOC>
202
AnyHolder<ALLOC> makeAnyEnumValue(const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
203
        const ALLOC& allocator)
204
3
{
205
3
    return makeAnyEnumValue(StringView(stringValue), typeInfo, allocator);
206
3
}
207
208
template <typename T, typename ALLOC,
209
        typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
210
AnyHolder<ALLOC> makeAnyEnumValue(T enumValue, const IBasicTypeInfo<ALLOC>&, const ALLOC& allocator)
211
3
{
212
3
    return AnyHolder<ALLOC>(enumValue, allocator);
213
3
}
214
215
template <typename T, typename ALLOC,
216
        typename std::enable_if<!std::is_enum<T>::value, int>::type = 0>
217
AnyHolder<ALLOC> makeAnyEnumValue(T enumRawValue, const IBasicTypeInfo<ALLOC>& typeInfo,
218
        const ALLOC& allocator)
219
8
{
220
8
    return makeAnyValue(typeInfo.getUnderlyingType(), enumRawValue, allocator);
221
8
}
222
223
template <typename ALLOC>
224
AnyHolder<ALLOC> parseBitmaskStringValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
225
        const ALLOC& allocator)
226
26
{
227
26
    uint64_t value = 0;
228
26
    size_t pos = 0;
229
44
    while (pos < stringValue.size())
230
38
    {
231
38
        bool match = false;
232
38
        const size_t available = stringValue.size() - pos;
233
38
        for (const auto& itemInfo : typeInfo.getBitmaskValues())
234
64
        {
235
64
            if (available >= itemInfo.schemaName.size() &&
236
64
                    
stringValue.substr(pos, itemInfo.schemaName.size()) == itemInfo.schemaName63
)
237
44
            {
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] == ' '33
||
stringValue[newPos] == '|'18
)
241
29
                {
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
29
                }
249
44
            }
250
64
        }
251
252
27
        if (!match)
253
9
            break;
254
255
33
        
while (18
pos < stringValue.size() &&
stringValue[pos] == ' '32
)
256
15
            ++pos;
257
258
18
        if (pos < stringValue.size() && 
stringValue[pos] == '|'17
)
259
15
            ++pos;
260
261
29
        while (pos < stringValue.size() && 
stringValue[pos] == ' '23
)
262
11
            ++pos;
263
18
    }
264
265
    // invalid format or identifier
266
15
    return AnyHolder<ALLOC>(allocator);
267
26
}
268
269
template <typename ALLOC>
270
AnyHolder<ALLOC> parseBitmaskNumericStringValue(const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
271
        const ALLOC& allocator)
272
10
{
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
10
}
280
281
template <typename ALLOC>
282
AnyHolder<ALLOC> makeAnyBitmaskValue(StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
283
        const ALLOC& allocator)
284
32
{
285
32
    if (!stringValue.empty())
286
30
    {
287
30
        const char firstChar = stringValue[0];
288
30
        if ((firstChar >= 'A' && 
firstChar <= 'Z'16
) ||
(17
firstChar >= 'a'17
&&
firstChar <= 'z'2
) ||
289
30
                
firstChar == '_'16
)
290
15
        {
291
15
            AnyHolder<ALLOC> anyValue = parseBitmaskStringValue(stringValue, typeInfo, allocator);
292
15
            if (anyValue.hasValue())
293
5
                return anyValue;
294
15
        }
295
15
        else if (firstChar >= '0' && 
firstChar <= '9'12
) // bitmask can be only unsigned
296
10
        {
297
            // ensure zero-terminated string
298
10
            const string<ALLOC> numericStringValue = toString(stringValue, allocator);
299
10
            AnyHolder<ALLOC> anyValue = parseBitmaskNumericStringValue(numericStringValue.c_str(), typeInfo,
300
10
                    allocator);
301
10
            if (anyValue.hasValue())
302
9
                return anyValue;
303
10
        }
304
30
    }
305
306
18
    throw CppRuntimeException("ZserioTreeCreator: Cannot create bitmask '") << typeInfo.getSchemaName() <<
307
18
            "' from string value '" << stringValue << "'!";
308
32
}
309
310
template <typename ALLOC>
311
AnyHolder<ALLOC> makeAnyBitmaskValue(const string<ALLOC>& stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
312
        const ALLOC& allocator)
313
2
{
314
2
    return makeAnyBitmaskValue(StringView(stringValue), typeInfo, allocator);
315
2
}
316
317
template <typename ALLOC>
318
AnyHolder<ALLOC> makeAnyBitmaskValue(const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo,
319
        const ALLOC& allocator)
320
1
{
321
1
    return makeAnyBitmaskValue(StringView(stringValue), typeInfo, allocator);
322
1
}
323
324
template <typename T, typename ALLOC,
325
        typename std::enable_if<is_bitmask<T>::value, int>::type = 0>
326
AnyHolder<ALLOC> makeAnyBitmaskValue(T bitmaskValue, const IBasicTypeInfo<ALLOC>&, const ALLOC& allocator)
327
3
{
328
3
    return AnyHolder<ALLOC>(bitmaskValue, allocator);
329
3
}
330
331
template <typename T, typename ALLOC,
332
        typename std::enable_if<!is_bitmask<T>::value, int>::type = 0>
333
AnyHolder<ALLOC> makeAnyBitmaskValue(T bitmaskRawValue, const IBasicTypeInfo<ALLOC>& typeInfo,
334
        const ALLOC& allocator)
335
7
{
336
7
    return makeAnyValue(typeInfo.getUnderlyingType(), bitmaskRawValue, allocator);
337
7
}
338
339
template <typename T, typename ALLOC>
340
AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>& typeInfo, T&& value, const ALLOC& allocator)
341
295
{
342
295
    switch (typeInfo.getCppType())
343
295
    {
344
7
    case CppType::BOOL:
345
7
        return makeAnyBoolValue<bool>(std::forward<T>(value), allocator);
346
37
    case CppType::UINT8:
347
37
        return makeAnyIntegralValue<uint8_t>(std::forward<T>(value), allocator);
348
7
    case CppType::UINT16:
349
7
        return makeAnyIntegralValue<uint16_t>(std::forward<T>(value), allocator);
350
34
    case CppType::UINT32:
351
34
        return makeAnyIntegralValue<uint32_t>(std::forward<T>(value), allocator);
352
6
    case CppType::UINT64:
353
6
        return makeAnyIntegralValue<uint64_t>(std::forward<T>(value), allocator);
354
19
    case CppType::INT8:
355
19
        return makeAnyIntegralValue<int8_t>(std::forward<T>(value), allocator);
356
7
    case CppType::INT16:
357
7
        return makeAnyIntegralValue<int16_t>(std::forward<T>(value), allocator);
358
7
    case CppType::INT32:
359
7
        return makeAnyIntegralValue<int32_t>(std::forward<T>(value), allocator);
360
6
    case CppType::INT64:
361
6
        return makeAnyIntegralValue<int64_t>(std::forward<T>(value), allocator);
362
6
    case CppType::FLOAT:
363
6
        return makeAnyFloatingValue<float>(std::forward<T>(value), allocator);
364
6
    case CppType::DOUBLE:
365
6
        return makeAnyFloatingValue<double>(std::forward<T>(value), allocator);
366
71
    case CppType::STRING:
367
71
        return makeAnyStringValue(std::forward<T>(value), allocator);
368
35
    case CppType::ENUM:
369
35
        return makeAnyEnumValue(std::forward<T>(value), typeInfo, allocator);
370
42
    case CppType::BITMASK:
371
42
        return makeAnyBitmaskValue(std::forward<T>(value), typeInfo, allocator);
372
5
    default:
373
5
        return AnyHolder<ALLOC>(std::forward<T>(value), allocator);
374
295
    }
375
295
}
376
377
// overload for values which are already in AnyHolder
378
template <typename ALLOC>
379
AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>&, AnyHolder<ALLOC>&& anyValue, const ALLOC&)
380
17
{
381
17
    return std::move(anyValue);
382
17
}
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
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
BasicZserioTreeCreator<ALLOC>::BasicZserioTreeCreator(const IBasicTypeInfo<ALLOC>& typeInfo,
550
        const ALLOC& allocator) :
551
        AllocatorHolder<ALLOC>(allocator),
552
        m_typeInfo(typeInfo), m_fieldInfoStack(allocator), m_valueStack(allocator)
553
85
{}
554
555
template <typename ALLOC>
556
void BasicZserioTreeCreator<ALLOC>::beginRoot()
557
87
{
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
IBasicReflectablePtr<ALLOC> BasicZserioTreeCreator<ALLOC>::endRoot()
567
54
{
568
54
    if (m_state != detail::CreatorState::IN_COMPOUND || 
m_valueStack.size() != 151
)
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
54
}
576
577
template <typename ALLOC>
578
void BasicZserioTreeCreator<ALLOC>::beginArray(const string<ALLOC>& name)
579
23
{
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
20
    const auto& fieldInfo = findFieldInfo(parentTypeInfo, name);
585
20
    if (!fieldInfo.isArray)
586
2
    {
587
2
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName <<
588
2
                 "' is not an array!";
589
2
    }
590
591
18
    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
18
    m_valueStack.push_back(m_valueStack.back()->createField(name));
600
601
18
    m_state = detail::CreatorState::IN_ARRAY;
602
18
}
603
604
template <typename ALLOC>
605
void BasicZserioTreeCreator<ALLOC>::endArray()
606
16
{
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
void BasicZserioTreeCreator<ALLOC>::beginCompound(const string<ALLOC>& name)
617
44
{
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
41
    const auto& fieldInfo = findFieldInfo(parentTypeInfo, name);
623
41
    if (fieldInfo.isArray)
624
1
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName << "' is an array!";
625
626
40
    if (!TypeInfoUtil::isCompound(fieldInfo.typeInfo.getCppType()))
627
1
    {
628
1
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName <<
629
1
                "' is not a compound!";
630
1
    }
631
632
39
    m_fieldInfoStack.push_back(fieldInfo);
633
39
    if (TypeInfoUtil::hasChoice(parentTypeInfo.getCppType()) || 
fieldInfo.isOptional36
)
634
1
    {
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
1
    }
638
38
    else
639
38
    {
640
38
        m_valueStack.push_back(m_valueStack.back()->getField(name));
641
38
    }
642
643
39
    m_state = detail::CreatorState::IN_COMPOUND;
644
39
}
645
646
template <typename ALLOC>
647
void BasicZserioTreeCreator<ALLOC>::endCompound()
648
17
{
649
17
    if (m_state != detail::CreatorState::IN_COMPOUND || 
m_fieldInfoStack.empty()14
)
650
4
    {
651
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound in state '") << m_state <<
652
4
                "'" << (m_fieldInfoStack.empty() ? 
", expecting endRoot!"2
:
"!'"2
);
653
4
    }
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
void BasicZserioTreeCreator<ALLOC>::setValue(const string<ALLOC>& name, T&& value)
666
136
{
667
136
    if (m_state != detail::CreatorState::IN_COMPOUND)
668
3
        throw CppRuntimeException("ZserioTreeCreator: Cannot set value in state '") << m_state << "'!";
669
670
133
    const BasicFieldInfo<ALLOC>& fieldInfo = findFieldInfo(getTypeInfo(), name);
671
133
    if (fieldInfo.isArray)
672
1
    {
673
1
        throw CppRuntimeException("ZserioTreeCreator: Expecting array in member '") <<
674
1
                fieldInfo.schemaName << "'!";
675
1
    }
676
677
132
    m_valueStack.back()->setField(fieldInfo.schemaName,
678
132
            makeAnyValue(fieldInfo.typeInfo, std::forward<T>(value)));
679
132
}
680
681
template <typename ALLOC>
682
void BasicZserioTreeCreator<ALLOC>::setValue(const string<ALLOC>& name, std::nullptr_t nullValue)
683
3
{
684
3
    if (m_state != detail::CreatorState::IN_COMPOUND)
685
1
    {
686
1
        throw CppRuntimeException("ZserioTreeCreator: Cannot set value (null) in state '") << m_state <<
687
1
                "'!";
688
1
    }
689
690
2
    const BasicFieldInfo<ALLOC>& fieldInfo = findFieldInfo(getTypeInfo(), name);
691
2
    if (fieldInfo.isOptional)
692
1
    {
693
        // reset an optional field
694
1
        m_valueStack.back()->setField(fieldInfo.schemaName, AnyHolder<ALLOC>(nullValue, get_allocator()));
695
1
    }
696
1
    else
697
1
    {
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
1
    }
702
2
}
703
704
template <typename ALLOC>
705
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getFieldType(const string<ALLOC>& name) const
706
59
{
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
59
}
712
713
template <typename ALLOC>
714
void BasicZserioTreeCreator<ALLOC>::beginCompoundElement()
715
9
{
716
9
    if (m_state != detail::CreatorState::IN_ARRAY)
717
4
    {
718
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin compound element in state '") <<
719
4
                m_state << "'!";
720
4
    }
721
722
5
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
723
5
    if (!TypeInfoUtil::isCompound(fieldInfo.typeInfo.getCppType()))
724
1
    {
725
1
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName <<
726
1
                "' is not a compound!";
727
1
    }
728
729
4
    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
void BasicZserioTreeCreator<ALLOC>::endCompoundElement()
737
8
{
738
8
    if (m_state != detail::CreatorState::IN_COMPOUND || 
m_fieldInfoStack.empty()5
)
739
4
    {
740
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound element in state '") <<
741
4
                m_state << (m_fieldInfoStack.empty() ? 
", expecting endRoot!"2
:
"'!"2
);
742
4
    }
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
void BasicZserioTreeCreator<ALLOC>::addValueElement(T&& value)
755
24
{
756
24
    if (m_state != detail::CreatorState::IN_ARRAY)
757
4
    {
758
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot add value element in state '") <<
759
4
                m_state << "'!";
760
4
    }
761
762
20
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
763
20
    m_valueStack.back()->append(makeAnyValue(fieldInfo.typeInfo, std::forward<T>(value)));
764
20
}
765
766
template <typename ALLOC>
767
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getElementType() const
768
7
{
769
7
    if (m_state != detail::CreatorState::IN_ARRAY)
770
1
    {
771
1
        throw CppRuntimeException("ZserioTreeCreator: Cannot get element type in state '") << m_state <<
772
1
                "'!";
773
1
    }
774
775
6
    return m_fieldInfoStack.back().get().typeInfo;
776
7
}
777
778
template <typename ALLOC>
779
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getTypeInfo() const
780
254
{
781
254
    return m_fieldInfoStack.empty() ? 
m_typeInfo156
:
m_fieldInfoStack.back().get().typeInfo98
;
782
254
}
783
784
template <typename ALLOC>
785
const BasicFieldInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::findFieldInfo(
786
        const IBasicTypeInfo<ALLOC>& typeInfo, StringView name) const
787
254
{
788
254
    Span<const BasicFieldInfo<ALLOC>> fields = typeInfo.getFields();
789
254
    auto found_it = std::find_if(fields.begin(), fields.end(),
790
762
            [name](const BasicFieldInfo<ALLOC>& field){ return field.schemaName == name; });
791
254
    if (found_it == fields.end())
792
10
    {
793
10
        throw CppRuntimeException("ZserioTreeCreator: Member '") << name <<  "' not found in '" <<
794
10
                typeInfo.getSchemaName() << "'!";
795
10
    }
796
797
244
    return *found_it;
798
254
}
799
800
template <typename ALLOC>
801
template <typename T>
802
AnyHolder<ALLOC> BasicZserioTreeCreator<ALLOC>::makeAnyValue(
803
        const IBasicTypeInfo<ALLOC>& typeInfo, T&& value) const
804
148
{
805
148
    return detail::makeAnyValue(typeInfo, std::forward<T>(value), get_allocator());
806
148
}
807
808
} // namespace zserio
809
810
#endif // ZSERIO_ZSERIO_TREE_CREATOR_H_INC