Coverage Report

Created: 2026-02-14 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jsoncons/include/jsoncons_ext/ubjson/ubjson_encoder.hpp
Line
Count
Source
1
// Copyright 2013-2026 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.46M
           : type_(type), length_(length)
58
2.46M
        {
59
2.46M
        }
60
        
61
        ~stack_item() = default;
62
63
        std::size_t length() const
64
330k
        {
65
330k
            return length_;
66
330k
        }
67
68
        std::size_t count() const
69
330k
        {
70
330k
            return count_;
71
330k
        }
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
2.43M
        {
80
2.43M
            return type_ == ubjson_container_type::indefinite_length_array || type_ == ubjson_container_type::indefinite_length_object;
81
2.43M
        }
82
83
    };
84
85
    Sink sink_;
86
    int max_nesting_depth_;
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
255
       : basic_ubjson_encoder(std::forward<Sink>(sink), ubjson_encode_options(), alloc)
100
255
    {
101
255
    }
102
103
    explicit basic_ubjson_encoder(Sink&& sink, 
104
                                  const ubjson_encode_options& options, 
105
                                  const Allocator& alloc = Allocator())
106
255
       : sink_(std::forward<Sink>(sink)),
107
255
         max_nesting_depth_(options.max_nesting_depth()),
108
255
         alloc_(alloc)
109
255
    {
110
255
    }
111
112
    ~basic_ubjson_encoder() noexcept
113
255
    {
114
255
        JSONCONS_TRY
115
255
        {
116
255
            sink_.flush();
117
255
        }
118
255
        JSONCONS_CATCH(...)
119
255
        {
120
0
        }
121
255
    }
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
16
    {
143
16
        sink_.flush();
144
16
    }
145
146
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
147
1.93k
    {
148
1.93k
        if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_))
149
0
        {
150
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
151
0
            JSONCONS_VISITOR_RETURN;
152
0
        } 
153
1.93k
        stack_.emplace_back(ubjson_container_type::indefinite_length_object);
154
1.93k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
155
156
1.93k
        JSONCONS_VISITOR_RETURN;
157
1.93k
    }
158
159
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override
160
62.1k
    {
161
62.1k
        if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_))
162
0
        {
163
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
164
0
            JSONCONS_VISITOR_RETURN;
165
0
        } 
166
62.1k
        stack_.emplace_back(ubjson_container_type::object, length);
167
62.1k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
168
62.1k
        sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
169
62.1k
        put_length(length);
170
171
62.1k
        JSONCONS_VISITOR_RETURN;
172
62.1k
    }
173
174
    JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code& ec) override
175
63.5k
    {
176
63.5k
        JSONCONS_ASSERT(!stack_.empty());
177
63.5k
        --nesting_depth_;
178
179
63.5k
        if (stack_.back().is_indefinite_length())
180
1.66k
        {
181
1.66k
            sink_.push_back(jsoncons::ubjson::ubjson_type::end_object_marker);
182
1.66k
        }
183
61.8k
        else
184
61.8k
        {
185
61.8k
            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
61.8k
            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
61.8k
        }
196
63.5k
        stack_.pop_back();
197
63.5k
        end_value();
198
63.5k
        JSONCONS_VISITOR_RETURN;
199
63.5k
    }
200
201
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
202
2.28M
    {
203
2.28M
        if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_))
204
0
        {
205
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
206
0
            JSONCONS_VISITOR_RETURN;
207
0
        } 
208
2.28M
        stack_.emplace_back(ubjson_container_type::indefinite_length_array);
209
2.28M
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
210
211
2.28M
        JSONCONS_VISITOR_RETURN;
212
2.28M
    }
213
214
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override
215
116k
    {
216
116k
        if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_))
217
0
        {
218
0
            ec = ubjson_errc::max_nesting_depth_exceeded;
219
0
            JSONCONS_VISITOR_RETURN;
220
0
        } 
221
116k
        stack_.emplace_back(ubjson_container_type::array, length);
222
116k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
223
116k
        sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
224
116k
        put_length(length);
225
226
116k
        JSONCONS_VISITOR_RETURN;
227
116k
    }
228
229
    JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code& ec) override
230
2.37M
    {
231
2.37M
        JSONCONS_ASSERT(!stack_.empty());
232
2.37M
        --nesting_depth_;
233
234
2.37M
        if (stack_.back().is_indefinite_length())
235
2.26M
        {
236
2.26M
            sink_.push_back(jsoncons::ubjson::ubjson_type::end_array_marker);
237
2.26M
        }
238
103k
        else
239
103k
        {
240
103k
            if (stack_.back().count() < stack_.back().length())
241
1
            {
242
1
                ec = ubjson_errc::too_few_items;
243
1
                JSONCONS_VISITOR_RETURN;
244
1
            }
245
103k
            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
103k
        }
251
2.37M
        stack_.pop_back();
252
2.37M
        end_value();
253
2.37M
        JSONCONS_VISITOR_RETURN;
254
2.37M
    }
255
256
    JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code& ec) override
257
825k
    {
258
825k
        auto sink = unicode_traits::validate(name.data(), name.size());
259
825k
        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
825k
        put_length(name.length());
266
267
825k
        for (auto c : name)
268
2.84M
        {
269
2.84M
            sink_.push_back(c);
270
2.84M
        }
271
825k
        JSONCONS_VISITOR_RETURN;
272
825k
    }
273
274
    JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) override
275
10.1M
    {
276
        // nil
277
10.1M
        binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::null_type), std::back_inserter(sink_));
278
10.1M
        end_value();
279
10.1M
        JSONCONS_VISITOR_RETURN;
280
10.1M
    }
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
4.25M
    {
284
4.25M
        switch (tag)
285
4.25M
        {
286
114
            case semantic_tag::bigint:
287
550
            case semantic_tag::bigdec:
288
550
            {
289
550
                sink_.push_back(jsoncons::ubjson::ubjson_type::high_precision_number_type);
290
550
                break;
291
114
            }
292
4.25M
            default:
293
4.25M
            {
294
4.25M
                sink_.push_back(jsoncons::ubjson::ubjson_type::string_type);
295
4.25M
                break;
296
114
            }
297
4.25M
        }
298
299
4.25M
        auto sink = unicode_traits::validate(sv.data(), sv.size());
300
4.25M
        if (sink.ec != unicode_traits::conv_errc())
301
9
        {
302
9
            ec = ubjson_errc::invalid_utf8_text_string;
303
9
            JSONCONS_VISITOR_RETURN;
304
9
        }
305
306
4.25M
        put_length(sv.length());
307
308
4.25M
        for (auto c : sv)
309
6.16M
        {
310
6.16M
            sink_.push_back(c);
311
6.16M
        }
312
313
4.25M
        end_value();
314
4.25M
        JSONCONS_VISITOR_RETURN;
315
4.25M
    }
316
317
    void put_length(std::size_t length)
318
5.26M
    {
319
5.26M
        if (length <= (std::numeric_limits<uint8_t>::max)())
320
5.24M
        {
321
5.24M
            sink_.push_back(ubjson_type::uint8_type);
322
5.24M
            binary::native_to_big(static_cast<uint8_t>(length), std::back_inserter(sink_));
323
5.24M
        }
324
12.1k
        else if (length <= (std::size_t)(std::numeric_limits<int16_t>::max)())
325
11.8k
        {
326
11.8k
            sink_.push_back(ubjson_type::int16_type);
327
11.8k
            binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
328
11.8k
        }
329
229
        else if (length <= (std::size_t)(std::numeric_limits<int32_t>::max)())
330
229
        {
331
229
            sink_.push_back(ubjson_type::int32_type);
332
229
            binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
333
229
        }
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
5.26M
    }
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
37.7k
    {
372
37.7k
        float valf = (float)val;
373
37.7k
        if ((double)valf == val)
374
31.7k
        {
375
            // float 32
376
31.7k
            sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float32_type));
377
31.7k
            binary::native_to_big(valf,std::back_inserter(sink_));
378
31.7k
        }
379
6.01k
        else
380
6.01k
        {
381
            // float 64
382
6.01k
            sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float64_type));
383
6.01k
            binary::native_to_big(val,std::back_inserter(sink_));
384
6.01k
        }
385
386
        // write double
387
388
37.7k
        end_value();
389
37.7k
        JSONCONS_VISITOR_RETURN;
390
37.7k
    }
391
392
    JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t val, semantic_tag, const ser_context&, 
393
        std::error_code&) override
394
1.91M
    {
395
1.91M
        if (val >= 0)
396
1.61M
        {
397
1.61M
            if (val <= (std::numeric_limits<uint8_t>::max)())
398
829k
            {
399
                // uint 8 stores a 8-bit unsigned integer
400
829k
                sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
401
829k
                binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
402
829k
            }
403
781k
            else if (val <= (std::numeric_limits<int16_t>::max)())
404
693k
            {
405
                // uint 16 stores a 16-bit big-endian unsigned integer
406
693k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
407
693k
                binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
408
693k
            }
409
87.8k
            else if (val <= (std::numeric_limits<int32_t>::max)())
410
28.0k
            {
411
                // uint 32 stores a 32-bit big-endian unsigned integer
412
28.0k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
413
28.0k
                binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
414
28.0k
            }
415
59.7k
            else if (val <= (std::numeric_limits<int64_t>::max)())
416
59.7k
            {
417
                // int 64 stores a 64-bit big-endian signed integer
418
59.7k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
419
59.7k
                binary::native_to_big(val,std::back_inserter(sink_));
420
59.7k
            }
421
0
            else
422
0
            {
423
                // big integer
424
0
            }
425
1.61M
        }
426
300k
        else
427
300k
        {
428
300k
            if (val >= (std::numeric_limits<int8_t>::lowest)())
429
67.0k
            {
430
                // int 8 stores a 8-bit signed integer
431
67.0k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int8_type);
432
67.0k
                binary::native_to_big(static_cast<int8_t>(val),std::back_inserter(sink_));
433
67.0k
            }
434
233k
            else if (val >= (std::numeric_limits<int16_t>::lowest)())
435
159k
            {
436
                // int 16 stores a 16-bit big-endian signed integer
437
159k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
438
159k
                binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
439
159k
            }
440
74.5k
            else if (val >= (std::numeric_limits<int32_t>::lowest)())
441
57.5k
            {
442
                // int 32 stores a 32-bit big-endian signed integer
443
57.5k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
444
57.5k
                binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
445
57.5k
            }
446
16.9k
            else if (val >= (std::numeric_limits<int64_t>::lowest)())
447
16.9k
            {
448
                // int 64 stores a 64-bit big-endian signed integer
449
16.9k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
450
16.9k
                binary::native_to_big(val,std::back_inserter(sink_));
451
16.9k
            }
452
300k
        }
453
1.91M
        end_value();
454
1.91M
        JSONCONS_VISITOR_RETURN;
455
1.91M
    }
456
457
    JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t val, 
458
                      semantic_tag, 
459
                      const ser_context&,
460
                      std::error_code&) override
461
296k
    {
462
296k
        if (val <= (std::numeric_limits<uint8_t>::max)())
463
296k
        {
464
296k
            sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
465
296k
            binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
466
296k
        }
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
296k
        end_value();
483
296k
        JSONCONS_VISITOR_RETURN;
484
296k
    }
485
486
    JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
487
155k
    {
488
        // true and false
489
155k
        sink_.push_back(static_cast<uint8_t>(val ? jsoncons::ubjson::ubjson_type::true_type : jsoncons::ubjson::ubjson_type::false_type));
490
491
155k
        end_value();
492
155k
        JSONCONS_VISITOR_RETURN;
493
155k
    }
494
495
    void end_value()
496
19.2M
    {
497
19.2M
        if (!stack_.empty())
498
19.2M
        {
499
19.2M
            ++stack_.back().count_;
500
19.2M
        }
501
19.2M
    }
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