Skip to content

Commit 2d42229

Browse files
authored
Support BSON uint64 de/serialization (#4590)
* Support BSON uint64 de/serialization Signed-off-by: Michael Valladolid <[email protected]> * Treat 0x11 as uint64 and not timestamp specific Signed-off-by: Michael Valladolid <[email protected]> --------- Signed-off-by: Michael Valladolid <[email protected]>
1 parent 1809b3d commit 2d42229

File tree

6 files changed

+35
-18
lines changed

6 files changed

+35
-18
lines changed

docs/mkdocs/docs/features/binary_formats/bson.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ The library uses the following mapping from JSON values types to BSON types:
2323
| number_integer | 2147483648..9223372036854775807 | int64 | 0x12 |
2424
| number_unsigned | 0..2147483647 | int32 | 0x10 |
2525
| number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 |
26-
| number_unsigned | 9223372036854775808..18446744073709551615 | -- | -- |
26+
| number_unsigned | 9223372036854775808..18446744073709551615 | uint64 | 0x11 |
2727
| number_float | *any value* | double | 0x01 |
2828
| string | *any value* | string | 0x02 |
2929
| array | *any value* | document | 0x04 |
@@ -73,7 +73,7 @@ The library maps BSON record types to JSON value types as follows:
7373
| Symbol | 0x0E | *unsupported* |
7474
| JavaScript Code | 0x0F | *unsupported* |
7575
| int32 | 0x10 | number_integer |
76-
| Timestamp | 0x11 | *unsupported* |
76+
| uint64(Timestamp) | 0x11 | number_unsigned |
7777
| 128-bit decimal float | 0x13 | *unsupported* |
7878
| Max Key | 0x7F | *unsupported* |
7979
| Min Key | 0xFF | *unsupported* |
@@ -94,3 +94,8 @@ The library maps BSON record types to JSON value types as follows:
9494
```json
9595
--8<-- "examples/from_bson.output"
9696
```
97+
98+
!!! note "Handling of BSON type 0x11"
99+
100+
BSON type 0x11 is used to represent uint64 numbers. This library treats these values purely as uint64 numbers
101+
and does not parse them into date-related formats.

docs/mkdocs/docs/home/exceptions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ A parsed number could not be stored as without changing it to NaN or INF.
839839

840840
### json.exception.out_of_range.407
841841

842-
UBJSON and BSON only support integer numbers up to 9223372036854775807.
842+
UBJSON only support integer numbers up to 9223372036854775807.
843843

844844
!!! failure "Example message"
845845

include/nlohmann/detail/input/binary_reader.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,12 @@ class binary_reader
328328
return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
329329
}
330330

331+
case 0x11: // uint64
332+
{
333+
std::uint64_t value{};
334+
return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);
335+
}
336+
331337
default: // anything else not supported (yet)
332338
{
333339
std::array<char, 3> cr{{}};

include/nlohmann/detail/output/binary_writer.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,8 @@ class binary_writer
10971097
}
10981098
else
10991099
{
1100-
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j));
1100+
write_bson_entry_header(name, 0x11 /* uint64 */);
1101+
write_number<std::uint64_t>(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned), true);
11011102
}
11021103
}
11031104

single_include/nlohmann/json.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10006,6 +10006,12 @@ class binary_reader
1000610006
return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
1000710007
}
1000810008

10009+
case 0x11: // uint64
10010+
{
10011+
std::uint64_t value{};
10012+
return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);
10013+
}
10014+
1000910015
default: // anything else not supported (yet)
1001010016
{
1001110017
std::array<char, 3> cr{{}};
@@ -16733,7 +16739,8 @@ class binary_writer
1673316739
}
1673416740
else
1673516741
{
16736-
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j));
16742+
write_bson_entry_header(name, 0x11 /* uint64 */);
16743+
write_number<std::uint64_t>(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned), true);
1673716744
}
1673816745
}
1673916746

tests/src/unit-bson.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,6 @@ TEST_CASE("BSON")
331331

332332
SECTION("non-empty object with unsigned integer (64-bit) member")
333333
{
334-
// directly encoding uint64 is not supported in bson (only for timestamp values)
335334
json const j =
336335
{
337336
{ "entry", std::uint64_t{0x1234567804030201} }
@@ -531,7 +530,6 @@ TEST_CASE("BSON")
531530

532531
SECTION("Some more complex document")
533532
{
534-
// directly encoding uint64 is not supported in bson (only for timestamp values)
535533
json const j =
536534
{
537535
{"double", 42.5},
@@ -1164,10 +1162,7 @@ TEST_CASE("BSON numerical data")
11641162
std::vector<std::uint64_t> const numbers
11651163
{
11661164
static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()) + 1ULL,
1167-
10000000000000000000ULL,
1168-
18000000000000000000ULL,
1169-
(std::numeric_limits<std::uint64_t>::max)() - 1ULL,
1170-
(std::numeric_limits<std::uint64_t>::max)(),
1165+
0xffffffffffffffff,
11711166
};
11721167

11731168
for (const auto i : numbers)
@@ -1184,7 +1179,7 @@ TEST_CASE("BSON numerical data")
11841179
std::vector<std::uint8_t> const expected_bson =
11851180
{
11861181
0x14u, 0x00u, 0x00u, 0x00u, // size (little endian)
1187-
0x12u, /// entry: int64
1182+
0x11u, /// entry: uint64
11881183
'e', 'n', 't', 'r', 'y', '\x00',
11891184
static_cast<std::uint8_t>((iu >> (8u * 0u)) & 0xffu),
11901185
static_cast<std::uint8_t>((iu >> (8u * 1u)) & 0xffu),
@@ -1197,12 +1192,15 @@ TEST_CASE("BSON numerical data")
11971192
0x00u // end marker
11981193
};
11991194

1200-
CHECK_THROWS_AS(json::to_bson(j), json::out_of_range&);
1201-
#if JSON_DIAGNOSTICS
1202-
CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] (/entry) integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64");
1203-
#else
1204-
CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64");
1205-
#endif
1195+
const auto bson = json::to_bson(j);
1196+
CHECK(bson == expected_bson);
1197+
1198+
auto j_roundtrip = json::from_bson(bson);
1199+
1200+
CHECK(j.at("entry").is_number_unsigned());
1201+
CHECK(j_roundtrip.at("entry").is_number_unsigned());
1202+
CHECK(j_roundtrip == j);
1203+
CHECK(json::from_bson(bson, true, false) == j);
12061204
}
12071205
}
12081206

0 commit comments

Comments
 (0)