Coverage Report

Created: 2025-08-29 06:24

/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
1.98M
           : type_(type), length_(length)
58
1.98M
        {
59
1.98M
        }
60
        
61
        ~stack_item() = default;
62
63
        std::size_t length() const
64
290k
        {
65
290k
            return length_;
66
290k
        }
67
68
        std::size_t count() const
69
290k
        {
70
290k
            return count_;
71
290k
        }
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.96M
        {
80
1.96M
            return type_ == ubjson_container_type::indefinite_length_array || type_ == ubjson_container_type::indefinite_length_object;
81
1.96M
        }
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
237
       : basic_ubjson_encoder(std::forward<Sink>(sink), ubjson_encode_options(), alloc)
100
237
    {
101
237
    }
102
103
    explicit basic_ubjson_encoder(Sink&& sink, 
104
                                  const ubjson_encode_options& options, 
105
                                  const Allocator& alloc = Allocator())
106
237
       : sink_(std::forward<Sink>(sink)),
107
237
         options_(options),
108
237
         alloc_(alloc)
109
237
    {
110
237
    }
111
112
    ~basic_ubjson_encoder() noexcept
113
237
    {
114
237
        JSONCONS_TRY
115
237
        {
116
237
            sink_.flush();
117
237
        }
118
237
        JSONCONS_CATCH(...)
119
237
        {
120
0
        }
121
237
    }
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.72k
    {
148
1.72k
        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.72k
        stack_.emplace_back(ubjson_container_type::indefinite_length_object);
154
1.72k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
155
156
1.72k
        JSONCONS_VISITOR_RETURN;
157
1.72k
    }
158
159
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override
160
50.1k
    {
161
50.1k
        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
50.1k
        stack_.emplace_back(ubjson_container_type::object, length);
167
50.1k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
168
50.1k
        sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
169
50.1k
        put_length(length);
170
171
50.1k
        JSONCONS_VISITOR_RETURN;
172
50.1k
    }
173
174
    JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code& ec) override
175
51.1k
    {
176
51.1k
        JSONCONS_ASSERT(!stack_.empty());
177
51.1k
        --nesting_depth_;
178
179
51.1k
        if (stack_.back().is_indefinite_length())
180
1.40k
        {
181
1.40k
            sink_.push_back(jsoncons::ubjson::ubjson_type::end_object_marker);
182
1.40k
        }
183
49.7k
        else
184
49.7k
        {
185
49.7k
            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.7k
            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.7k
        }
196
51.1k
        stack_.pop_back();
197
51.1k
        end_value();
198
51.1k
        JSONCONS_VISITOR_RETURN;
199
51.1k
    }
200
201
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
202
1.83M
    {
203
1.83M
        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.83M
        stack_.emplace_back(ubjson_container_type::indefinite_length_array);
209
1.83M
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
210
211
1.83M
        JSONCONS_VISITOR_RETURN;
212
1.83M
    }
213
214
    JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override
215
105k
    {
216
105k
        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
105k
        stack_.emplace_back(ubjson_container_type::array, length);
222
105k
        sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
223
105k
        sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
224
105k
        put_length(length);
225
226
105k
        JSONCONS_VISITOR_RETURN;
227
105k
    }
228
229
    JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code& ec) override
230
1.91M
    {
231
1.91M
        JSONCONS_ASSERT(!stack_.empty());
232
1.91M
        --nesting_depth_;
233
234
1.91M
        if (stack_.back().is_indefinite_length())
235
1.82M
        {
236
1.82M
            sink_.push_back(jsoncons::ubjson::ubjson_type::end_array_marker);
237
1.82M
        }
238
95.3k
        else
239
95.3k
        {
240
95.3k
            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
95.3k
            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
95.3k
        }
251
1.91M
        stack_.pop_back();
252
1.91M
        end_value();
253
1.91M
        JSONCONS_VISITOR_RETURN;
254
1.91M
    }
255
256
    JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code& ec) override
257
869k
    {
258
869k
        auto sink = unicode_traits::validate(name.data(), name.size());
259
869k
        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
869k
        put_length(name.length());
266
267
869k
        for (auto c : name)
268
2.47M
        {
269
2.47M
            sink_.push_back(c);
270
2.47M
        }
271
869k
        JSONCONS_VISITOR_RETURN;
272
869k
    }
273
274
    JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) override
275
3.73M
    {
276
        // nil
277
3.73M
        binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::null_type), std::back_inserter(sink_));
278
3.73M
        end_value();
279
3.73M
        JSONCONS_VISITOR_RETURN;
280
3.73M
    }
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.48M
    {
284
1.48M
        switch (tag)
285
1.48M
        {
286
38
            case semantic_tag::bigint:
287
369
            case semantic_tag::bigdec:
288
369
            {
289
369
                sink_.push_back(jsoncons::ubjson::ubjson_type::high_precision_number_type);
290
369
                break;
291
38
            }
292
1.48M
            default:
293
1.48M
            {
294
1.48M
                sink_.push_back(jsoncons::ubjson::ubjson_type::string_type);
295
1.48M
                break;
296
38
            }
297
1.48M
        }
298
299
1.48M
        auto sink = unicode_traits::validate(sv.data(), sv.size());
300
1.48M
        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.48M
        put_length(sv.length());
307
308
1.48M
        for (auto c : sv)
309
2.48M
        {
310
2.48M
            sink_.push_back(c);
311
2.48M
        }
312
313
1.48M
        end_value();
314
1.48M
        JSONCONS_VISITOR_RETURN;
315
1.48M
    }
316
317
    void put_length(std::size_t length)
318
2.51M
    {
319
2.51M
        if (length <= (std::numeric_limits<uint8_t>::max)())
320
2.50M
        {
321
2.50M
            sink_.push_back(ubjson_type::uint8_type);
322
2.50M
            binary::native_to_big(static_cast<uint8_t>(length), std::back_inserter(sink_));
323
2.50M
        }
324
8.88k
        else if (length <= (std::size_t)(std::numeric_limits<int16_t>::max)())
325
8.80k
        {
326
8.80k
            sink_.push_back(ubjson_type::int16_type);
327
8.80k
            binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
328
8.80k
        }
329
79
        else if (length <= (std::size_t)(std::numeric_limits<int32_t>::max)())
330
79
        {
331
79
            sink_.push_back(ubjson_type::int32_type);
332
79
            binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
333
79
        }
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.51M
    }
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
48.8k
    {
372
48.8k
        float valf = (float)val;
373
48.8k
        if ((double)valf == val)
374
37.3k
        {
375
            // float 32
376
37.3k
            sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float32_type));
377
37.3k
            binary::native_to_big(valf,std::back_inserter(sink_));
378
37.3k
        }
379
11.5k
        else
380
11.5k
        {
381
            // float 64
382
11.5k
            sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float64_type));
383
11.5k
            binary::native_to_big(val,std::back_inserter(sink_));
384
11.5k
        }
385
386
        // write double
387
388
48.8k
        end_value();
389
48.8k
        JSONCONS_VISITOR_RETURN;
390
48.8k
    }
391
392
    JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t val, semantic_tag, const ser_context&, 
393
        std::error_code&) override
394
1.21M
    {
395
1.21M
        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
34.6k
            else if (val <= (std::numeric_limits<int32_t>::max)())
410
23.6k
            {
411
                // uint 32 stores a 32-bit big-endian unsigned integer
412
23.6k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
413
23.6k
                binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
414
23.6k
            }
415
10.9k
            else if (val <= (std::numeric_limits<int64_t>::max)())
416
10.9k
            {
417
                // int 64 stores a 64-bit big-endian signed integer
418
10.9k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
419
10.9k
                binary::native_to_big(val,std::back_inserter(sink_));
420
10.9k
            }
421
0
            else
422
0
            {
423
                // big integer
424
0
            }
425
904k
        }
426
308k
        else
427
308k
        {
428
308k
            if (val >= (std::numeric_limits<int8_t>::lowest)())
429
65.7k
            {
430
                // int 8 stores a 8-bit signed integer
431
65.7k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int8_type);
432
65.7k
                binary::native_to_big(static_cast<int8_t>(val),std::back_inserter(sink_));
433
65.7k
            }
434
243k
            else if (val >= (std::numeric_limits<int16_t>::lowest)())
435
176k
            {
436
                // int 16 stores a 16-bit big-endian signed integer
437
176k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
438
176k
                binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
439
176k
            }
440
66.6k
            else if (val >= (std::numeric_limits<int32_t>::lowest)())
441
55.6k
            {
442
                // int 32 stores a 32-bit big-endian signed integer
443
55.6k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
444
55.6k
                binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
445
55.6k
            }
446
10.9k
            else if (val >= (std::numeric_limits<int64_t>::lowest)())
447
10.9k
            {
448
                // int 64 stores a 64-bit big-endian signed integer
449
10.9k
                sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
450
10.9k
                binary::native_to_big(val,std::back_inserter(sink_));
451
10.9k
            }
452
308k
        }
453
1.21M
        end_value();
454
1.21M
        JSONCONS_VISITOR_RETURN;
455
1.21M
    }
456
457
    JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t val, 
458
                      semantic_tag, 
459
                      const ser_context&,
460
                      std::error_code&) override
461
385k
    {
462
385k
        if (val <= (std::numeric_limits<uint8_t>::max)())
463
385k
        {
464
385k
            sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
465
385k
            binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
466
385k
        }
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
385k
        end_value();
483
385k
        JSONCONS_VISITOR_RETURN;
484
385k
    }
485
486
    JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
487
145k
    {
488
        // true and false
489
145k
        sink_.push_back(static_cast<uint8_t>(val ? jsoncons::ubjson::ubjson_type::true_type : jsoncons::ubjson::ubjson_type::false_type));
490
491
145k
        end_value();
492
145k
        JSONCONS_VISITOR_RETURN;
493
145k
    }
494
495
    void end_value()
496
8.98M
    {
497
8.98M
        if (!stack_.empty())
498
8.98M
        {
499
8.98M
            ++stack_.back().count_;
500
8.98M
        }
501
8.98M
    }
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