GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/zserio/JsonDecoder.h Lines: 164 164 100.0 %
Date: 2023-12-13 14:51:09 Branches: 196 484 40.5 %

Line Branch Exec Source
1
#ifndef ZSERIO_JSON_DECODER_H_INC
2
#define ZSERIO_JSON_DECODER_H_INC
3
4
#include <utility>
5
#include <cerrno>
6
#include <cmath>
7
#include <cstdlib>
8
#include <cstring>
9
10
#include "zserio/AllocatorHolder.h"
11
#include "zserio/AnyHolder.h"
12
#include "zserio/CppRuntimeException.h"
13
#include "zserio/String.h"
14
#include "zserio/StringView.h"
15
16
namespace zserio
17
{
18
19
/**
20
 * JSON value decoder.
21
 */
22
template <typename ALLOC = std::allocator<uint8_t>>
23
89
class BasicJsonDecoder : public AllocatorHolder<ALLOC>
24
{
25
public:
26
    using AllocatorHolder<ALLOC>::get_allocator;
27
28
    /**
29
     * Decoder result value.
30
     */
31
49010
    struct DecoderResult
32
    {
33
        /**
34
         * Constructor used for decoder failure.
35
         *
36
         * \param numRead Number of processed characters.
37
         * \param allocator Allocator to use.
38
         */
39
132
        DecoderResult(size_t numRead, const ALLOC& allocator) :
40
132
                numReadChars(numRead), value(allocator), integerOverflow(false)
41
132
        {}
42
43
        /**
44
         * Constructor for decoder success.
45
         *
46
         * \param numRead Number of processed characters.
47
         * \param decodedValue Value decoded from JSON stream.
48
         * \param allocator Allocator to use.
49
         */
50
        template <typename T>
51
20355
        DecoderResult(size_t numRead, T&& decodedValue, const ALLOC& allocator) :
52
20355
                numReadChars(numRead), value(std::forward<T>(decodedValue), allocator), integerOverflow(false)
53
20355
        {}
54
55
        /**
56
         * Constructor used for integer decoder.
57
         *
58
         * \param numRead Number of processed characters.
59
         * \param decodedValue Value decoded from JSON stream.
60
         * \param overflow True in case of integer overflow.
61
         * \param allocator Allocator to use.
62
         */
63
        template <typename T>
64
4107
        DecoderResult(size_t numRead, T&& decodedValue, bool overflow, const ALLOC& allocator) :
65
                numReadChars(numRead), value(createValue(decodedValue, overflow, allocator)),
66
4107
                        integerOverflow(overflow)
67
4107
        {}
68
69
        size_t numReadChars; /**< Number of processed characters. */
70
        AnyHolder<ALLOC> value; /**< Decoded value. Empty on failure. */
71
        bool integerOverflow; /**< True if decoded value was bigger than UINT64_MAX or was not in interval
72
                                   <INT64_MIN, INT64_MAX>. */
73
74
    private:
75
        template <typename T>
76
4107
        AnyHolder<ALLOC> createValue(T&& decodedValue, bool overflow, const ALLOC& allocator)
77
        {
78
            return overflow ? AnyHolder<ALLOC>(allocator) :
79


4107
                    AnyHolder<ALLOC>(std::forward<T>(decodedValue), allocator);
80
        }
81
    };
82
83
    /**
84
     * Empty constructor.
85
     */
86
12
    BasicJsonDecoder() :
87
12
            AllocatorHolder<ALLOC>(ALLOC())
88
12
    {}
89
90
    /**
91
     * Constructor from given allocator.
92
     *
93
     * \param allocator Allocator to use.
94
     */
95
91
    explicit BasicJsonDecoder(const ALLOC& allocator) :
96
91
            AllocatorHolder<ALLOC>(allocator)
97
91
    {}
98
99
    /**
100
     * Decodes the JSON value from the input.
101
     *
102
     * \param input Input to decode from.
103
     *
104
     * \return Decoder result.
105
     */
106
24503
    DecoderResult decodeValue(StringView input)
107
    {
108

24503
        if (input.empty())
109

1
            return DecoderResult(0, get_allocator());
110
111




24502
        switch (input[0])
112
        {
113
        case 'n':
114

9
            return decodeLiteral(input, "null"_sv, nullptr);
115
        case 't':
116

7
            return decodeLiteral(input, "true"_sv, true);
117
        case 'f':
118

4
            return decodeLiteral(input, "false"_sv, false);
119
        case 'N':
120

4
            return decodeLiteral(input, "NaN"_sv, static_cast<double>(NAN));
121
        case 'I':
122

4
            return decodeLiteral(input, "Infinity"_sv, static_cast<double>(INFINITY));
123
        case '"':
124
16331
            return decodeString(input);
125
        case '-':
126



20
            if (input.size() > 1 && input[1] == 'I')
127

5
                return decodeLiteral(input, "-Infinity"_sv, -static_cast<double>(INFINITY));
128
15
            return decodeNumber(input);
129
        default:
130
8123
            return decodeNumber(input);
131
        }
132
    }
133
134
private:
135
    template <typename T>
136
    DecoderResult decodeLiteral(StringView input, StringView literal, T&& value);
137
    DecoderResult decodeString(StringView input);
138
    static bool decodeUnicodeEscape(StringView input, StringView::const_iterator& inputIt,
139
            string<ALLOC>& value);
140
    static char decodeHex(char ch);
141
    size_t checkNumber(StringView input, bool& isDouble, bool& isSigned);
142
    DecoderResult decodeNumber(StringView input);
143
    DecoderResult decodeSigned(StringView input);
144
    DecoderResult decodeUnsigned(StringView input);
145
    DecoderResult decodeDouble(StringView input, size_t numChars);
146
};
147
148
template <typename ALLOC>
149
template <typename T>
150
33
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeLiteral(
151
        StringView input, StringView literal, T&& value)
152
{
153
33
    StringView::const_iterator literalIt = literal.begin();
154
33
    StringView::const_iterator inputIt = input.begin();
155









315
    while (inputIt != input.end() && literalIt != literal.end())
156
    {
157



148
        if (*inputIt++ != *literalIt++)
158
        {
159
            // failure, not decoded
160




7
            return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
161
        }
162
    }
163
164



26
    if (literalIt != literal.end())
165
    {
166
        // short input, not decoded
167




6
        return DecoderResult(input.size(), get_allocator());
168
    }
169
170
    // success
171




20
    return DecoderResult(literal.size(), std::forward<T>(value), get_allocator());
172
}
173
174
template <typename ALLOC>
175
16331
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeString(StringView input)
176
{
177
16331
    StringView::const_iterator inputIt = input.begin() + 1; // we know that at the beginning is '"'
178


32662
    string<ALLOC> value(get_allocator());
179
180

149493
    while (inputIt != input.end())
181
    {
182

82910
        if (*inputIt == '\\')
183
        {
184
24
            ++inputIt;
185

24
            if (inputIt == input.end())
186
            {
187
                // wrong escape, not decoded
188


1
                return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
189
            }
190
191
23
            char nextChar = *inputIt;
192




23
            switch (nextChar)
193
            {
194
            case '\\':
195
            case '"':
196

4
                value.push_back(nextChar);
197
4
                ++inputIt;
198
4
                break;
199
            case 'b':
200

1
                value.push_back('\b');
201
1
                ++inputIt;
202
1
                break;
203
            case 'f':
204

1
                value.push_back('\f');
205
1
                ++inputIt;
206
1
                break;
207
            case 'n':
208

2
                value.push_back('\n');
209
2
                ++inputIt;
210
2
                break;
211
            case 'r':
212

1
                value.push_back('\r');
213
1
                ++inputIt;
214
1
                break;
215
            case 't':
216

2
                value.push_back('\t');
217
2
                ++inputIt;
218
2
                break;
219
            case 'u': // unicode escape
220
                {
221
11
                    ++inputIt;
222


11
                    if (!decodeUnicodeEscape(input, inputIt, value))
223
                    {
224
                        // unsupported unicode escape, not decoded
225


10
                        return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
226
                    }
227
1
                    break;
228
                }
229
            default:
230
1
                ++inputIt;
231
                // unknown escape, not decoded
232


1
                return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
233
            }
234
        }
235

82886
        else if (*inputIt == '"')
236
        {
237
16317
            ++inputIt;
238
            // successfully decoded
239
32634
            return DecoderResult(static_cast<size_t>(inputIt - input.begin()), std::move(value),
240


48951
                    get_allocator());
241
        }
242
        else
243
        {
244

66569
            value.push_back(*inputIt++);
245
        }
246
    }
247
248
    // unterminated string, not decoded
249


2
    return DecoderResult(input.size(), get_allocator());
250
}
251
252
template <typename ALLOC>
253
11
bool BasicJsonDecoder<ALLOC>::decodeUnicodeEscape(StringView input, StringView::const_iterator& inputIt,
254
        string<ALLOC>& value)
255
{
256
    // TODO[Mi-L@]: Simplified just to decode what zserio encodes, for complex solution we could use
257
    //              std::wstring_convert but it's deprecated in C++17.
258



11
    if (inputIt == input.end() || *inputIt++ != '0')
259
2
        return false;
260



9
    if (inputIt == input.end() || *inputIt++ != '0')
261
2
        return false;
262
263

7
    if (inputIt == input.end())
264
1
        return false;
265
6
    const char ch1 = decodeHex(*inputIt++);
266

6
    if (ch1 == -1)
267
2
        return false;
268
269

4
    if (inputIt == input.end())
270
1
        return false;
271
3
    const char ch2 = decodeHex(*inputIt++);
272

3
    if (ch2 == -1)
273
2
        return false;
274
275
1
    value.push_back(static_cast<char>((static_cast<uint32_t>(ch1) << 4U) | static_cast<uint32_t>(ch2)));
276
1
    return true;
277
}
278
279
template <typename ALLOC>
280
9
char BasicJsonDecoder<ALLOC>::decodeHex(char ch)
281
{
282


9
    if (ch >= '0' && ch <= '9')
283
3
        return static_cast<char>(ch - '0');
284


6
    else if (ch >= 'a' && ch <= 'f')
285
1
        return static_cast<char>(ch - 'a' + 10);
286


5
    else if (ch >= 'A' && ch <= 'F')
287
1
        return static_cast<char>(ch - 'A' + 10);
288
289
4
    return -1;
290
}
291
292
template <typename ALLOC>
293
8138
size_t BasicJsonDecoder<ALLOC>::checkNumber(StringView input, bool& isDouble, bool& isSigned)
294
{
295
8138
    StringView::const_iterator inputIt = input.begin();
296
8138
    bool acceptExpSign = false;
297
8138
    isDouble = false;
298
299

8138
    if (*inputIt == '-') // we know that at the beginning is at least one character
300
    {
301
15
        ++inputIt;
302
15
        isSigned = true;
303
    }
304
    else
305
    {
306
8123
        isSigned = false;
307
    }
308
309

105006
    while (inputIt != input.end())
310
    {
311

56544
        if (acceptExpSign)
312
        {
313
4013
            acceptExpSign = false;
314


4013
            if (*inputIt == '+' || *inputIt == '-')
315
            {
316
11
                ++inputIt;
317
11
                continue;
318
            }
319
        }
320


56533
        if (*inputIt >= '0' && *inputIt <= '9')
321
        {
322
44399
            ++inputIt;
323
44399
            continue;
324
        }
325




12134
        if (!isDouble && (*inputIt == '.' || *inputIt == 'e' || *inputIt == 'E'))
326
        {
327
4024
            isDouble = true;
328


4024
            if (*inputIt == 'e' || *inputIt == 'E')
329
4015
                acceptExpSign = true;
330
4024
            ++inputIt;
331
4024
            continue;
332
        }
333
334
8110
        break; // end of a number
335
    }
336
337
8138
    const size_t numberLen = static_cast<size_t>(inputIt - input.begin());
338


8138
    if (isSigned && numberLen == 1)
339
3
        return 0; // single minus is not a number
340
341
8135
    return numberLen;
342
}
343
344
template <typename ALLOC>
345
8138
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeNumber(StringView input)
346
{
347
8138
    bool isDouble = false;
348
8138
    bool isSigned = false;
349
8138
    const size_t numChars = checkNumber(input, isDouble, isSigned);
350

8138
    if (numChars == 0)
351


7
        return DecoderResult(1, get_allocator());
352
353
    // for decodeSigned and decodeUnsigned, we know that all numChars will be processed because checkNumber
354
    // already checked this
355

8131
    if (isDouble)
356

4024
        return decodeDouble(input, numChars);
357

4107
    else if (isSigned)
358

7
        return decodeSigned(input);
359
    else
360

4100
        return decodeUnsigned(input);
361
}
362
363
template <typename ALLOC>
364
7
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeSigned(StringView input)
365
{
366
7
    char* pEnd = nullptr;
367
7
    errno = 0; // no library function sets its value back to zero once changed
368
7
    const int64_t value = std::strtoll(input.begin(), &pEnd, 10);
369
370
7
    const bool overflow = (errno == ERANGE);
371
372


7
    return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator());
373
}
374
375
template <typename ALLOC>
376
4100
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeUnsigned(StringView input)
377
{
378
4100
    char* pEnd = nullptr;
379
4100
    errno = 0; // no library function sets its value back to zero once changed
380
4100
    const uint64_t value = std::strtoull(input.begin(), &pEnd, 10);
381
382
4100
    const bool overflow = (errno == ERANGE);
383
384


4100
    return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator());
385
}
386
387
template <typename ALLOC>
388
4024
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeDouble(
389
        StringView input, size_t numChars)
390
{
391
4024
    char* pEnd = nullptr;
392
4024
    const double value = std::strtod(input.begin(), &pEnd);
393

4024
    if (static_cast<size_t>(pEnd - input.begin()) != numChars)
394


6
        return DecoderResult(numChars, get_allocator());
395
396


4018
    return DecoderResult(numChars, value, get_allocator());
397
}
398
399
} // namespace zserio
400
401
#endif // ZSERIO_JSON_DECODER_H_INC