Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : #include <algorithm> // reverse
4 : : #include <array> // array
5 : : #include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
6 : : #include <cstring> // memcpy
7 : : #include <limits> // numeric_limits
8 : : #include <string> // string
9 : :
10 : : #include <nlohmann/detail/input/binary_reader.hpp>
11 : : #include <nlohmann/detail/output/output_adapters.hpp>
12 : :
13 : : namespace nlohmann
14 : : {
15 : : namespace detail
16 : : {
17 : : ///////////////////
18 : : // binary writer //
19 : : ///////////////////
20 : :
21 : : /*!
22 : : @brief serialization to CBOR and MessagePack values
23 : : */
24 : : template<typename BasicJsonType, typename CharType>
25 : : class binary_writer
26 : : {
27 : : using string_t = typename BasicJsonType::string_t;
28 : :
29 : : public:
30 : : /*!
31 : : @brief create a binary writer
32 : :
33 : : @param[in] adapter output adapter to write to
34 : : */
35 : : explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
36 : : {
37 : : assert(oa);
38 : : }
39 : :
40 : : /*!
41 : : @param[in] j JSON value to serialize
42 : : @pre j.type() == value_t::object
43 : : */
44 : : void write_bson(const BasicJsonType& j)
45 : : {
46 : : switch (j.type())
47 : : {
48 : : case value_t::object:
49 : : {
50 : : write_bson_object(*j.m_value.object);
51 : : break;
52 : : }
53 : :
54 : : default:
55 : : {
56 : : JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name())));
57 : : }
58 : : }
59 : : }
60 : :
61 : : /*!
62 : : @param[in] j JSON value to serialize
63 : : */
64 : : void write_cbor(const BasicJsonType& j)
65 : : {
66 : : switch (j.type())
67 : : {
68 : : case value_t::null:
69 : : {
70 : : oa->write_character(to_char_type(0xF6));
71 : : break;
72 : : }
73 : :
74 : : case value_t::boolean:
75 : : {
76 : : oa->write_character(j.m_value.boolean
77 : : ? to_char_type(0xF5)
78 : : : to_char_type(0xF4));
79 : : break;
80 : : }
81 : :
82 : : case value_t::number_integer:
83 : : {
84 : : if (j.m_value.number_integer >= 0)
85 : : {
86 : : // CBOR does not differentiate between positive signed
87 : : // integers and unsigned integers. Therefore, we used the
88 : : // code from the value_t::number_unsigned case here.
89 : : if (j.m_value.number_integer <= 0x17)
90 : : {
91 : : write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
92 : : }
93 : : else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
94 : : {
95 : : oa->write_character(to_char_type(0x18));
96 : : write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
97 : : }
98 : : else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
99 : : {
100 : : oa->write_character(to_char_type(0x19));
101 : : write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
102 : : }
103 : : else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
104 : : {
105 : : oa->write_character(to_char_type(0x1A));
106 : : write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
107 : : }
108 : : else
109 : : {
110 : : oa->write_character(to_char_type(0x1B));
111 : : write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
112 : : }
113 : : }
114 : : else
115 : : {
116 : : // The conversions below encode the sign in the first
117 : : // byte, and the value is converted to a positive number.
118 : : const auto positive_number = -1 - j.m_value.number_integer;
119 : : if (j.m_value.number_integer >= -24)
120 : : {
121 : : write_number(static_cast<std::uint8_t>(0x20 + positive_number));
122 : : }
123 : : else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
124 : : {
125 : : oa->write_character(to_char_type(0x38));
126 : : write_number(static_cast<std::uint8_t>(positive_number));
127 : : }
128 : : else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
129 : : {
130 : : oa->write_character(to_char_type(0x39));
131 : : write_number(static_cast<std::uint16_t>(positive_number));
132 : : }
133 : : else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
134 : : {
135 : : oa->write_character(to_char_type(0x3A));
136 : : write_number(static_cast<std::uint32_t>(positive_number));
137 : : }
138 : : else
139 : : {
140 : : oa->write_character(to_char_type(0x3B));
141 : : write_number(static_cast<std::uint64_t>(positive_number));
142 : : }
143 : : }
144 : : break;
145 : : }
146 : :
147 : : case value_t::number_unsigned:
148 : : {
149 : : if (j.m_value.number_unsigned <= 0x17)
150 : : {
151 : : write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
152 : : }
153 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
154 : : {
155 : : oa->write_character(to_char_type(0x18));
156 : : write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
157 : : }
158 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
159 : : {
160 : : oa->write_character(to_char_type(0x19));
161 : : write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));
162 : : }
163 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
164 : : {
165 : : oa->write_character(to_char_type(0x1A));
166 : : write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));
167 : : }
168 : : else
169 : : {
170 : : oa->write_character(to_char_type(0x1B));
171 : : write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));
172 : : }
173 : : break;
174 : : }
175 : :
176 : : case value_t::number_float:
177 : : {
178 : : oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
179 : : write_number(j.m_value.number_float);
180 : : break;
181 : : }
182 : :
183 : : case value_t::string:
184 : : {
185 : : // step 1: write control byte and the string length
186 : : const auto N = j.m_value.string->size();
187 : : if (N <= 0x17)
188 : : {
189 : : write_number(static_cast<std::uint8_t>(0x60 + N));
190 : : }
191 : : else if (N <= (std::numeric_limits<std::uint8_t>::max)())
192 : : {
193 : : oa->write_character(to_char_type(0x78));
194 : : write_number(static_cast<std::uint8_t>(N));
195 : : }
196 : : else if (N <= (std::numeric_limits<std::uint16_t>::max)())
197 : : {
198 : : oa->write_character(to_char_type(0x79));
199 : : write_number(static_cast<std::uint16_t>(N));
200 : : }
201 : : else if (N <= (std::numeric_limits<std::uint32_t>::max)())
202 : : {
203 : : oa->write_character(to_char_type(0x7A));
204 : : write_number(static_cast<std::uint32_t>(N));
205 : : }
206 : : // LCOV_EXCL_START
207 : : else if (N <= (std::numeric_limits<std::uint64_t>::max)())
208 : : {
209 : : oa->write_character(to_char_type(0x7B));
210 : : write_number(static_cast<std::uint64_t>(N));
211 : : }
212 : : // LCOV_EXCL_STOP
213 : :
214 : : // step 2: write the string
215 : : oa->write_characters(
216 : : reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
217 : : j.m_value.string->size());
218 : : break;
219 : : }
220 : :
221 : : case value_t::array:
222 : : {
223 : : // step 1: write control byte and the array size
224 : : const auto N = j.m_value.array->size();
225 : : if (N <= 0x17)
226 : : {
227 : : write_number(static_cast<std::uint8_t>(0x80 + N));
228 : : }
229 : : else if (N <= (std::numeric_limits<std::uint8_t>::max)())
230 : : {
231 : : oa->write_character(to_char_type(0x98));
232 : : write_number(static_cast<std::uint8_t>(N));
233 : : }
234 : : else if (N <= (std::numeric_limits<std::uint16_t>::max)())
235 : : {
236 : : oa->write_character(to_char_type(0x99));
237 : : write_number(static_cast<std::uint16_t>(N));
238 : : }
239 : : else if (N <= (std::numeric_limits<std::uint32_t>::max)())
240 : : {
241 : : oa->write_character(to_char_type(0x9A));
242 : : write_number(static_cast<std::uint32_t>(N));
243 : : }
244 : : // LCOV_EXCL_START
245 : : else if (N <= (std::numeric_limits<std::uint64_t>::max)())
246 : : {
247 : : oa->write_character(to_char_type(0x9B));
248 : : write_number(static_cast<std::uint64_t>(N));
249 : : }
250 : : // LCOV_EXCL_STOP
251 : :
252 : : // step 2: write each element
253 : : for (const auto& el : *j.m_value.array)
254 : : {
255 : : write_cbor(el);
256 : : }
257 : : break;
258 : : }
259 : :
260 : : case value_t::object:
261 : : {
262 : : // step 1: write control byte and the object size
263 : : const auto N = j.m_value.object->size();
264 : : if (N <= 0x17)
265 : : {
266 : : write_number(static_cast<std::uint8_t>(0xA0 + N));
267 : : }
268 : : else if (N <= (std::numeric_limits<std::uint8_t>::max)())
269 : : {
270 : : oa->write_character(to_char_type(0xB8));
271 : : write_number(static_cast<std::uint8_t>(N));
272 : : }
273 : : else if (N <= (std::numeric_limits<std::uint16_t>::max)())
274 : : {
275 : : oa->write_character(to_char_type(0xB9));
276 : : write_number(static_cast<std::uint16_t>(N));
277 : : }
278 : : else if (N <= (std::numeric_limits<std::uint32_t>::max)())
279 : : {
280 : : oa->write_character(to_char_type(0xBA));
281 : : write_number(static_cast<std::uint32_t>(N));
282 : : }
283 : : // LCOV_EXCL_START
284 : : else if (N <= (std::numeric_limits<std::uint64_t>::max)())
285 : : {
286 : : oa->write_character(to_char_type(0xBB));
287 : : write_number(static_cast<std::uint64_t>(N));
288 : : }
289 : : // LCOV_EXCL_STOP
290 : :
291 : : // step 2: write each element
292 : : for (const auto& el : *j.m_value.object)
293 : : {
294 : : write_cbor(el.first);
295 : : write_cbor(el.second);
296 : : }
297 : : break;
298 : : }
299 : :
300 : : default:
301 : : break;
302 : : }
303 : : }
304 : :
305 : : /*!
306 : : @param[in] j JSON value to serialize
307 : : */
308 : : void write_msgpack(const BasicJsonType& j)
309 : : {
310 : : switch (j.type())
311 : : {
312 : : case value_t::null: // nil
313 : : {
314 : : oa->write_character(to_char_type(0xC0));
315 : : break;
316 : : }
317 : :
318 : : case value_t::boolean: // true and false
319 : : {
320 : : oa->write_character(j.m_value.boolean
321 : : ? to_char_type(0xC3)
322 : : : to_char_type(0xC2));
323 : : break;
324 : : }
325 : :
326 : : case value_t::number_integer:
327 : : {
328 : : if (j.m_value.number_integer >= 0)
329 : : {
330 : : // MessagePack does not differentiate between positive
331 : : // signed integers and unsigned integers. Therefore, we used
332 : : // the code from the value_t::number_unsigned case here.
333 : : if (j.m_value.number_unsigned < 128)
334 : : {
335 : : // positive fixnum
336 : : write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
337 : : }
338 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
339 : : {
340 : : // uint 8
341 : : oa->write_character(to_char_type(0xCC));
342 : : write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
343 : : }
344 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
345 : : {
346 : : // uint 16
347 : : oa->write_character(to_char_type(0xCD));
348 : : write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
349 : : }
350 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
351 : : {
352 : : // uint 32
353 : : oa->write_character(to_char_type(0xCE));
354 : : write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
355 : : }
356 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
357 : : {
358 : : // uint 64
359 : : oa->write_character(to_char_type(0xCF));
360 : : write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
361 : : }
362 : : }
363 : : else
364 : : {
365 : : if (j.m_value.number_integer >= -32)
366 : : {
367 : : // negative fixnum
368 : : write_number(static_cast<std::int8_t>(j.m_value.number_integer));
369 : : }
370 : : else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() and
371 : : j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
372 : : {
373 : : // int 8
374 : : oa->write_character(to_char_type(0xD0));
375 : : write_number(static_cast<std::int8_t>(j.m_value.number_integer));
376 : : }
377 : : else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() and
378 : : j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
379 : : {
380 : : // int 16
381 : : oa->write_character(to_char_type(0xD1));
382 : : write_number(static_cast<std::int16_t>(j.m_value.number_integer));
383 : : }
384 : : else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() and
385 : : j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
386 : : {
387 : : // int 32
388 : : oa->write_character(to_char_type(0xD2));
389 : : write_number(static_cast<std::int32_t>(j.m_value.number_integer));
390 : : }
391 : : else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() and
392 : : j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
393 : : {
394 : : // int 64
395 : : oa->write_character(to_char_type(0xD3));
396 : : write_number(static_cast<std::int64_t>(j.m_value.number_integer));
397 : : }
398 : : }
399 : : break;
400 : : }
401 : :
402 : : case value_t::number_unsigned:
403 : : {
404 : : if (j.m_value.number_unsigned < 128)
405 : : {
406 : : // positive fixnum
407 : : write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
408 : : }
409 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
410 : : {
411 : : // uint 8
412 : : oa->write_character(to_char_type(0xCC));
413 : : write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
414 : : }
415 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
416 : : {
417 : : // uint 16
418 : : oa->write_character(to_char_type(0xCD));
419 : : write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
420 : : }
421 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
422 : : {
423 : : // uint 32
424 : : oa->write_character(to_char_type(0xCE));
425 : : write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
426 : : }
427 : : else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
428 : : {
429 : : // uint 64
430 : : oa->write_character(to_char_type(0xCF));
431 : : write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
432 : : }
433 : : break;
434 : : }
435 : :
436 : : case value_t::number_float:
437 : : {
438 : : oa->write_character(get_msgpack_float_prefix(j.m_value.number_float));
439 : : write_number(j.m_value.number_float);
440 : : break;
441 : : }
442 : :
443 : : case value_t::string:
444 : : {
445 : : // step 1: write control byte and the string length
446 : : const auto N = j.m_value.string->size();
447 : : if (N <= 31)
448 : : {
449 : : // fixstr
450 : : write_number(static_cast<std::uint8_t>(0xA0 | N));
451 : : }
452 : : else if (N <= (std::numeric_limits<std::uint8_t>::max)())
453 : : {
454 : : // str 8
455 : : oa->write_character(to_char_type(0xD9));
456 : : write_number(static_cast<std::uint8_t>(N));
457 : : }
458 : : else if (N <= (std::numeric_limits<std::uint16_t>::max)())
459 : : {
460 : : // str 16
461 : : oa->write_character(to_char_type(0xDA));
462 : : write_number(static_cast<std::uint16_t>(N));
463 : : }
464 : : else if (N <= (std::numeric_limits<std::uint32_t>::max)())
465 : : {
466 : : // str 32
467 : : oa->write_character(to_char_type(0xDB));
468 : : write_number(static_cast<std::uint32_t>(N));
469 : : }
470 : :
471 : : // step 2: write the string
472 : : oa->write_characters(
473 : : reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
474 : : j.m_value.string->size());
475 : : break;
476 : : }
477 : :
478 : : case value_t::array:
479 : : {
480 : : // step 1: write control byte and the array size
481 : : const auto N = j.m_value.array->size();
482 : : if (N <= 15)
483 : : {
484 : : // fixarray
485 : : write_number(static_cast<std::uint8_t>(0x90 | N));
486 : : }
487 : : else if (N <= (std::numeric_limits<std::uint16_t>::max)())
488 : : {
489 : : // array 16
490 : : oa->write_character(to_char_type(0xDC));
491 : : write_number(static_cast<std::uint16_t>(N));
492 : : }
493 : : else if (N <= (std::numeric_limits<std::uint32_t>::max)())
494 : : {
495 : : // array 32
496 : : oa->write_character(to_char_type(0xDD));
497 : : write_number(static_cast<std::uint32_t>(N));
498 : : }
499 : :
500 : : // step 2: write each element
501 : : for (const auto& el : *j.m_value.array)
502 : : {
503 : : write_msgpack(el);
504 : : }
505 : : break;
506 : : }
507 : :
508 : : case value_t::object:
509 : : {
510 : : // step 1: write control byte and the object size
511 : : const auto N = j.m_value.object->size();
512 : : if (N <= 15)
513 : : {
514 : : // fixmap
515 : : write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));
516 : : }
517 : : else if (N <= (std::numeric_limits<std::uint16_t>::max)())
518 : : {
519 : : // map 16
520 : : oa->write_character(to_char_type(0xDE));
521 : : write_number(static_cast<std::uint16_t>(N));
522 : : }
523 : : else if (N <= (std::numeric_limits<std::uint32_t>::max)())
524 : : {
525 : : // map 32
526 : : oa->write_character(to_char_type(0xDF));
527 : : write_number(static_cast<std::uint32_t>(N));
528 : : }
529 : :
530 : : // step 2: write each element
531 : : for (const auto& el : *j.m_value.object)
532 : : {
533 : : write_msgpack(el.first);
534 : : write_msgpack(el.second);
535 : : }
536 : : break;
537 : : }
538 : :
539 : : default:
540 : : break;
541 : : }
542 : : }
543 : :
544 : : /*!
545 : : @param[in] j JSON value to serialize
546 : : @param[in] use_count whether to use '#' prefixes (optimized format)
547 : : @param[in] use_type whether to use '$' prefixes (optimized format)
548 : : @param[in] add_prefix whether prefixes need to be used for this value
549 : : */
550 : : void write_ubjson(const BasicJsonType& j, const bool use_count,
551 : : const bool use_type, const bool add_prefix = true)
552 : : {
553 : : switch (j.type())
554 : : {
555 : : case value_t::null:
556 : : {
557 : : if (add_prefix)
558 : : {
559 : : oa->write_character(to_char_type('Z'));
560 : : }
561 : : break;
562 : : }
563 : :
564 : : case value_t::boolean:
565 : : {
566 : : if (add_prefix)
567 : : {
568 : : oa->write_character(j.m_value.boolean
569 : : ? to_char_type('T')
570 : : : to_char_type('F'));
571 : : }
572 : : break;
573 : : }
574 : :
575 : : case value_t::number_integer:
576 : : {
577 : : write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);
578 : : break;
579 : : }
580 : :
581 : : case value_t::number_unsigned:
582 : : {
583 : : write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);
584 : : break;
585 : : }
586 : :
587 : : case value_t::number_float:
588 : : {
589 : : write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);
590 : : break;
591 : : }
592 : :
593 : : case value_t::string:
594 : : {
595 : : if (add_prefix)
596 : : {
597 : : oa->write_character(to_char_type('S'));
598 : : }
599 : : write_number_with_ubjson_prefix(j.m_value.string->size(), true);
600 : : oa->write_characters(
601 : : reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
602 : : j.m_value.string->size());
603 : : break;
604 : : }
605 : :
606 : : case value_t::array:
607 : : {
608 : : if (add_prefix)
609 : : {
610 : : oa->write_character(to_char_type('['));
611 : : }
612 : :
613 : : bool prefix_required = true;
614 : : if (use_type and not j.m_value.array->empty())
615 : : {
616 : : assert(use_count);
617 : : const CharType first_prefix = ubjson_prefix(j.front());
618 : : const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
619 : : [this, first_prefix](const BasicJsonType & v)
620 : : {
621 : : return ubjson_prefix(v) == first_prefix;
622 : : });
623 : :
624 : : if (same_prefix)
625 : : {
626 : : prefix_required = false;
627 : : oa->write_character(to_char_type('$'));
628 : : oa->write_character(first_prefix);
629 : : }
630 : : }
631 : :
632 : : if (use_count)
633 : : {
634 : : oa->write_character(to_char_type('#'));
635 : : write_number_with_ubjson_prefix(j.m_value.array->size(), true);
636 : : }
637 : :
638 : : for (const auto& el : *j.m_value.array)
639 : : {
640 : : write_ubjson(el, use_count, use_type, prefix_required);
641 : : }
642 : :
643 : : if (not use_count)
644 : : {
645 : : oa->write_character(to_char_type(']'));
646 : : }
647 : :
648 : : break;
649 : : }
650 : :
651 : : case value_t::object:
652 : : {
653 : : if (add_prefix)
654 : : {
655 : : oa->write_character(to_char_type('{'));
656 : : }
657 : :
658 : : bool prefix_required = true;
659 : : if (use_type and not j.m_value.object->empty())
660 : : {
661 : : assert(use_count);
662 : : const CharType first_prefix = ubjson_prefix(j.front());
663 : : const bool same_prefix = std::all_of(j.begin(), j.end(),
664 : : [this, first_prefix](const BasicJsonType & v)
665 : : {
666 : : return ubjson_prefix(v) == first_prefix;
667 : : });
668 : :
669 : : if (same_prefix)
670 : : {
671 : : prefix_required = false;
672 : : oa->write_character(to_char_type('$'));
673 : : oa->write_character(first_prefix);
674 : : }
675 : : }
676 : :
677 : : if (use_count)
678 : : {
679 : : oa->write_character(to_char_type('#'));
680 : : write_number_with_ubjson_prefix(j.m_value.object->size(), true);
681 : : }
682 : :
683 : : for (const auto& el : *j.m_value.object)
684 : : {
685 : : write_number_with_ubjson_prefix(el.first.size(), true);
686 : : oa->write_characters(
687 : : reinterpret_cast<const CharType*>(el.first.c_str()),
688 : : el.first.size());
689 : : write_ubjson(el.second, use_count, use_type, prefix_required);
690 : : }
691 : :
692 : : if (not use_count)
693 : : {
694 : : oa->write_character(to_char_type('}'));
695 : : }
696 : :
697 : : break;
698 : : }
699 : :
700 : : default:
701 : : break;
702 : : }
703 : : }
704 : :
705 : : private:
706 : : //////////
707 : : // BSON //
708 : : //////////
709 : :
710 : : /*!
711 : : @return The size of a BSON document entry header, including the id marker
712 : : and the entry name size (and its null-terminator).
713 : : */
714 : : static std::size_t calc_bson_entry_header_size(const string_t& name)
715 : : {
716 : : const auto it = name.find(static_cast<typename string_t::value_type>(0));
717 : : if (JSON_UNLIKELY(it != BasicJsonType::string_t::npos))
718 : : {
719 : : JSON_THROW(out_of_range::create(409,
720 : : "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")"));
721 : : }
722 : :
723 : : return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
724 : : }
725 : :
726 : : /*!
727 : : @brief Writes the given @a element_type and @a name to the output adapter
728 : : */
729 : : void write_bson_entry_header(const string_t& name,
730 : : const std::uint8_t element_type)
731 : : {
732 : : oa->write_character(to_char_type(element_type)); // boolean
733 : : oa->write_characters(
734 : : reinterpret_cast<const CharType*>(name.c_str()),
735 : : name.size() + 1u);
736 : : }
737 : :
738 : : /*!
739 : : @brief Writes a BSON element with key @a name and boolean value @a value
740 : : */
741 : : void write_bson_boolean(const string_t& name,
742 : : const bool value)
743 : : {
744 : : write_bson_entry_header(name, 0x08);
745 : : oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
746 : : }
747 : :
748 : : /*!
749 : : @brief Writes a BSON element with key @a name and double value @a value
750 : : */
751 : : void write_bson_double(const string_t& name,
752 : : const double value)
753 : : {
754 : : write_bson_entry_header(name, 0x01);
755 : : write_number<double, true>(value);
756 : : }
757 : :
758 : : /*!
759 : : @return The size of the BSON-encoded string in @a value
760 : : */
761 : : static std::size_t calc_bson_string_size(const string_t& value)
762 : : {
763 : : return sizeof(std::int32_t) + value.size() + 1ul;
764 : : }
765 : :
766 : : /*!
767 : : @brief Writes a BSON element with key @a name and string value @a value
768 : : */
769 : : void write_bson_string(const string_t& name,
770 : : const string_t& value)
771 : : {
772 : : write_bson_entry_header(name, 0x02);
773 : :
774 : : write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));
775 : : oa->write_characters(
776 : : reinterpret_cast<const CharType*>(value.c_str()),
777 : : value.size() + 1);
778 : : }
779 : :
780 : : /*!
781 : : @brief Writes a BSON element with key @a name and null value
782 : : */
783 : : void write_bson_null(const string_t& name)
784 : : {
785 : : write_bson_entry_header(name, 0x0A);
786 : : }
787 : :
788 : : /*!
789 : : @return The size of the BSON-encoded integer @a value
790 : : */
791 : : static std::size_t calc_bson_integer_size(const std::int64_t value)
792 : : {
793 : : return (std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)()
794 : : ? sizeof(std::int32_t)
795 : : : sizeof(std::int64_t);
796 : : }
797 : :
798 : : /*!
799 : : @brief Writes a BSON element with key @a name and integer @a value
800 : : */
801 : : void write_bson_integer(const string_t& name,
802 : : const std::int64_t value)
803 : : {
804 : : if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
805 : : {
806 : : write_bson_entry_header(name, 0x10); // int32
807 : : write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
808 : : }
809 : : else
810 : : {
811 : : write_bson_entry_header(name, 0x12); // int64
812 : : write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
813 : : }
814 : : }
815 : :
816 : : /*!
817 : : @return The size of the BSON-encoded unsigned integer in @a j
818 : : */
819 : : static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
820 : : {
821 : : return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
822 : : ? sizeof(std::int32_t)
823 : : : sizeof(std::int64_t);
824 : : }
825 : :
826 : : /*!
827 : : @brief Writes a BSON element with key @a name and unsigned @a value
828 : : */
829 : : void write_bson_unsigned(const string_t& name,
830 : : const std::uint64_t value)
831 : : {
832 : : if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
833 : : {
834 : : write_bson_entry_header(name, 0x10 /* int32 */);
835 : : write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
836 : : }
837 : : else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
838 : : {
839 : : write_bson_entry_header(name, 0x12 /* int64 */);
840 : : write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
841 : : }
842 : : else
843 : : {
844 : : JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64"));
845 : : }
846 : : }
847 : :
848 : : /*!
849 : : @brief Writes a BSON element with key @a name and object @a value
850 : : */
851 : : void write_bson_object_entry(const string_t& name,
852 : : const typename BasicJsonType::object_t& value)
853 : : {
854 : : write_bson_entry_header(name, 0x03); // object
855 : : write_bson_object(value);
856 : : }
857 : :
858 : : /*!
859 : : @return The size of the BSON-encoded array @a value
860 : : */
861 : : static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
862 : : {
863 : : std::size_t embedded_document_size = 0ul;
864 : : std::size_t array_index = 0ul;
865 : :
866 : : for (const auto& el : value)
867 : : {
868 : : embedded_document_size += calc_bson_element_size(std::to_string(array_index++), el);
869 : : }
870 : :
871 : : return sizeof(std::int32_t) + embedded_document_size + 1ul;
872 : : }
873 : :
874 : : /*!
875 : : @brief Writes a BSON element with key @a name and array @a value
876 : : */
877 : : void write_bson_array(const string_t& name,
878 : : const typename BasicJsonType::array_t& value)
879 : : {
880 : : write_bson_entry_header(name, 0x04); // array
881 : : write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));
882 : :
883 : : std::size_t array_index = 0ul;
884 : :
885 : : for (const auto& el : value)
886 : : {
887 : : write_bson_element(std::to_string(array_index++), el);
888 : : }
889 : :
890 : : oa->write_character(to_char_type(0x00));
891 : : }
892 : :
893 : : /*!
894 : : @brief Calculates the size necessary to serialize the JSON value @a j with its @a name
895 : : @return The calculated size for the BSON document entry for @a j with the given @a name.
896 : : */
897 : : static std::size_t calc_bson_element_size(const string_t& name,
898 : : const BasicJsonType& j)
899 : : {
900 : : const auto header_size = calc_bson_entry_header_size(name);
901 : : switch (j.type())
902 : : {
903 : : case value_t::object:
904 : : return header_size + calc_bson_object_size(*j.m_value.object);
905 : :
906 : : case value_t::array:
907 : : return header_size + calc_bson_array_size(*j.m_value.array);
908 : :
909 : : case value_t::boolean:
910 : : return header_size + 1ul;
911 : :
912 : : case value_t::number_float:
913 : : return header_size + 8ul;
914 : :
915 : : case value_t::number_integer:
916 : : return header_size + calc_bson_integer_size(j.m_value.number_integer);
917 : :
918 : : case value_t::number_unsigned:
919 : : return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);
920 : :
921 : : case value_t::string:
922 : : return header_size + calc_bson_string_size(*j.m_value.string);
923 : :
924 : : case value_t::null:
925 : : return header_size + 0ul;
926 : :
927 : : // LCOV_EXCL_START
928 : : default:
929 : : assert(false);
930 : : return 0ul;
931 : : // LCOV_EXCL_STOP
932 : : }
933 : : }
934 : :
935 : : /*!
936 : : @brief Serializes the JSON value @a j to BSON and associates it with the
937 : : key @a name.
938 : : @param name The name to associate with the JSON entity @a j within the
939 : : current BSON document
940 : : @return The size of the BSON entry
941 : : */
942 : : void write_bson_element(const string_t& name,
943 : : const BasicJsonType& j)
944 : : {
945 : : switch (j.type())
946 : : {
947 : : case value_t::object:
948 : : return write_bson_object_entry(name, *j.m_value.object);
949 : :
950 : : case value_t::array:
951 : : return write_bson_array(name, *j.m_value.array);
952 : :
953 : : case value_t::boolean:
954 : : return write_bson_boolean(name, j.m_value.boolean);
955 : :
956 : : case value_t::number_float:
957 : : return write_bson_double(name, j.m_value.number_float);
958 : :
959 : : case value_t::number_integer:
960 : : return write_bson_integer(name, j.m_value.number_integer);
961 : :
962 : : case value_t::number_unsigned:
963 : : return write_bson_unsigned(name, j.m_value.number_unsigned);
964 : :
965 : : case value_t::string:
966 : : return write_bson_string(name, *j.m_value.string);
967 : :
968 : : case value_t::null:
969 : : return write_bson_null(name);
970 : :
971 : : // LCOV_EXCL_START
972 : : default:
973 : : assert(false);
974 : : return;
975 : : // LCOV_EXCL_STOP
976 : : }
977 : : }
978 : :
979 : : /*!
980 : : @brief Calculates the size of the BSON serialization of the given
981 : : JSON-object @a j.
982 : : @param[in] j JSON value to serialize
983 : : @pre j.type() == value_t::object
984 : : */
985 : : static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
986 : : {
987 : : std::size_t document_size = std::accumulate(value.begin(), value.end(), 0ul,
988 : : [](size_t result, const typename BasicJsonType::object_t::value_type & el)
989 : : {
990 : : return result += calc_bson_element_size(el.first, el.second);
991 : : });
992 : :
993 : : return sizeof(std::int32_t) + document_size + 1ul;
994 : : }
995 : :
996 : : /*!
997 : : @param[in] j JSON value to serialize
998 : : @pre j.type() == value_t::object
999 : : */
1000 : : void write_bson_object(const typename BasicJsonType::object_t& value)
1001 : : {
1002 : : write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));
1003 : :
1004 : : for (const auto& el : value)
1005 : : {
1006 : : write_bson_element(el.first, el.second);
1007 : : }
1008 : :
1009 : : oa->write_character(to_char_type(0x00));
1010 : : }
1011 : :
1012 : : //////////
1013 : : // CBOR //
1014 : : //////////
1015 : :
1016 : : static constexpr CharType get_cbor_float_prefix(float /*unused*/)
1017 : : {
1018 : : return to_char_type(0xFA); // Single-Precision Float
1019 : : }
1020 : :
1021 : : static constexpr CharType get_cbor_float_prefix(double /*unused*/)
1022 : : {
1023 : : return to_char_type(0xFB); // Double-Precision Float
1024 : : }
1025 : :
1026 : : /////////////
1027 : : // MsgPack //
1028 : : /////////////
1029 : :
1030 : : static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
1031 : : {
1032 : : return to_char_type(0xCA); // float 32
1033 : : }
1034 : :
1035 : : static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
1036 : : {
1037 : : return to_char_type(0xCB); // float 64
1038 : : }
1039 : :
1040 : : ////////////
1041 : : // UBJSON //
1042 : : ////////////
1043 : :
1044 : : // UBJSON: write number (floating point)
1045 : : template<typename NumberType, typename std::enable_if<
1046 : : std::is_floating_point<NumberType>::value, int>::type = 0>
1047 : : void write_number_with_ubjson_prefix(const NumberType n,
1048 : : const bool add_prefix)
1049 : : {
1050 : : if (add_prefix)
1051 : : {
1052 : : oa->write_character(get_ubjson_float_prefix(n));
1053 : : }
1054 : : write_number(n);
1055 : : }
1056 : :
1057 : : // UBJSON: write number (unsigned integer)
1058 : : template<typename NumberType, typename std::enable_if<
1059 : : std::is_unsigned<NumberType>::value, int>::type = 0>
1060 : : void write_number_with_ubjson_prefix(const NumberType n,
1061 : : const bool add_prefix)
1062 : : {
1063 : : if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
1064 : : {
1065 : : if (add_prefix)
1066 : : {
1067 : : oa->write_character(to_char_type('i')); // int8
1068 : : }
1069 : : write_number(static_cast<std::uint8_t>(n));
1070 : : }
1071 : : else if (n <= (std::numeric_limits<std::uint8_t>::max)())
1072 : : {
1073 : : if (add_prefix)
1074 : : {
1075 : : oa->write_character(to_char_type('U')); // uint8
1076 : : }
1077 : : write_number(static_cast<std::uint8_t>(n));
1078 : : }
1079 : : else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
1080 : : {
1081 : : if (add_prefix)
1082 : : {
1083 : : oa->write_character(to_char_type('I')); // int16
1084 : : }
1085 : : write_number(static_cast<std::int16_t>(n));
1086 : : }
1087 : : else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
1088 : : {
1089 : : if (add_prefix)
1090 : : {
1091 : : oa->write_character(to_char_type('l')); // int32
1092 : : }
1093 : : write_number(static_cast<std::int32_t>(n));
1094 : : }
1095 : : else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
1096 : : {
1097 : : if (add_prefix)
1098 : : {
1099 : : oa->write_character(to_char_type('L')); // int64
1100 : : }
1101 : : write_number(static_cast<std::int64_t>(n));
1102 : : }
1103 : : else
1104 : : {
1105 : : JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
1106 : : }
1107 : : }
1108 : :
1109 : : // UBJSON: write number (signed integer)
1110 : : template<typename NumberType, typename std::enable_if<
1111 : : std::is_signed<NumberType>::value and
1112 : : not std::is_floating_point<NumberType>::value, int>::type = 0>
1113 : : void write_number_with_ubjson_prefix(const NumberType n,
1114 : : const bool add_prefix)
1115 : : {
1116 : : if ((std::numeric_limits<std::int8_t>::min)() <= n and n <= (std::numeric_limits<std::int8_t>::max)())
1117 : : {
1118 : : if (add_prefix)
1119 : : {
1120 : : oa->write_character(to_char_type('i')); // int8
1121 : : }
1122 : : write_number(static_cast<std::int8_t>(n));
1123 : : }
1124 : : else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n and n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
1125 : : {
1126 : : if (add_prefix)
1127 : : {
1128 : : oa->write_character(to_char_type('U')); // uint8
1129 : : }
1130 : : write_number(static_cast<std::uint8_t>(n));
1131 : : }
1132 : : else if ((std::numeric_limits<std::int16_t>::min)() <= n and n <= (std::numeric_limits<std::int16_t>::max)())
1133 : : {
1134 : : if (add_prefix)
1135 : : {
1136 : : oa->write_character(to_char_type('I')); // int16
1137 : : }
1138 : : write_number(static_cast<std::int16_t>(n));
1139 : : }
1140 : : else if ((std::numeric_limits<std::int32_t>::min)() <= n and n <= (std::numeric_limits<std::int32_t>::max)())
1141 : : {
1142 : : if (add_prefix)
1143 : : {
1144 : : oa->write_character(to_char_type('l')); // int32
1145 : : }
1146 : : write_number(static_cast<std::int32_t>(n));
1147 : : }
1148 : : else if ((std::numeric_limits<std::int64_t>::min)() <= n and n <= (std::numeric_limits<std::int64_t>::max)())
1149 : : {
1150 : : if (add_prefix)
1151 : : {
1152 : : oa->write_character(to_char_type('L')); // int64
1153 : : }
1154 : : write_number(static_cast<std::int64_t>(n));
1155 : : }
1156 : : // LCOV_EXCL_START
1157 : : else
1158 : : {
1159 : : JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
1160 : : }
1161 : : // LCOV_EXCL_STOP
1162 : : }
1163 : :
1164 : : /*!
1165 : : @brief determine the type prefix of container values
1166 : :
1167 : : @note This function does not need to be 100% accurate when it comes to
1168 : : integer limits. In case a number exceeds the limits of int64_t,
1169 : : this will be detected by a later call to function
1170 : : write_number_with_ubjson_prefix. Therefore, we return 'L' for any
1171 : : value that does not fit the previous limits.
1172 : : */
1173 : : CharType ubjson_prefix(const BasicJsonType& j) const noexcept
1174 : : {
1175 : : switch (j.type())
1176 : : {
1177 : : case value_t::null:
1178 : : return 'Z';
1179 : :
1180 : : case value_t::boolean:
1181 : : return j.m_value.boolean ? 'T' : 'F';
1182 : :
1183 : : case value_t::number_integer:
1184 : : {
1185 : : if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
1186 : : {
1187 : : return 'i';
1188 : : }
1189 : : if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
1190 : : {
1191 : : return 'U';
1192 : : }
1193 : : if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
1194 : : {
1195 : : return 'I';
1196 : : }
1197 : : if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
1198 : : {
1199 : : return 'l';
1200 : : }
1201 : : // no check and assume int64_t (see note above)
1202 : : return 'L';
1203 : : }
1204 : :
1205 : : case value_t::number_unsigned:
1206 : : {
1207 : : if (j.m_value.number_unsigned <= (std::numeric_limits<std::int8_t>::max)())
1208 : : {
1209 : : return 'i';
1210 : : }
1211 : : if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
1212 : : {
1213 : : return 'U';
1214 : : }
1215 : : if (j.m_value.number_unsigned <= (std::numeric_limits<std::int16_t>::max)())
1216 : : {
1217 : : return 'I';
1218 : : }
1219 : : if (j.m_value.number_unsigned <= (std::numeric_limits<std::int32_t>::max)())
1220 : : {
1221 : : return 'l';
1222 : : }
1223 : : // no check and assume int64_t (see note above)
1224 : : return 'L';
1225 : : }
1226 : :
1227 : : case value_t::number_float:
1228 : : return get_ubjson_float_prefix(j.m_value.number_float);
1229 : :
1230 : : case value_t::string:
1231 : : return 'S';
1232 : :
1233 : : case value_t::array:
1234 : : return '[';
1235 : :
1236 : : case value_t::object:
1237 : : return '{';
1238 : :
1239 : : default: // discarded values
1240 : : return 'N';
1241 : : }
1242 : : }
1243 : :
1244 : : static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
1245 : : {
1246 : : return 'd'; // float 32
1247 : : }
1248 : :
1249 : : static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
1250 : : {
1251 : : return 'D'; // float 64
1252 : : }
1253 : :
1254 : : ///////////////////////
1255 : : // Utility functions //
1256 : : ///////////////////////
1257 : :
1258 : : /*
1259 : : @brief write a number to output input
1260 : : @param[in] n number of type @a NumberType
1261 : : @tparam NumberType the type of the number
1262 : : @tparam OutputIsLittleEndian Set to true if output data is
1263 : : required to be little endian
1264 : :
1265 : : @note This function needs to respect the system's endianess, because bytes
1266 : : in CBOR, MessagePack, and UBJSON are stored in network order (big
1267 : : endian) and therefore need reordering on little endian systems.
1268 : : */
1269 : : template<typename NumberType, bool OutputIsLittleEndian = false>
1270 : : void write_number(const NumberType n)
1271 : : {
1272 : : // step 1: write number to array of length NumberType
1273 : : std::array<CharType, sizeof(NumberType)> vec;
1274 : : std::memcpy(vec.data(), &n, sizeof(NumberType));
1275 : :
1276 : : // step 2: write array to output (with possible reordering)
1277 : : if (is_little_endian != OutputIsLittleEndian)
1278 : : {
1279 : : // reverse byte order prior to conversion if necessary
1280 : : std::reverse(vec.begin(), vec.end());
1281 : : }
1282 : :
1283 : : oa->write_characters(vec.data(), sizeof(NumberType));
1284 : : }
1285 : :
1286 : : public:
1287 : : // The following to_char_type functions are implement the conversion
1288 : : // between uint8_t and CharType. In case CharType is not unsigned,
1289 : : // such a conversion is required to allow values greater than 128.
1290 : : // See <https://github.com/nlohmann/json/issues/1286> for a discussion.
1291 : : template < typename C = CharType,
1292 : : enable_if_t < std::is_signed<C>::value and std::is_signed<char>::value > * = nullptr >
1293 : : static constexpr CharType to_char_type(std::uint8_t x) noexcept
1294 : : {
1295 : : return *reinterpret_cast<char*>(&x);
1296 : : }
1297 : :
1298 : : template < typename C = CharType,
1299 : : enable_if_t < std::is_signed<C>::value and std::is_unsigned<char>::value > * = nullptr >
1300 : : static CharType to_char_type(std::uint8_t x) noexcept
1301 : : {
1302 : : static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
1303 : : static_assert(std::is_pod<CharType>::value, "CharType must be POD");
1304 : : CharType result;
1305 : : std::memcpy(&result, &x, sizeof(x));
1306 : : return result;
1307 : : }
1308 : :
1309 : : template<typename C = CharType,
1310 : : enable_if_t<std::is_unsigned<C>::value>* = nullptr>
1311 : : static constexpr CharType to_char_type(std::uint8_t x) noexcept
1312 : : {
1313 : : return x;
1314 : : }
1315 : :
1316 : : template < typename InputCharType, typename C = CharType,
1317 : : enable_if_t <
1318 : : std::is_signed<C>::value and
1319 : : std::is_signed<char>::value and
1320 : : std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
1321 : : > * = nullptr >
1322 : 0 : static constexpr CharType to_char_type(InputCharType x) noexcept
1323 : : {
1324 : 0 : return x;
1325 : : }
1326 : :
1327 : : private:
1328 : : /// whether we can assume little endianess
1329 : : const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
1330 : :
1331 : : /// the output
1332 : : output_adapter_t<CharType> oa = nullptr;
1333 : : };
1334 : : } // namespace detail
1335 : : } // namespace nlohmann
|