Coverage Report

Created: 2025-08-24 06:56

/src/jsoncons/include/jsoncons_ext/ubjson/ubjson_encoder.hpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2013-2025 Daniel Parker
2
// Distributed under the Boost license, Version 1.0.
3
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
4
5
// See https://github.com/danielaparker/jsoncons for latest version
6
7
#ifndef JSONCONS_EXT_UBJSON_UBJSON_ENCODER_HPP
8
#define JSONCONS_EXT_UBJSON_UBJSON_ENCODER_HPP
9
10
#include <cstddef>
11
#include <cstdint>
12
#include <limits> // std::numeric_limits
13
#include <memory>
14
#include <system_error>
15
#include <utility> // std::move
16
#include <vector>
17
18
#include <jsoncons/config/compiler_support.hpp>
19
#include <jsoncons/config/jsoncons_config.hpp>
20
#include <jsoncons/utility/read_number.hpp>
21
#include <jsoncons/json_exception.hpp>
22
#include <jsoncons/json_type.hpp>
23
#include <jsoncons/json_visitor.hpp>
24
#include <jsoncons/semantic_tag.hpp>
25
#include <jsoncons/ser_util.hpp>
26
#include <jsoncons/sink.hpp>
27
#include <jsoncons/utility/binary.hpp>
28
#include <jsoncons/utility/unicode_traits.hpp>
29
30
#include <jsoncons_ext/ubjson/ubjson_error.hpp>
31
#include <jsoncons_ext/ubjson/ubjson_options.hpp>
32
#include <jsoncons_ext/ubjson/ubjson_type.hpp>
33
34
namespace jsoncons { 
35
namespace ubjson {
36
37
enum class ubjson_container_type {object, indefinite_length_object, array, indefinite_length_array};
38
39
template <typename Sink=jsoncons::binary_stream_sink,typename Allocator=std::allocator<char>>
40
class basic_ubjson_encoder final : public basic_json_visitor<char>
41
{
42
43
    enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 };
44
public:
45
    using allocator_type = Allocator;
46
    using typename basic_json_visitor<char>::string_view_type;
47
    using sink_type = Sink;
48
49
private:
50
    struct stack_item
51
    {
52
        ubjson_container_type type_;
53
        std::size_t length_{0};
54
        std::size_t count_{0};
55
56
        stack_item(ubjson_container_type type, std::size_t length = 0) noexcept
57
2.00M
           : type_(type), length_(length)
58
2.00M
        {
59
2.00M
        }
60
        
61
        ~stack_item() = default;
62
63
        std::size_t length() const
64
287k
        {
65
287k
            return length_;
66
287k
        }
67
68
        std::size_t count() const
69
287k
        {
70
287k
            return count_;
71
287k
        }
72
73
        bool is_object() const
74
        {
75
            return type_ == ubjson_container_type::object || type_ == ubjson_container_type::indefinite_length_object;
76
        }
77
78
        bool is_indefinite_length() const
79
1.98M
        {
80
1.98M
            return type_ == ubjson_container_type::indefinite_length_array || type_ == ubjson_container_type::indefinite_length_object;
81
1.98M
        }
82
83
    };
84
85
    Sink sink_;
86
    const ubjson_encode_options options_;
87
    allocator_type alloc_;
88
89
    std::vector<stack_item> stack_;
90
    int nesting_depth_{0};
91
public:
92
93
    // Noncopyable and nonmoveable
94
    basic_ubjson_encoder(const basic_ubjson_encoder&) = delete;
95
    basic_ubjson_encoder(basic_ubjson_encoder&&) = delete;
96
97
    basic_ubjson_encoder(Sink&& sink, 
98
                         const Allocator& alloc = Allocator())
99
238
       : basic_ubjson_encoder(std::forward<Sink>(sink), ubjson_encode_options(), alloc)
100
238
    {
101
238
    }
102
103
    explicit basic_ubjson_encoder(Sink&& sink, 
104
                                  const ubjson_encode_options& options, 
105
                                  const Allocator& alloc = Allocator())
106
238
       : sink_(std::forward<Sink>(sink)),
107
238
         options_(options),
108
238
         alloc_(alloc)
109
238
    {
110
238
    }
111
112
    ~basic_ubjson_encoder() noexcept
113
238
    {
114
238
        JSONCONS_TRY
115
238
        {
116
238
            sink_.flush();
117
238
        }
118
238
        JSONCONS_CATCH(...)
119
238
        {
120
0
        }
121
238
    }
122
123
    basic_ubjson_encoder& operator=(const basic_ubjson_encoder&) = delete;
124
    basic_ubjson_encoder& operator=(basic_ubjson_encoder&&) = delete;
125
126
    void reset()
127
    {
128
        stack_.clear();
129
        nesting_depth_ = 0;
130
    }
131
132
    void reset(Sink&& sink)
133
    {
134
        sink_ = std::move(sink);
135
        reset();
136
    }
137
138
private:
139
    // Implementing methods
140
141
    void visit_flush() override
142
12
    {
143
12
        sink_.flush();
144
12
    }
145
146
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
147
1.62k
    {
148
1.62k
        if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
149
0
        {
150
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
151
0
            JSONCONS_VISITOR_RETURN;
152
0
        } 
153
1.62k
        stack_.emplace_back(ubjson_container_type::indefinite_length_object);
154
1.62k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
155
156
1.62k
        JSONCONS_VISITOR_RETURN;
157
1.62k
    }
158
159
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override
160
49.9k
    {
161
49.9k
        if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
162
0
        {
163
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
164
0
            JSONCONS_VISITOR_RETURN;
165
0
        } 
166
49.9k
        stack_.emplace_back(ubjson_container_type::object, length);
167
49.9k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
168
49.9k
        sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
169
49.9k
        put_length(length);
170
171
49.9k
        JSONCONS_VISITOR_RETURN;
172
49.9k
    }
173
174
    JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code& ec) override
175
51.0k
    {
176
51.0k
        JSONCONS_ASSERT(!stack_.empty());
177
51.0k
        --nesting_depth_;
178
179
51.0k
        if (stack_.back().is_indefinite_length())
180
1.34k
        {
181
1.34k
            sink_.push_back(jsoncons::ubjson::ubjson_type::end_object_marker);
182
1.34k
        }
183
49.6k
        else
184
49.6k
        {
185
49.6k
            if (stack_.back().count() < stack_.back().length())
186
0
            {
187
0
                ec = ubjson_errc::too_few_items;
188
0
                JSONCONS_VISITOR_RETURN;
189
0
            }
190
49.6k
            if (stack_.back().count() > stack_.back().length())
191
0
            {
192
0
                ec = ubjson_errc::too_many_items;
193
0
                JSONCONS_VISITOR_RETURN;
194
0
            }
195
49.6k
        }
196
51.0k
        stack_.pop_back();
197
51.0k
        end_value();
198
51.0k
        JSONCONS_VISITOR_RETURN;
199
51.0k
    }
200
201
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
202
1.85M
    {
203
1.85M
        if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
204
0
        {
205
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
206
0
            JSONCONS_VISITOR_RETURN;
207
0
        } 
208
1.85M
        stack_.emplace_back(ubjson_container_type::indefinite_length_array);
209
1.85M
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
210
211
1.85M
        JSONCONS_VISITOR_RETURN;
212
1.85M
    }
213
214
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override
215
103k
    {
216
103k
        if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
217
0
        {
218
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
219
0
            JSONCONS_VISITOR_RETURN;
220
0
        } 
221
103k
        stack_.emplace_back(ubjson_container_type::array, length);
222
103k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
223
103k
        sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
224
103k
        put_length(length);
225
226
103k
        JSONCONS_VISITOR_RETURN;
227
103k
    }
228
229
    JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code& ec) override
230
1.93M
    {
231
1.93M
        JSONCONS_ASSERT(!stack_.empty());
232
1.93M
        --nesting_depth_;
233
234
1.93M
        if (stack_.back().is_indefinite_length())
235
1.84M
        {
236
1.84M
            sink_.push_back(jsoncons::ubjson::ubjson_type::end_array_marker);
237
1.84M
        }
238
93.9k
        else
239
93.9k
        {
240
93.9k
            if (stack_.back().count() < stack_.back().length())
241
0
            {
242
0
                ec = ubjson_errc::too_few_items;
243
0
                JSONCONS_VISITOR_RETURN;
244
0
            }
245
93.9k
            if (stack_.back().count() > stack_.back().length())
246
0
            {
247
0
                ec = ubjson_errc::too_many_items;
248
0
                JSONCONS_VISITOR_RETURN;
249
0
            }
250
93.9k
        }
251
1.93M
        stack_.pop_back();
252
1.93M
        end_value();
253
1.93M
        JSONCONS_VISITOR_RETURN;
254
1.93M
    }
255
256
    JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code& ec) override
257
883k
    {
258
883k
        auto sink = unicode_traits::validate(name.data(), name.size());
259
883k
        if (sink.ec != unicode_traits::conv_errc())
260
0
        {
261
0
            ec = ubjson_errc::invalid_utf8_text_string;
262
0
            JSONCONS_VISITOR_RETURN;
263
0
        }
264
265
883k
        put_length(name.length());
266
267
883k
        for (auto c : name)
268
2.48M
        {
269
2.48M
            sink_.push_back(c);
270
2.48M
        }
271
883k
        JSONCONS_VISITOR_RETURN;
272
883k
    }
273
274
    JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) override
275
3.72M
    {
276
        // nil
277
3.72M
        binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::null_type), std::back_inserter(sink_));
278
3.72M
        end_value();
279
3.72M
        JSONCONS_VISITOR_RETURN;
280
3.72M
    }
281
282
    JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override
283
1.62M
    {
284
1.62M
        switch (tag)
285
1.62M
        {
286
38
            case semantic_tag::bigint:
287
351
            case semantic_tag::bigdec:
288
351
            {
289
351
                sink_.push_back(jsoncons::ubjson::ubjson_type::high_precision_number_type);
290
351
                break;
291
38
            }
292
1.62M
            default:
293
1.62M
            {
294
1.62M
                sink_.push_back(jsoncons::ubjson::ubjson_type::string_type);
295
1.62M
                break;
296
38
            }
297
1.62M
        }
298
299
1.62M
        auto sink = unicode_traits::validate(sv.data(), sv.size());
300
1.62M
        if (sink.ec != unicode_traits::conv_errc())
301
11
        {
302
11
            ec = ubjson_errc::invalid_utf8_text_string;
303
11
            JSONCONS_VISITOR_RETURN;
304
11
        }
305
306
1.62M
        put_length(sv.length());
307
308
1.62M
        for (auto c : sv)
309
2.62M
        {
310
2.62M
            sink_.push_back(c);
311
2.62M
        }
312
313
1.62M
        end_value();
314
1.62M
        JSONCONS_VISITOR_RETURN;
315
1.62M
    }
316
317
    void put_length(std::size_t length)
318
2.66M
    {
319
2.66M
        if (length <= (std::numeric_limits<uint8_t>::max)())
320
2.65M
        {
321
2.65M
            sink_.push_back(ubjson_type::uint8_type);
322
2.65M
            binary::native_to_big(static_cast<uint8_t>(length), std::back_inserter(sink_));
323
2.65M
        }
324
8.83k
        else if (length <= (std::size_t)(std::numeric_limits<int16_t>::max)())
325
8.75k
        {
326
8.75k
            sink_.push_back(ubjson_type::int16_type);
327
8.75k
            binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
328
8.75k
        }
329
77
        else if (length <= (std::size_t)(std::numeric_limits<int32_t>::max)())
330
77
        {
331
77
            sink_.push_back(ubjson_type::int32_type);
332
77
            binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
333
77
        }
334
0
        else if (length <= (std::size_t)(std::numeric_limits<int64_t>::max)())
335
0
        {
336
0
            sink_.push_back(ubjson_type::int64_type);
337
0
            binary::native_to_big(static_cast<uint64_t>(length),std::back_inserter(sink_));
338
0
        }
339
0
        else
340
0
        {
341
0
            JSONCONS_THROW(ser_error(ubjson_errc::too_many_items));
342
0
        }
343
2.66M
    }
344
345
    JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, 
346
                              semantic_tag, 
347
                              const ser_context&,
348
                              std::error_code&) override
349
0
    {
350
351
0
        const size_t length = b.size();
352
0
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
353
0
        sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::type_marker));
354
0
        sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::uint8_type));
355
0
        sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
356
0
        put_length(length);
357
358
0
        for (auto c : b)
359
0
        {
360
0
            sink_.push_back(c);
361
0
        }
362
363
0
        end_value();
364
0
        JSONCONS_VISITOR_RETURN;
365
0
    }
366
367
    JSONCONS_VISITOR_RETURN_TYPE visit_double(double val, 
368
                         semantic_tag,
369
                         const ser_context&,
370
                         std::error_code&) override
371
42.9k
    {
372
42.9k
        float valf = (float)val;
373
42.9k
        if ((double)valf == val)
374
31.6k
        {
375
            // float 32
376
31.6k
            sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float32_type));
377
31.6k
            binary::native_to_big(valf,std::back_inserter(sink_));
378
31.6k
        }
379
11.2k
        else
380
11.2k
        {
381
            // float 64
382
11.2k
            sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float64_type));
383
11.2k
            binary::native_to_big(val,std::back_inserter(sink_));
384
11.2k
        }
385
386
        // write double
387
388
42.9k
        end_value();
389
42.9k
        JSONCONS_VISITOR_RETURN;
390
42.9k
    }
391
392
    JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t val, semantic_tag, const ser_context&, 
393
        std::error_code&) override
394
1.20M
    {
395
1.20M
        if (val >= 0)
396
904k
        {
397
904k
            if (val <= (std::numeric_limits<uint8_t>::max)())
398
493k
            {
399
                // uint 8 stores a 8-bit unsigned integer
400
493k
                sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
401
493k
                binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
402
493k
            }
403
410k
            else if (val <= (std::numeric_limits<int16_t>::max)())
404
376k
            {
405
                // uint 16 stores a 16-bit big-endian unsigned integer
406
376k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
407
376k
                binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
408
376k
            }
409
33.7k
            else if (val <= (std::numeric_limits<int32_t>::max)())
410
22.7k
            {
411
                // uint 32 stores a 32-bit big-endian unsigned integer
412
22.7k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
413
22.7k
                binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
414
22.7k
            }
415
11.0k
            else if (val <= (std::numeric_limits<int64_t>::max)())
416
11.0k
            {
417
                // int 64 stores a 64-bit big-endian signed integer
418
11.0k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
419
11.0k
                binary::native_to_big(val,std::back_inserter(sink_));
420
11.0k
            }
421
0
            else
422
0
            {
423
                // big integer
424
0
            }
425
904k
        }
426
304k
        else
427
304k
        {
428
304k
            if (val >= (std::numeric_limits<int8_t>::lowest)())
429
65.6k
            {
430
                // int 8 stores a 8-bit signed integer
431
65.6k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int8_type);
432
65.6k
                binary::native_to_big(static_cast<int8_t>(val),std::back_inserter(sink_));
433
65.6k
            }
434
238k
            else if (val >= (std::numeric_limits<int16_t>::lowest)())
435
172k
            {
436
                // int 16 stores a 16-bit big-endian signed integer
437
172k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
438
172k
                binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
439
172k
            }
440
66.2k
            else if (val >= (std::numeric_limits<int32_t>::lowest)())
441
55.4k
            {
442
                // int 32 stores a 32-bit big-endian signed integer
443
55.4k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
444
55.4k
                binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
445
55.4k
            }
446
10.7k
            else if (val >= (std::numeric_limits<int64_t>::lowest)())
447
10.7k
            {
448
                // int 64 stores a 64-bit big-endian signed integer
449
10.7k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
450
10.7k
                binary::native_to_big(val,std::back_inserter(sink_));
451
10.7k
            }
452
304k
        }
453
1.20M
        end_value();
454
1.20M
        JSONCONS_VISITOR_RETURN;
455
1.20M
    }
456
457
    JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t val, 
458
                      semantic_tag, 
459
                      const ser_context&,
460
                      std::error_code&) override
461
397k
    {
462
397k
        if (val <= (std::numeric_limits<uint8_t>::max)())
463
397k
        {
464
397k
            sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
465
397k
            binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
466
397k
        }
467
0
        else if (val <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
468
0
        {
469
0
            sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
470
0
            binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
471
0
        }
472
0
        else if (val <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
473
0
        {
474
0
            sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
475
0
            binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
476
0
        }
477
0
        else if (val <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
478
0
        {
479
0
            sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
480
0
            binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
481
0
        }
482
397k
        end_value();
483
397k
        JSONCONS_VISITOR_RETURN;
484
397k
    }
485
486
    JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
487
140k
    {
488
        // true and false
489
140k
        sink_.push_back(static_cast<uint8_t>(val ? jsoncons::ubjson::ubjson_type::true_type : jsoncons::ubjson::ubjson_type::false_type));
490
491
140k
        end_value();
492
140k
        JSONCONS_VISITOR_RETURN;
493
140k
    }
494
495
    void end_value()
496
9.12M
    {
497
9.12M
        if (!stack_.empty())
498
9.12M
        {
499
9.12M
            ++stack_.back().count_;
500
9.12M
        }
501
9.12M
    }
502
};
503
504
using ubjson_stream_encoder = basic_ubjson_encoder<jsoncons::binary_stream_sink>;
505
using ubjson_bytes_encoder = basic_ubjson_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>;
506
507
} // namespace ubjson
508
} // namespace jsoncons
509
510
#endif // JSONCONS_EXT_UBJSON_UBJSON_ENCODER_HPP