Coverage Report

Created: 2024-07-18 11:41

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