Coverage Report

Created: 2024-04-30 09:35

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 <cerrno>
5
#include <cstdlib>
6
#include <limits>
7
#include <type_traits>
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/Traits.h"
15
#include "zserio/TypeInfoUtil.h"
16
#include "zserio/Types.h"
17
#include "zserio/Vector.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
59
{
32
    // value is unsigned
33
59
    return (value <= static_cast<U>(std::numeric_limits<T>::max()));
34
59
}
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,
39
                int>::type = 0>
40
bool checkArithmeticValueRanges(U value)
41
19
{
42
    // value is signed and it is converted to signed value
43
19
    return (static_cast<int64_t>(value) >= static_cast<int64_t>(std::numeric_limits<T>::min()) &&
44
19
            
static_cast<int64_t>(value) <= static_cast<int64_t>(std::numeric_limits<T>::max())16
);
45
19
}
46
47
template <typename T, typename U,
48
        typename std::enable_if<std::is_signed<typename std::decay<U>::type>::value &&
49
                        std::is_unsigned<typename std::decay<T>::type>::value,
50
                int>::type = 0>
51
bool checkArithmeticValueRanges(U value)
52
18
{
53
    // value is signed and it is converted to unsigned value
54
18
    return (value >= 0 && 
static_cast<uint64_t>(value) <= static_cast<uint64_t>(std::numeric_limits<T>::max())14
);
55
18
}
56
57
template <typename T, typename ALLOC>
58
AnyHolder<ALLOC> makeAnyBoolValue(bool value, const ALLOC& allocator)
59
3
{
60
3
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
61
3
}
62
63
template <typename T, typename U, typename ALLOC>
64
AnyHolder<ALLOC> makeAnyBoolValue(const U& value, const ALLOC&)
65
4
{
66
4
    throw CppRuntimeException("ZserioTreeCreator: Value '") << value << "' cannot be converted to bool value!";
67
4
}
68
69
template <typename T, typename ALLOC>
70
AnyHolder<ALLOC> makeAnyIntegralValue(bool value, const ALLOC&)
71
8
{
72
8
    throw CppRuntimeException("ZserioTreeCreator: Bool value '")
73
8
            << value << "' cannot be converted to integral type!";
74
8
}
75
76
template <typename T, typename U, typename ALLOC,
77
        typename std::enable_if<std::is_integral<typename std::decay<U>::type>::value, int>::type = 0>
78
AnyHolder<ALLOC> makeAnyIntegralValue(U value, const ALLOC& allocator)
79
96
{
80
    // check ranges of integers
81
96
    if (!checkArithmeticValueRanges<T>(value))
82
15
    {
83
15
        throw CppRuntimeException("ZserioTreeCreator: Integral value '")
84
15
                << value << "' overflow (<" << std::numeric_limits<T>::min() << ", "
85
15
                << std::numeric_limits<T>::max() << ">)!";
86
15
    }
87
88
81
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
89
96
}
90
91
template <typename T, typename U, typename ALLOC,
92
        typename std::enable_if<!std::is_integral<typename std::decay<U>::type>::value, int>::type = 0>
93
AnyHolder<ALLOC> makeAnyIntegralValue(const U& value, const ALLOC&)
94
27
{
95
27
    throw CppRuntimeException("ZserioTreeCreator: Value '")
96
27
            << value << "' cannot be converted to integral value!";
97
27
}
98
99
template <typename T, typename ALLOC>
100
AnyHolder<ALLOC> makeAnyFloatingValue(bool value, const ALLOC&)
101
2
{
102
2
    throw CppRuntimeException("ZserioTreeCreator: Bool value '")
103
2
            << value << "' cannot be converted to floating type!";
104
2
}
105
106
template <typename T, typename U, typename ALLOC,
107
        typename std::enable_if<std::is_arithmetic<typename std::decay<U>::type>::value, int>::type = 0>
108
AnyHolder<ALLOC> makeAnyFloatingValue(U value, const ALLOC& allocator)
109
6
{
110
    // allow conversion integers to floats
111
6
    return AnyHolder<ALLOC>(static_cast<T>(value), allocator);
112
6
}
113
114
template <typename T, typename U, typename ALLOC,
115
        typename std::enable_if<!std::is_arithmetic<typename std::decay<U>::type>::value, int>::type = 0>
116
AnyHolder<ALLOC> makeAnyFloatingValue(const U& value, const ALLOC&)
117
4
{
118
4
    throw CppRuntimeException("ZserioTreeCreator: Value '")
119
4
            << value << "' cannot be converted to floating value!";
120
4
}
121
122
template <typename ALLOC>
123
AnyHolder<ALLOC> makeAnyStringValue(const string<ALLOC>& value, const ALLOC& allocator)
124
2
{
125
2
    return AnyHolder<ALLOC>(value, allocator);
126
2
}
127
128
template <typename ALLOC>
129
AnyHolder<ALLOC> makeAnyStringValue(string<ALLOC>&& value, const ALLOC& allocator)
130
2
{
131
2
    return AnyHolder<ALLOC>(std::move(value), allocator);
132
2
}
133
134
template <typename ALLOC>
135
AnyHolder<ALLOC> makeAnyStringValue(StringView value, const ALLOC& allocator)
136
64
{
137
64
    return AnyHolder<ALLOC>(toString(value, allocator), allocator);
138
64
}
139
140
template <typename ALLOC>
141
AnyHolder<ALLOC> makeAnyStringValue(const char* value, const ALLOC& allocator)
142
7
{
143
7
    return makeAnyStringValue(StringView(value), allocator);
144
7
}
145
146
template <typename T, typename ALLOC>
147
AnyHolder<ALLOC> makeAnyStringValue(const T&, const ALLOC&)
148
5
{
149
5
    throw CppRuntimeException("ZserioTreeCreator: Trying to make any string value from unsupported type!");
150
5
}
151
152
template <typename ALLOC>
153
AnyHolder<ALLOC> parseEnumStringValue(
154
        StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
155
11
{
156
11
    for (const auto& itemInfo : typeInfo.getEnumItems())
157
26
    {
158
26
        if (itemInfo.schemaName == stringValue)
159
7
        {
160
7
            if (TypeInfoUtil::isSigned(typeInfo.getUnderlyingType().getCppType()))
161
5
            {
162
5
                return makeAnyValue(
163
5
                        typeInfo.getUnderlyingType(), static_cast<int64_t>(itemInfo.value), allocator);
164
5
            }
165
2
            else
166
2
            {
167
2
                return makeAnyValue(typeInfo.getUnderlyingType(), itemInfo.value, allocator);
168
2
            }
169
7
        }
170
26
    }
171
172
4
    return AnyHolder<ALLOC>(allocator);
173
11
}
174
175
template <typename ALLOC>
176
AnyHolder<ALLOC> makeAnyEnumValue(
177
        StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
178
24
{
179
24
    if (!stringValue.empty())
180
22
    {
181
22
        const char firstChar = stringValue[0];
182
22
        if ((firstChar >= 'A' && 
firstChar <= 'Z'12
) ||
(13
firstChar >= 'a'13
&&
firstChar <= 'z'2
) ||
183
22
                
firstChar == '_'12
)
184
11
        {
185
11
            AnyHolder<ALLOC> anyValue = parseEnumStringValue(stringValue, typeInfo, allocator);
186
11
            if (anyValue.hasValue())
187
7
                return anyValue;
188
11
        }
189
        // else it's a no match
190
22
    }
191
192
17
    throw CppRuntimeException("ZserioTreeCreator: Cannot create enum '")
193
17
            << typeInfo.getSchemaName() << "' from string value '" << stringValue << "'!";
194
24
}
195
196
template <typename ALLOC>
197
AnyHolder<ALLOC> makeAnyEnumValue(
198
        const string<ALLOC>& stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
199
2
{
200
2
    return makeAnyEnumValue(StringView(stringValue), typeInfo, allocator);
201
2
}
202
203
template <typename ALLOC>
204
AnyHolder<ALLOC> makeAnyEnumValue(
205
        const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
206
3
{
207
3
    return makeAnyEnumValue(StringView(stringValue), typeInfo, allocator);
208
3
}
209
210
template <typename T, typename ALLOC, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
211
AnyHolder<ALLOC> makeAnyEnumValue(T enumValue, const IBasicTypeInfo<ALLOC>&, const ALLOC& allocator)
212
3
{
213
3
    return AnyHolder<ALLOC>(enumValue, allocator);
214
3
}
215
216
template <typename T, typename ALLOC, typename std::enable_if<!std::is_enum<T>::value, int>::type = 0>
217
AnyHolder<ALLOC> makeAnyEnumValue(T enumRawValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
218
10
{
219
10
    return makeAnyValue(typeInfo.getUnderlyingType(), enumRawValue, allocator);
220
10
}
221
222
template <typename ALLOC>
223
AnyHolder<ALLOC> parseBitmaskStringValue(
224
        StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
225
26
{
226
26
    uint64_t value = 0;
227
26
    size_t pos = 0;
228
44
    while (pos < stringValue.size())
229
38
    {
230
38
        bool match = false;
231
38
        const size_t available = stringValue.size() - pos;
232
38
        for (const auto& itemInfo : typeInfo.getBitmaskValues())
233
64
        {
234
64
            if (available >= itemInfo.schemaName.size() &&
235
64
                    
stringValue.substr(pos, itemInfo.schemaName.size()) == itemInfo.schemaName63
)
236
44
            {
237
44
                const size_t newPos = pos + itemInfo.schemaName.size();
238
                // check that the identifier really ends here
239
44
                if (newPos == stringValue.size() || 
stringValue[newPos] == ' '33
||
stringValue[newPos] == '|'18
)
240
29
                {
241
29
                    value |= itemInfo.value;
242
29
                    if (newPos == stringValue.size())
243
11
                        return makeAnyValue(typeInfo.getUnderlyingType(), value, allocator); // end of string
244
18
                    match = true;
245
18
                    pos += itemInfo.schemaName.size();
246
18
                    break;
247
29
                }
248
44
            }
249
64
        }
250
251
27
        if (!match)
252
9
            break;
253
254
33
        
while (18
pos < stringValue.size() &&
stringValue[pos] == ' '32
)
255
15
            ++pos;
256
257
18
        if (pos < stringValue.size() && 
stringValue[pos] == '|'17
)
258
15
            ++pos;
259
260
29
        while (pos < stringValue.size() && 
stringValue[pos] == ' '23
)
261
11
            ++pos;
262
18
    }
263
264
    // invalid format or identifier
265
15
    return AnyHolder<ALLOC>(allocator);
266
26
}
267
268
template <typename ALLOC>
269
AnyHolder<ALLOC> parseBitmaskNumericStringValue(
270
        const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
271
10
{
272
10
    char* pEnd = nullptr;
273
10
    errno = 0;
274
10
    uint64_t value = std::strtoull(stringValue, &pEnd, 10);
275
10
    if (errno == ERANGE)
276
1
        return AnyHolder<ALLOC>(allocator);
277
9
    return makeAnyValue(typeInfo.getUnderlyingType(), value, allocator);
278
10
}
279
280
template <typename ALLOC>
281
AnyHolder<ALLOC> makeAnyBitmaskValue(
282
        StringView stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
283
32
{
284
32
    if (!stringValue.empty())
285
30
    {
286
30
        const char firstChar = stringValue[0];
287
30
        if ((firstChar >= 'A' && 
firstChar <= 'Z'16
) ||
(17
firstChar >= 'a'17
&&
firstChar <= 'z'2
) ||
288
30
                
firstChar == '_'16
)
289
15
        {
290
15
            AnyHolder<ALLOC> anyValue = parseBitmaskStringValue(stringValue, typeInfo, allocator);
291
15
            if (anyValue.hasValue())
292
5
                return anyValue;
293
15
        }
294
15
        else if (firstChar >= '0' && 
firstChar <= '9'12
) // bitmask can be only unsigned
295
10
        {
296
            // ensure zero-terminated string
297
10
            const string<ALLOC> numericStringValue = toString(stringValue, allocator);
298
10
            AnyHolder<ALLOC> anyValue =
299
10
                    parseBitmaskNumericStringValue(numericStringValue.c_str(), typeInfo, allocator);
300
10
            if (anyValue.hasValue())
301
9
                return anyValue;
302
10
        }
303
30
    }
304
305
18
    throw CppRuntimeException("ZserioTreeCreator: Cannot create bitmask '")
306
18
            << typeInfo.getSchemaName() << "' from string value '" << stringValue << "'!";
307
32
}
308
309
template <typename ALLOC>
310
AnyHolder<ALLOC> makeAnyBitmaskValue(
311
        const string<ALLOC>& stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
312
2
{
313
2
    return makeAnyBitmaskValue(StringView(stringValue), typeInfo, allocator);
314
2
}
315
316
template <typename ALLOC>
317
AnyHolder<ALLOC> makeAnyBitmaskValue(
318
        const char* stringValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
319
1
{
320
1
    return makeAnyBitmaskValue(StringView(stringValue), typeInfo, allocator);
321
1
}
322
323
template <typename T, typename ALLOC, typename std::enable_if<is_bitmask<T>::value, int>::type = 0>
324
AnyHolder<ALLOC> makeAnyBitmaskValue(T bitmaskValue, const IBasicTypeInfo<ALLOC>&, const ALLOC& allocator)
325
3
{
326
3
    return AnyHolder<ALLOC>(bitmaskValue, allocator);
327
3
}
328
329
template <typename T, typename ALLOC, typename std::enable_if<!is_bitmask<T>::value, int>::type = 0>
330
AnyHolder<ALLOC> makeAnyBitmaskValue(
331
        T bitmaskRawValue, const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator)
332
9
{
333
9
    return makeAnyValue(typeInfo.getUnderlyingType(), bitmaskRawValue, allocator);
334
9
}
335
336
template <typename T, typename ALLOC>
337
AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>& typeInfo, T&& value, const ALLOC& allocator)
338
309
{
339
309
    switch (typeInfo.getCppType())
340
309
    {
341
7
    case CppType::BOOL:
342
7
        return makeAnyBoolValue<bool>(std::forward<T>(value), allocator);
343
39
    case CppType::UINT8:
344
39
        return makeAnyIntegralValue<uint8_t>(std::forward<T>(value), allocator);
345
7
    case CppType::UINT16:
346
7
        return makeAnyIntegralValue<uint16_t>(std::forward<T>(value), allocator);
347
38
    case CppType::UINT32:
348
38
        return makeAnyIntegralValue<uint32_t>(std::forward<T>(value), allocator);
349
6
    case CppType::UINT64:
350
6
        return makeAnyIntegralValue<uint64_t>(std::forward<T>(value), allocator);
351
21
    case CppType::INT8:
352
21
        return makeAnyIntegralValue<int8_t>(std::forward<T>(value), allocator);
353
7
    case CppType::INT16:
354
7
        return makeAnyIntegralValue<int16_t>(std::forward<T>(value), allocator);
355
7
    case CppType::INT32:
356
7
        return makeAnyIntegralValue<int32_t>(std::forward<T>(value), allocator);
357
6
    case CppType::INT64:
358
6
        return makeAnyIntegralValue<int64_t>(std::forward<T>(value), allocator);
359
6
    case CppType::FLOAT:
360
6
        return makeAnyFloatingValue<float>(std::forward<T>(value), allocator);
361
6
    case CppType::DOUBLE:
362
6
        return makeAnyFloatingValue<double>(std::forward<T>(value), allocator);
363
73
    case CppType::STRING:
364
73
        return makeAnyStringValue(std::forward<T>(value), allocator);
365
37
    case CppType::ENUM:
366
37
        return makeAnyEnumValue(std::forward<T>(value), typeInfo, allocator);
367
44
    case CppType::BITMASK:
368
44
        return makeAnyBitmaskValue(std::forward<T>(value), typeInfo, allocator);
369
5
    default:
370
5
        return AnyHolder<ALLOC>(std::forward<T>(value), allocator);
371
309
    }
372
309
}
373
374
// overload for values which are already in AnyHolder
375
template <typename ALLOC>
376
AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>&, AnyHolder<ALLOC>&& anyValue, const ALLOC&)
377
21
{
378
21
    return std::move(anyValue);
379
21
}
380
381
enum class CreatorState : uint8_t
382
{
383
    BEFORE_ROOT,
384
    IN_COMPOUND,
385
    IN_ARRAY
386
};
387
388
} // namespace detail
389
390
/**
391
 * Allows to append detail::CreatorState to CppRuntimeException.
392
 *
393
 * \param exception Exception to modify.
394
 * \param state Creator state to append.
395
 *
396
 * \return Reference to the exception to allow operator chaining.
397
 */
398
CppRuntimeException& operator<<(CppRuntimeException& exception, detail::CreatorState state);
399
400
/**
401
 * Allows to build zserio object tree defined by the given type info.
402
 */
403
template <typename ALLOC>
404
class BasicZserioTreeCreator : AllocatorHolder<ALLOC>
405
{
406
public:
407
    /**
408
     * Constructor.
409
     *
410
     * \param typeInfo Type info defining the tree.
411
     */
412
    explicit BasicZserioTreeCreator(const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator = ALLOC());
413
414
    /**
415
     * Creates the top level compound element and move to state of building its children.
416
     */
417
    void beginRoot();
418
419
    /**
420
     * Finishes building and returns the created tree.
421
     *
422
     * \return Zserio object tree.
423
     *
424
     * \throw CppRuntimeException When the creator is not in state of building the root object.
425
     */
426
    IBasicReflectablePtr<ALLOC> endRoot();
427
428
    /**
429
     * Creates an array field within the current compound.
430
     *
431
     * \param name Name of the array field.
432
     *
433
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
434
     */
435
    void beginArray(const string<ALLOC>& name);
436
437
    /**
438
     * Finishes the array field.
439
     *
440
     * \throw CppRuntimeException When the creator is not in an array field.
441
     */
442
    void endArray();
443
444
    /**
445
     * Creates a compound field within the current compound.
446
     *
447
     * \param name Name of the compound field.
448
     *
449
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
450
     */
451
    void beginCompound(const string<ALLOC>& name);
452
453
    /**
454
     * Finishes the compound.
455
     *
456
     * \throw CppRuntimeException When the creator is not in a compound.
457
     */
458
    void endCompound();
459
460
    /**
461
     * Sets field value within the current compound.
462
     *
463
     * \param name Name of the field.
464
     * \param value Value to set.
465
     *
466
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
467
     */
468
    template <typename T>
469
    void setValue(const string<ALLOC>& name, T&& value);
470
471
    /**
472
     * Overload for setting of a null value.
473
     *
474
     * Note that this does nothing in C++ since non-optional fields are always present (default initialized).
475
     *
476
     * \param name Name of the field.
477
     * \param nullValue Null value.
478
     *
479
     * \throw CppRuntimeException When the field doesn't exist or when the creator is not in a compound.
480
     */
481
    void setValue(const string<ALLOC>& name, std::nullptr_t nullValue);
482
483
    /**
484
     * Gets type info of the expected field.
485
     *
486
     * \param name Field name.
487
     *
488
     * \return Type info of the expected field.
489
     *
490
     * \throw CppRuntimeException When the creator is not in a compound.
491
     */
492
    const IBasicTypeInfo<ALLOC>& getFieldType(const string<ALLOC>& name) const;
493
494
    /**
495
     * Creates compound array element within the current array.
496
     *
497
     * \throw CppRuntimeException When the creator is not in an array of compounds.
498
     */
499
    void beginCompoundElement();
500
501
    /**
502
     * Finishes the compound element.
503
     *
504
     * \throw CppRuntimeException When the creator is not in a compound element.
505
     */
506
    void endCompoundElement();
507
508
    /**
509
     * Adds the value to the array.
510
     *
511
     * \param value Value to add.
512
     *
513
     * \throw CppRuntimeException When the creator is not in an array of simple values.
514
     */
515
    template <typename T>
516
    void addValueElement(T&& value);
517
518
    /**
519
     * Gets type info of the expected array element.
520
     *
521
     * \return Type info of the expected array element.
522
     *
523
     * \throw CppRuntimeException When the creator is not in an array.
524
     */
525
    const IBasicTypeInfo<ALLOC>& getElementType() const;
526
527
private:
528
    using AllocatorHolder<ALLOC>::get_allocator;
529
530
    const IBasicTypeInfo<ALLOC>& getTypeInfo() const;
531
    const BasicFieldInfo<ALLOC>& findFieldInfo(const IBasicTypeInfo<ALLOC>& typeInfo, StringView name) const;
532
533
    template <typename T>
534
    AnyHolder<ALLOC> makeAnyValue(const IBasicTypeInfo<ALLOC>& typeInfo, T&& value) const;
535
536
    const IBasicTypeInfo<ALLOC>& m_typeInfo;
537
    vector<std::reference_wrapper<const BasicFieldInfo<ALLOC>>, ALLOC> m_fieldInfoStack;
538
    vector<IBasicReflectablePtr<ALLOC>, ALLOC> m_valueStack;
539
    detail::CreatorState m_state = detail::CreatorState::BEFORE_ROOT;
540
};
541
542
/** Typedef provided for convenience - using default std::allocator<uint8_t>. */
543
using ZserioTreeCreator = BasicZserioTreeCreator<std::allocator<uint8_t>>;
544
545
template <typename ALLOC>
546
BasicZserioTreeCreator<ALLOC>::BasicZserioTreeCreator(
547
        const IBasicTypeInfo<ALLOC>& typeInfo, const ALLOC& allocator) :
548
        AllocatorHolder<ALLOC>(allocator),
549
        m_typeInfo(typeInfo),
550
        m_fieldInfoStack(allocator),
551
        m_valueStack(allocator)
552
87
{}
553
554
template <typename ALLOC>
555
void BasicZserioTreeCreator<ALLOC>::beginRoot()
556
89
{
557
89
    if (m_state != detail::CreatorState::BEFORE_ROOT)
558
5
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin root in state '") << m_state << "'!";
559
560
84
    m_valueStack.push_back(m_typeInfo.createInstance(get_allocator()));
561
84
    m_state = detail::CreatorState::IN_COMPOUND;
562
84
}
563
564
template <typename ALLOC>
565
IBasicReflectablePtr<ALLOC> BasicZserioTreeCreator<ALLOC>::endRoot()
566
56
{
567
56
    if (m_state != detail::CreatorState::IN_COMPOUND || 
m_valueStack.size() != 153
)
568
5
        throw CppRuntimeException("ZserioTreeCreator: Cannot end root in state '") << m_state << "'!";
569
570
51
    m_state = detail::CreatorState::BEFORE_ROOT;
571
51
    auto value = m_valueStack.back();
572
51
    m_valueStack.pop_back();
573
51
    return value;
574
56
}
575
576
template <typename ALLOC>
577
void BasicZserioTreeCreator<ALLOC>::beginArray(const string<ALLOC>& name)
578
23
{
579
23
    if (m_state != detail::CreatorState::IN_COMPOUND)
580
3
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin array in state '") << m_state << "'!";
581
582
20
    const auto& parentTypeInfo = getTypeInfo();
583
20
    const auto& fieldInfo = findFieldInfo(parentTypeInfo, name);
584
20
    if (!fieldInfo.isArray)
585
2
    {
586
2
        throw CppRuntimeException("ZserioTreeCreator: Member '")
587
2
                << fieldInfo.schemaName << "' is not an array!";
588
2
    }
589
590
18
    m_fieldInfoStack.push_back(fieldInfo);
591
592
    // note that we cannot just call getField() in case that the array is not optional like we do it in
593
    // setValue() and beginCompound() methods because in case of arrays we would join multiple arrays together
594
    // when this method is called multiple times with the same name - thus we will just create a new array
595
    //
596
    // moreover we need to properly initialize arrays of dynamic bit fields
597
    // see https://github.com/ndsev/zserio/issues/414
598
18
    m_valueStack.push_back(m_valueStack.back()->createField(name));
599
600
18
    m_state = detail::CreatorState::IN_ARRAY;
601
18
}
602
603
template <typename ALLOC>
604
void BasicZserioTreeCreator<ALLOC>::endArray()
605
16
{
606
16
    if (m_state != detail::CreatorState::IN_ARRAY)
607
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot end array in state '") << m_state << "'!";
608
609
12
    m_fieldInfoStack.pop_back();
610
12
    m_valueStack.pop_back();
611
12
    m_state = detail::CreatorState::IN_COMPOUND;
612
12
}
613
614
template <typename ALLOC>
615
void BasicZserioTreeCreator<ALLOC>::beginCompound(const string<ALLOC>& name)
616
46
{
617
46
    if (m_state != detail::CreatorState::IN_COMPOUND)
618
3
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin compound in state '") << m_state << "'!";
619
620
43
    const auto& parentTypeInfo = getTypeInfo();
621
43
    const auto& fieldInfo = findFieldInfo(parentTypeInfo, name);
622
43
    if (fieldInfo.isArray)
623
1
        throw CppRuntimeException("ZserioTreeCreator: Member '") << fieldInfo.schemaName << "' is an array!";
624
625
42
    if (!TypeInfoUtil::isCompound(fieldInfo.typeInfo.getCppType()))
626
1
    {
627
1
        throw CppRuntimeException("ZserioTreeCreator: Member '")
628
1
                << fieldInfo.schemaName << "' is not a compound!";
629
1
    }
630
631
41
    m_fieldInfoStack.push_back(fieldInfo);
632
41
    if (TypeInfoUtil::hasChoice(parentTypeInfo.getCppType()) || 
fieldInfo.isOptional38
)
633
1
    {
634
        // optional field, or field within choice or union -> create the new compound
635
1
        m_valueStack.push_back(m_valueStack.back()->createField(name));
636
1
    }
637
40
    else
638
40
    {
639
40
        m_valueStack.push_back(m_valueStack.back()->getField(name));
640
40
    }
641
642
41
    m_state = detail::CreatorState::IN_COMPOUND;
643
41
}
644
645
template <typename ALLOC>
646
void BasicZserioTreeCreator<ALLOC>::endCompound()
647
19
{
648
19
    if (m_state != detail::CreatorState::IN_COMPOUND || 
m_fieldInfoStack.empty()16
)
649
4
    {
650
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound in state '")
651
4
                << m_state << "'" << (m_fieldInfoStack.empty() ? 
", expecting endRoot!"2
:
"!'"2
);
652
4
    }
653
654
15
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
655
15
    if (fieldInfo.isArray)
656
1
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound, it's an array element!");
657
658
14
    m_fieldInfoStack.pop_back();
659
14
    m_valueStack.pop_back();
660
14
}
661
662
template <typename ALLOC>
663
template <typename T>
664
void BasicZserioTreeCreator<ALLOC>::setValue(const string<ALLOC>& name, T&& value)
665
150
{
666
150
    if (m_state != detail::CreatorState::IN_COMPOUND)
667
3
        throw CppRuntimeException("ZserioTreeCreator: Cannot set value in state '") << m_state << "'!";
668
669
147
    const BasicFieldInfo<ALLOC>& fieldInfo = findFieldInfo(getTypeInfo(), name);
670
147
    if (fieldInfo.isArray)
671
1
    {
672
1
        throw CppRuntimeException("ZserioTreeCreator: Expecting array in member '")
673
1
                << fieldInfo.schemaName << "'!";
674
1
    }
675
676
146
    m_valueStack.back()->setField(
677
146
            fieldInfo.schemaName, makeAnyValue(fieldInfo.typeInfo, std::forward<T>(value)));
678
146
}
679
680
template <typename ALLOC>
681
void BasicZserioTreeCreator<ALLOC>::setValue(const string<ALLOC>& name, std::nullptr_t nullValue)
682
3
{
683
3
    if (m_state != detail::CreatorState::IN_COMPOUND)
684
1
    {
685
1
        throw CppRuntimeException("ZserioTreeCreator: Cannot set value (null) in state '") << m_state << "'!";
686
1
    }
687
688
2
    const BasicFieldInfo<ALLOC>& fieldInfo = findFieldInfo(getTypeInfo(), name);
689
2
    if (fieldInfo.isOptional)
690
1
    {
691
        // reset an optional field
692
1
        m_valueStack.back()->setField(fieldInfo.schemaName, AnyHolder<ALLOC>(nullValue, get_allocator()));
693
1
    }
694
1
    else
695
1
    {
696
        // reset non-optional field with default-constructed value
697
        // (classes generated in C++ do not support null values)
698
1
        m_valueStack.back()->createField(fieldInfo.schemaName);
699
1
    }
700
2
}
701
702
template <typename ALLOC>
703
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getFieldType(const string<ALLOC>& name) const
704
65
{
705
65
    if (m_state != detail::CreatorState::IN_COMPOUND)
706
1
        throw CppRuntimeException("ZserioTreeCreator: Cannot get field type in state '") << m_state << "'!";
707
708
64
    return findFieldInfo(getTypeInfo(), name).typeInfo;
709
65
}
710
711
template <typename ALLOC>
712
void BasicZserioTreeCreator<ALLOC>::beginCompoundElement()
713
9
{
714
9
    if (m_state != detail::CreatorState::IN_ARRAY)
715
4
    {
716
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot begin compound element in state '")
717
4
                << m_state << "'!";
718
4
    }
719
720
5
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
721
5
    if (!TypeInfoUtil::isCompound(fieldInfo.typeInfo.getCppType()))
722
1
    {
723
1
        throw CppRuntimeException("ZserioTreeCreator: Member '")
724
1
                << fieldInfo.schemaName << "' is not a compound!";
725
1
    }
726
727
4
    auto compoundArray = m_valueStack.back();
728
4
    compoundArray->resize(compoundArray->size() + 1);
729
4
    m_valueStack.push_back(compoundArray->at(compoundArray->size() - 1));
730
4
    m_state = detail::CreatorState::IN_COMPOUND;
731
4
}
732
733
template <typename ALLOC>
734
void BasicZserioTreeCreator<ALLOC>::endCompoundElement()
735
8
{
736
8
    if (m_state != detail::CreatorState::IN_COMPOUND || 
m_fieldInfoStack.empty()5
)
737
4
    {
738
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound element in state '")
739
4
                << m_state << (m_fieldInfoStack.empty() ? 
", expecting endRoot!"2
:
"'!"2
);
740
4
    }
741
742
4
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
743
4
    if (!fieldInfo.isArray)
744
1
        throw CppRuntimeException("ZserioTreeCreator: Cannot end compound element, not in array!");
745
746
3
    m_valueStack.pop_back();
747
3
    m_state = detail::CreatorState::IN_ARRAY;
748
3
}
749
750
template <typename ALLOC>
751
template <typename T>
752
void BasicZserioTreeCreator<ALLOC>::addValueElement(T&& value)
753
24
{
754
24
    if (m_state != detail::CreatorState::IN_ARRAY)
755
4
    {
756
4
        throw CppRuntimeException("ZserioTreeCreator: Cannot add value element in state '") << m_state << "'!";
757
4
    }
758
759
20
    const BasicFieldInfo<ALLOC>& fieldInfo = m_fieldInfoStack.back();
760
20
    m_valueStack.back()->append(makeAnyValue(fieldInfo.typeInfo, std::forward<T>(value)));
761
20
}
762
763
template <typename ALLOC>
764
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getElementType() const
765
7
{
766
7
    if (m_state != detail::CreatorState::IN_ARRAY)
767
1
    {
768
1
        throw CppRuntimeException("ZserioTreeCreator: Cannot get element type in state '") << m_state << "'!";
769
1
    }
770
771
6
    return m_fieldInfoStack.back().get().typeInfo;
772
7
}
773
774
template <typename ALLOC>
775
const IBasicTypeInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::getTypeInfo() const
776
276
{
777
276
    return m_fieldInfoStack.empty() ? 
m_typeInfo162
:
m_fieldInfoStack.back().get().typeInfo114
;
778
276
}
779
780
template <typename ALLOC>
781
const BasicFieldInfo<ALLOC>& BasicZserioTreeCreator<ALLOC>::findFieldInfo(
782
        const IBasicTypeInfo<ALLOC>& typeInfo, StringView name) const
783
276
{
784
276
    Span<const BasicFieldInfo<ALLOC>> fields = typeInfo.getFields();
785
828
    auto found_it = std::find_if(fields.begin(), fields.end(), [name](const BasicFieldInfo<ALLOC>& field) {
786
828
        return field.schemaName == name;
787
828
    });
788
276
    if (found_it == fields.end())
789
10
    {
790
10
        throw CppRuntimeException("ZserioTreeCreator: Member '")
791
10
                << name << "' not found in '" << typeInfo.getSchemaName() << "'!";
792
10
    }
793
794
266
    return *found_it;
795
276
}
796
797
template <typename ALLOC>
798
template <typename T>
799
AnyHolder<ALLOC> BasicZserioTreeCreator<ALLOC>::makeAnyValue(
800
        const IBasicTypeInfo<ALLOC>& typeInfo, T&& value) const
801
162
{
802
162
    return detail::makeAnyValue(typeInfo, std::forward<T>(value), get_allocator());
803
162
}
804
805
} // namespace zserio
806
807
#endif // ZSERIO_ZSERIO_TREE_CREATOR_H_INC