2022-07-20 13:38:07 +03:00
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
2023-11-29 00:36:31 +03:00
// | | |__ | | | | | | version 3.11.3
2022-07-20 13:38:07 +03:00
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
2023-11-26 17:51:19 +03:00
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
2022-07-20 13:38:07 +03:00
// SPDX-License-Identifier: MIT
2018-09-14 23:58:22 +03:00
2019-01-13 19:41:21 +03:00
# include "doctest_compatibility.h"
2018-09-14 23:58:22 +03:00
# include <nlohmann/json.hpp>
using nlohmann : : json ;
2019-01-13 19:41:21 +03:00
2023-11-29 17:02:51 +03:00
# include "make_test_data_available.hpp"
# include "test_utils.hpp"
2018-09-14 23:58:22 +03:00
# include <fstream>
2022-09-19 09:02:50 +03:00
# include <limits>
2019-01-13 19:41:21 +03:00
# include <sstream>
2018-09-14 23:58:22 +03:00
TEST_CASE ( " BSON " )
{
2018-09-15 01:43:39 +03:00
SECTION ( " individual values not supported " )
2018-09-14 23:58:22 +03:00
{
SECTION ( " null " )
{
2022-09-13 13:58:26 +03:00
json const j = nullptr ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is null " , json : : type_error & ) ;
2018-09-14 23:58:22 +03:00
}
SECTION ( " boolean " )
{
SECTION ( " true " )
{
2022-09-13 13:58:26 +03:00
json const j = true ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is boolean " , json : : type_error & ) ;
2018-09-14 23:58:22 +03:00
}
SECTION ( " false " )
{
2022-09-13 13:58:26 +03:00
json const j = false ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is boolean " , json : : type_error & ) ;
2018-09-14 23:58:22 +03:00
}
}
2018-09-15 01:43:39 +03:00
SECTION ( " number " )
{
2022-09-13 13:58:26 +03:00
json const j = 42 ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is number " , json : : type_error & ) ;
2018-09-15 01:43:39 +03:00
}
SECTION ( " float " )
{
2022-09-13 13:58:26 +03:00
json const j = 4.2 ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is number " , json : : type_error & ) ;
2018-09-15 01:43:39 +03:00
}
SECTION ( " string " )
{
2022-09-13 13:58:26 +03:00
json const j = " not supported " ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is string " , json : : type_error & ) ;
2018-09-15 01:43:39 +03:00
}
SECTION ( " array " )
{
2023-11-29 17:02:51 +03:00
json const j = std : : vector < int > { 1 , 2 , 3 , 4 , 5 , 6 , 7 } ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is array " , json : : type_error & ) ;
2018-09-15 01:43:39 +03:00
}
}
2018-10-16 21:42:00 +03:00
SECTION ( " keys containing code-point U+0000 cannot be serialized to BSON " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ std : : string ( " en \0 try " , 6 ) , true } } ;
2021-01-02 18:13:04 +03:00
# if JSON_DIAGNOSTICS
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.out_of_range.409] (/en) BSON key cannot contain code point U+0000 (at byte 2) " , json : : out_of_range & ) ;
2021-01-02 18:13:04 +03:00
# else
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : to_bson ( j ) , " [json.exception.out_of_range.409] BSON key cannot contain code point U+0000 (at byte 2) " , json : : out_of_range & ) ;
2021-01-02 18:13:04 +03:00
# endif
2018-10-16 21:42:00 +03:00
}
2018-10-28 11:16:40 +03:00
SECTION ( " string length must be at least 1 " )
{
// from https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11175
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const v =
2023-11-29 17:02:51 +03:00
{
0x20 ,
0x20 ,
0x20 ,
0x20 ,
0x02 ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x80 } ;
2019-07-02 22:06:42 +03:00
json _ ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( _ = json : : from_bson ( v ) , " [json.exception.parse_error.112] parse error at byte 10: syntax error while parsing BSON string: string length must be at least 1, is -2147483648 " , json : : parse_error & ) ;
2018-10-28 11:16:40 +03:00
}
2018-09-15 01:43:39 +03:00
SECTION ( " objects " )
{
SECTION ( " empty object " )
{
2022-09-13 13:58:26 +03:00
json const j = json : : object ( ) ;
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x05 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
// no entries
0x00 // end marker
} ;
2018-09-15 01:43:39 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 04:08:50 +03:00
SECTION ( " non-empty object with bool " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , true } } ;
2018-09-15 04:08:50 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x0D ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x08 , // entry: boolean
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x01 , // value = true
0x00 // end marker
} ;
2018-09-15 04:08:50 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 04:23:54 +03:00
SECTION ( " non-empty object with bool " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , false } } ;
2018-09-15 04:23:54 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x0D ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x08 , // entry: boolean
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x00 , // value = false
0x00 // end marker
} ;
2018-09-15 04:23:54 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
SECTION ( " non-empty object with double " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , 4.2 } } ;
2018-09-15 04:23:54 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x14 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x01 , /// entry: double
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0xcd ,
0xcc ,
0xcc ,
0xcc ,
0xcc ,
0xcc ,
0x10 ,
0x40 ,
0x00 // end marker
} ;
2018-09-15 04:23:54 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 12:33:24 +03:00
SECTION ( " non-empty object with string " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , " bsonstr " } } ;
2018-09-15 12:33:24 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x18 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x02 , /// entry: string (UTF-8)
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x08 ,
0x00 ,
0x00 ,
0x00 ,
' b ' ,
' s ' ,
' o ' ,
' n ' ,
' s ' ,
' t ' ,
' r ' ,
' \x00 ' ,
0x00 // end marker
} ;
2018-09-15 12:33:24 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 12:38:26 +03:00
SECTION ( " non-empty object with null member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , nullptr } } ;
2018-09-15 12:38:26 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x0C ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x0A , /// entry: null
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x00 // end marker
} ;
2018-09-15 12:38:26 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 12:54:17 +03:00
SECTION ( " non-empty object with integer (32-bit) member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , std : : int32_t { 0x12345678 } } } ;
2018-09-15 12:54:17 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x10 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x10 , /// entry: int32
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x78 ,
0x56 ,
0x34 ,
0x12 ,
0x00 // end marker
} ;
2018-09-15 12:54:17 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 13:00:53 +03:00
SECTION ( " non-empty object with integer (64-bit) member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , std : : int64_t { 0x1234567804030201 } } } ;
2018-09-15 13:00:53 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x14 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x12 , /// entry: int64
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x01 ,
0x02 ,
0x03 ,
0x04 ,
0x78 ,
0x56 ,
0x34 ,
0x12 ,
0x00 // end marker
} ;
2018-09-15 13:00:53 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 12:33:24 +03:00
2018-09-15 13:11:21 +03:00
SECTION ( " non-empty object with negative integer (32-bit) member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , std : : int32_t { - 1 } } } ;
2018-09-15 13:11:21 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x10 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x10 , /// entry: int32
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0xFF ,
0xFF ,
0xFF ,
0xFF ,
0x00 // end marker
} ;
2018-09-15 13:11:21 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
SECTION ( " non-empty object with negative integer (64-bit) member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , std : : int64_t { - 1 } } } ;
2018-09-15 13:11:21 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x10 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x10 , /// entry: int32
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0xFF ,
0xFF ,
0xFF ,
0xFF ,
0x00 // end marker
} ;
2018-09-15 13:11:21 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
SECTION ( " non-empty object with unsigned integer (64-bit) member " )
{
// directly encoding uint64 is not supported in bson (only for timestamp values)
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , std : : uint64_t { 0x1234567804030201 } } } ;
2018-09-15 13:11:21 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x14 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x12 , /// entry: int64
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x01 ,
0x02 ,
0x03 ,
0x04 ,
0x78 ,
0x56 ,
0x34 ,
0x12 ,
0x00 // end marker
} ;
2018-09-15 13:11:21 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 14:03:42 +03:00
2018-09-25 21:34:25 +03:00
SECTION ( " non-empty object with small unsigned integer member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , std : : uint64_t { 0x42 } } } ;
2018-09-25 21:34:25 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x10 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x10 , /// entry: int32
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x42 ,
0x00 ,
0x00 ,
0x00 ,
0x00 // end marker
} ;
2018-09-25 21:34:25 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 14:03:42 +03:00
SECTION ( " non-empty object with object member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , json : : object ( ) } } ;
2018-09-15 14:03:42 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x11 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x03 , /// entry: embedded document
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x05 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
// no entries
0x00 , // end marker (embedded document)
0x00 // end marker
} ;
2018-09-15 14:03:42 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 14:40:20 +03:00
2018-09-15 14:54:08 +03:00
SECTION ( " non-empty object with array member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , json : : array ( ) } } ;
2018-09-15 14:54:08 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x11 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x04 , /// entry: embedded document
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x05 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
// no entries
0x00 , // end marker (embedded document)
0x00 // end marker
} ;
2018-09-15 14:54:08 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 15:08:38 +03:00
SECTION ( " non-empty object with non-empty array member " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , json : : array ( { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } ) } } ;
2018-09-15 15:08:38 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x49 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x04 , /// entry: embedded document
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x3D ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x10 ,
' 0 ' ,
0x00 ,
0x01 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
' 1 ' ,
0x00 ,
0x02 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
' 2 ' ,
0x00 ,
0x03 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
' 3 ' ,
0x00 ,
0x04 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
' 4 ' ,
0x00 ,
0x05 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
' 5 ' ,
0x00 ,
0x06 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
' 6 ' ,
0x00 ,
0x07 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
' 7 ' ,
0x00 ,
0x08 ,
0x00 ,
0x00 ,
0x00 ,
0x00 , // end marker (embedded document)
0x00 // end marker
} ;
2018-09-15 15:08:38 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2019-07-05 07:13:25 +03:00
SECTION ( " non-empty object with binary member " )
{
const size_t N = 10 ;
2020-05-06 22:24:00 +03:00
const auto s = std : : vector < std : : uint8_t > ( N , ' x ' ) ;
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , json : : binary ( s , 0 ) } } ;
2019-07-05 07:13:25 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x1B ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x05 , // entry: binary
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x0A ,
0x00 ,
0x00 ,
0x00 , // size of binary (little endian)
0x00 , // Generic binary subtype
0x78 ,
0x78 ,
0x78 ,
0x78 ,
0x78 ,
0x78 ,
0x78 ,
0x78 ,
0x78 ,
0x78 ,
0x00 // end marker
} ;
2019-07-05 07:13:25 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2020-05-06 22:24:00 +03:00
SECTION ( " non-empty object with binary member with subtype " )
{
// an MD5 hash
const std : : vector < std : : uint8_t > md5hash = { 0xd7 , 0x7e , 0x27 , 0x54 , 0xbe , 0x12 , 0x37 , 0xfe , 0xd6 , 0x0c , 0x33 , 0x98 , 0x30 , 0x3b , 0x8d , 0xc4 } ;
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , json : : binary ( md5hash , 5 ) } } ;
2020-05-06 22:24:00 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
0x21 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x05 , // entry: binary
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x10 ,
0x00 ,
0x00 ,
0x00 , // size of binary (little endian)
0x05 , // MD5 binary subtype
0xd7 ,
0x7e ,
0x27 ,
0x54 ,
0xbe ,
0x12 ,
0x37 ,
0xfe ,
0xd6 ,
0x0c ,
0x33 ,
0x98 ,
0x30 ,
0x3b ,
0x8d ,
0xc4 ,
0x00 // end marker
} ;
2020-05-06 22:24:00 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-15 14:40:20 +03:00
SECTION ( " Some more complex document " )
{
// directly encoding uint64 is not supported in bson (only for timestamp values)
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " double " , 42.5 } ,
{ " entry " , 4.2 } ,
{ " number " , 12345 } ,
{ " object " , { { " string " , " value " } } } } ;
2018-09-15 14:40:20 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected =
2023-11-29 17:02:51 +03:00
{
/*size */ 0x4f ,
0x00 ,
0x00 ,
0x00 ,
/*entry*/ 0x01 ,
' d ' ,
' o ' ,
' u ' ,
' b ' ,
' l ' ,
' e ' ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x40 ,
0x45 ,
0x40 ,
/*entry*/ 0x01 ,
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
0x00 ,
0xcd ,
0xcc ,
0xcc ,
0xcc ,
0xcc ,
0xcc ,
0x10 ,
0x40 ,
/*entry*/ 0x10 ,
' n ' ,
' u ' ,
' m ' ,
' b ' ,
' e ' ,
' r ' ,
0x00 ,
0x39 ,
0x30 ,
0x00 ,
0x00 ,
/*entry*/ 0x03 ,
' o ' ,
' b ' ,
' j ' ,
' e ' ,
' c ' ,
' t ' ,
0x00 ,
/*entry: obj-size */ 0x17 ,
0x00 ,
0x00 ,
0x00 ,
/*entry: obj-entry*/ 0x02 ,
' s ' ,
' t ' ,
' r ' ,
' i ' ,
' n ' ,
' g ' ,
0x00 ,
0x06 ,
0x00 ,
0x00 ,
0x00 ,
' v ' ,
' a ' ,
' l ' ,
' u ' ,
' e ' ,
0 ,
/*entry: obj-term.*/ 0x00 ,
/*obj-term*/ 0x00 } ;
2018-09-15 14:40:20 +03:00
const auto result = json : : to_bson ( j ) ;
CHECK ( result = = expected ) ;
// roundtrip
CHECK ( json : : from_bson ( result ) = = j ) ;
CHECK ( json : : from_bson ( result , true , false ) = = j ) ;
}
2018-09-14 23:58:22 +03:00
}
2018-10-25 00:39:30 +03:00
2023-09-23 18:19:28 +03:00
SECTION ( " Examples from https://bsonspec.org/faq.html " )
2018-10-25 00:39:30 +03:00
{
SECTION ( " Example 1 " )
{
std : : vector < std : : uint8_t > input = { 0x16 , 0x00 , 0x00 , 0x00 , 0x02 , ' h ' , ' e ' , ' l ' , ' l ' , ' o ' , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , ' w ' , ' o ' , ' r ' , ' l ' , ' d ' , 0x00 , 0x00 } ;
json parsed = json : : from_bson ( input ) ;
json expected = { { " hello " , " world " } } ;
CHECK ( parsed = = expected ) ;
auto dumped = json : : to_bson ( parsed ) ;
CHECK ( dumped = = input ) ;
2018-10-25 14:01:18 +03:00
CHECK ( json : : from_bson ( dumped ) = = expected ) ;
2018-10-25 00:39:30 +03:00
}
SECTION ( " Example 2 " )
{
std : : vector < std : : uint8_t > input = { 0x31 , 0x00 , 0x00 , 0x00 , 0x04 , ' B ' , ' S ' , ' O ' , ' N ' , 0x00 , 0x26 , 0x00 , 0x00 , 0x00 , 0x02 , 0x30 , 0x00 , 0x08 , 0x00 , 0x00 , 0x00 , ' a ' , ' w ' , ' e ' , ' s ' , ' o ' , ' m ' , ' e ' , 0x00 , 0x01 , 0x31 , 0x00 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x14 , 0x40 , 0x10 , 0x32 , 0x00 , 0xc2 , 0x07 , 0x00 , 0x00 , 0x00 , 0x00 } ;
json parsed = json : : from_bson ( input ) ;
json expected = { { " BSON " , { " awesome " , 5.05 , 1986 } } } ;
CHECK ( parsed = = expected ) ;
auto dumped = json : : to_bson ( parsed ) ;
2018-10-25 14:01:18 +03:00
CHECK ( dumped = = input ) ;
2018-10-25 00:39:30 +03:00
CHECK ( json : : from_bson ( dumped ) = = expected ) ;
}
}
2018-09-14 23:58:22 +03:00
}
2018-09-25 00:35:19 +03:00
TEST_CASE ( " BSON input/output_adapters " )
{
json json_representation =
2023-11-29 17:02:51 +03:00
{
{ " double " , 42.5 } ,
{ " entry " , 4.2 } ,
{ " number " , 12345 } ,
{ " object " , { { " string " , " value " } } } } ;
2018-09-25 00:35:19 +03:00
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const bson_representation =
2023-11-29 17:02:51 +03:00
{
/*size */ 0x4f ,
0x00 ,
0x00 ,
0x00 ,
/*entry*/ 0x01 ,
' d ' ,
' o ' ,
' u ' ,
' b ' ,
' l ' ,
' e ' ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x40 ,
0x45 ,
0x40 ,
/*entry*/ 0x01 ,
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
0x00 ,
0xcd ,
0xcc ,
0xcc ,
0xcc ,
0xcc ,
0xcc ,
0x10 ,
0x40 ,
/*entry*/ 0x10 ,
' n ' ,
' u ' ,
' m ' ,
' b ' ,
' e ' ,
' r ' ,
0x00 ,
0x39 ,
0x30 ,
0x00 ,
0x00 ,
/*entry*/ 0x03 ,
' o ' ,
' b ' ,
' j ' ,
' e ' ,
' c ' ,
' t ' ,
0x00 ,
/*entry: obj-size */ 0x17 ,
0x00 ,
0x00 ,
0x00 ,
/*entry: obj-entry*/ 0x02 ,
' s ' ,
' t ' ,
' r ' ,
' i ' ,
' n ' ,
' g ' ,
0x00 ,
0x06 ,
0x00 ,
0x00 ,
0x00 ,
' v ' ,
' a ' ,
' l ' ,
' u ' ,
' e ' ,
0 ,
/*entry: obj-term.*/ 0x00 ,
/*obj-term*/ 0x00 } ;
2018-09-25 00:35:19 +03:00
json j2 ;
CHECK_NOTHROW ( j2 = json : : from_bson ( bson_representation ) ) ;
// compare parsed JSON values
CHECK ( json_representation = = j2 ) ;
SECTION ( " roundtrips " )
{
SECTION ( " std::ostringstream " )
{
2019-11-03 15:48:25 +03:00
std : : basic_ostringstream < std : : uint8_t > ss ;
2018-09-25 00:35:19 +03:00
json : : to_bson ( json_representation , ss ) ;
2019-11-03 15:48:25 +03:00
json j3 = json : : from_bson ( ss . str ( ) ) ;
2018-09-25 00:35:19 +03:00
CHECK ( json_representation = = j3 ) ;
}
SECTION ( " std::string " )
{
std : : string s ;
json : : to_bson ( json_representation , s ) ;
json j3 = json : : from_bson ( s ) ;
CHECK ( json_representation = = j3 ) ;
}
SECTION ( " std::vector " )
{
std : : vector < std : : uint8_t > v ;
json : : to_bson ( json_representation , v ) ;
json j3 = json : : from_bson ( v ) ;
CHECK ( json_representation = = j3 ) ;
}
}
}
2018-09-25 21:34:25 +03:00
2023-11-29 17:02:51 +03:00
namespace {
2018-09-25 21:34:25 +03:00
class SaxCountdown
{
public :
2023-11-29 17:02:51 +03:00
explicit SaxCountdown ( const int count )
: events_left ( count )
2018-09-25 21:34:25 +03:00
{ }
bool null ( )
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool boolean ( bool /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool number_integer ( json : : number_integer_t /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool number_unsigned ( json : : number_unsigned_t /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool number_float ( json : : number_float_t /*unused*/ , const std : : string & /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool string ( std : : string & /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool binary ( std : : vector < std : : uint8_t > & /*unused*/ )
2019-07-05 07:13:25 +03:00
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool start_object ( std : : size_t /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool key ( std : : string & /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
bool end_object ( )
{
return events_left - - > 0 ;
}
2021-03-24 09:15:18 +03:00
bool start_array ( std : : size_t /*unused*/ )
2018-09-25 21:34:25 +03:00
{
return events_left - - > 0 ;
}
bool end_array ( )
{
return events_left - - > 0 ;
}
2023-11-29 17:02:51 +03:00
bool parse_error ( std : : size_t /*unused*/ , const std : : string & /*unused*/ , const json : : exception & /*unused*/ ) // NOLINT(readability-convert-member-functions-to-static)
2018-09-25 21:34:25 +03:00
{
return false ;
}
private :
int events_left = 0 ;
} ;
2023-11-29 17:02:51 +03:00
} // namespace
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
TEST_CASE ( " Incomplete BSON Input " )
2018-09-25 21:34:25 +03:00
{
2018-10-25 00:39:30 +03:00
SECTION ( " Incomplete BSON Input 1 " )
2018-09-25 21:34:25 +03:00
{
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const incomplete_bson =
2023-11-29 17:02:51 +03:00
{
0x0D ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x08 , // entry: boolean
' e ' ,
' n ' ,
' t ' // unexpected EOF
} ;
2018-09-25 21:34:25 +03:00
2019-07-02 22:06:42 +03:00
json _ ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( _ = json : : from_bson ( incomplete_bson ) , " [json.exception.parse_error.110] parse error at byte 9: syntax error while parsing BSON cstring: unexpected end of input " , json : : parse_error & ) ;
2018-10-17 20:06:22 +03:00
2018-10-25 00:39:30 +03:00
CHECK ( json : : from_bson ( incomplete_bson , true , false ) . is_discarded ( ) ) ;
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
SaxCountdown scp ( 0 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! json : : sax_parse ( incomplete_bson , & scp , json : : input_format_t : : bson ) ) ;
2018-10-25 00:39:30 +03:00
}
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
SECTION ( " Incomplete BSON Input 2 " )
2018-09-25 21:34:25 +03:00
{
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const incomplete_bson =
2023-11-29 17:02:51 +03:00
{
0x0D ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x08 , // entry: boolean, unexpected EOF
} ;
2018-09-25 21:34:25 +03:00
2019-07-02 22:06:42 +03:00
json _ ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( _ = json : : from_bson ( incomplete_bson ) , " [json.exception.parse_error.110] parse error at byte 6: syntax error while parsing BSON cstring: unexpected end of input " , json : : parse_error & ) ;
2018-10-25 00:39:30 +03:00
CHECK ( json : : from_bson ( incomplete_bson , true , false ) . is_discarded ( ) ) ;
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
SaxCountdown scp ( 0 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! json : : sax_parse ( incomplete_bson , & scp , json : : input_format_t : : bson ) ) ;
2018-10-25 00:39:30 +03:00
}
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
SECTION ( " Incomplete BSON Input 3 " )
2018-09-25 21:34:25 +03:00
{
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const incomplete_bson =
2023-11-29 17:02:51 +03:00
{
0x41 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x04 , /// entry: embedded document
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x35 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x10 ,
0x00 ,
0x01 ,
0x00 ,
0x00 ,
0x00 ,
0x10 ,
0x00 ,
0x02 ,
0x00 ,
0x00 ,
0x00
// missing input data...
} ;
2018-09-25 21:34:25 +03:00
2019-07-02 22:06:42 +03:00
json _ ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( _ = json : : from_bson ( incomplete_bson ) , " [json.exception.parse_error.110] parse error at byte 28: syntax error while parsing BSON element list: unexpected end of input " , json : : parse_error & ) ;
2018-10-25 00:39:30 +03:00
CHECK ( json : : from_bson ( incomplete_bson , true , false ) . is_discarded ( ) ) ;
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
SaxCountdown scp ( 1 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! json : : sax_parse ( incomplete_bson , & scp , json : : input_format_t : : bson ) ) ;
2018-10-25 00:39:30 +03:00
}
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
SECTION ( " Incomplete BSON Input 4 " )
2018-09-25 21:34:25 +03:00
{
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const incomplete_bson =
2023-11-29 17:02:51 +03:00
{
0x0D ,
0x00 , // size (incomplete), unexpected EOF
} ;
2018-09-25 21:34:25 +03:00
2019-07-02 22:06:42 +03:00
json _ ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( _ = json : : from_bson ( incomplete_bson ) , " [json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BSON number: unexpected end of input " , json : : parse_error & ) ;
2018-10-25 00:39:30 +03:00
CHECK ( json : : from_bson ( incomplete_bson , true , false ) . is_discarded ( ) ) ;
2018-09-25 21:34:25 +03:00
2018-10-25 00:39:30 +03:00
SaxCountdown scp ( 0 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! json : : sax_parse ( incomplete_bson , & scp , json : : input_format_t : : bson ) ) ;
2018-10-25 00:39:30 +03:00
}
2018-10-31 22:30:24 +03:00
SECTION ( " Improve coverage " )
{
SECTION ( " key " )
{
2022-09-13 13:58:26 +03:00
json const j = { { " key " , " value " } } ;
2018-10-31 22:30:24 +03:00
auto bson_vec = json : : to_bson ( j ) ;
SaxCountdown scp ( 2 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! json : : sax_parse ( bson_vec , & scp , json : : input_format_t : : bson ) ) ;
2018-10-31 22:30:24 +03:00
}
SECTION ( " array " )
{
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , json : : array ( ) } } ;
2018-10-31 22:30:24 +03:00
auto bson_vec = json : : to_bson ( j ) ;
SaxCountdown scp ( 2 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! json : : sax_parse ( bson_vec , & scp , json : : input_format_t : : bson ) ) ;
2018-10-31 22:30:24 +03:00
}
}
2018-09-25 21:34:25 +03:00
}
2020-05-09 15:16:57 +03:00
TEST_CASE ( " Negative size of binary value " )
{
// invalid BSON: the size of the binary value is -1
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const input =
2023-11-29 17:02:51 +03:00
{
0x21 ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0x05 , // entry: binary
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0xFF ,
0xFF ,
0xFF ,
0xFF , // size of binary (little endian)
0x05 , // MD5 binary subtype
0xd7 ,
0x7e ,
0x27 ,
0x54 ,
0xbe ,
0x12 ,
0x37 ,
0xfe ,
0xd6 ,
0x0c ,
0x33 ,
0x98 ,
0x30 ,
0x3b ,
0x8d ,
0xc4 ,
0x00 // end marker
} ;
2020-12-16 23:44:35 +03:00
json _ ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( _ = json : : from_bson ( input ) , " [json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1 " , json : : parse_error ) ;
2020-05-09 15:16:57 +03:00
}
2018-10-07 08:52:12 +03:00
TEST_CASE ( " Unsupported BSON input " )
{
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const bson =
2023-11-29 17:02:51 +03:00
{
0x0C ,
0x00 ,
0x00 ,
0x00 , // size (little endian)
0xFF , // entry type: Min key (not supported yet)
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
0x00 // end marker
} ;
2018-10-07 08:52:12 +03:00
2019-07-02 22:06:42 +03:00
json _ ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( _ = json : : from_bson ( bson ) , " [json.exception.parse_error.114] parse error at byte 5: Unsupported BSON record type 0xFF " , json : : parse_error & ) ;
2018-10-07 08:52:12 +03:00
CHECK ( json : : from_bson ( bson , true , false ) . is_discarded ( ) ) ;
SaxCountdown scp ( 0 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! json : : sax_parse ( bson , & scp , json : : input_format_t : : bson ) ) ;
2018-10-07 08:52:12 +03:00
}
2018-10-16 20:13:07 +03:00
TEST_CASE ( " BSON numerical data " )
{
SECTION ( " number " )
{
SECTION ( " signed " )
{
SECTION ( " std::int64_t: INT64_MIN .. INT32_MIN-1 " )
{
2023-11-29 17:02:51 +03:00
std : : vector < int64_t > const numbers {
2022-09-19 09:02:50 +03:00
( std : : numeric_limits < int64_t > : : min ) ( ) ,
2018-10-16 20:13:07 +03:00
- 1000000000000000000LL ,
- 100000000000000000LL ,
- 10000000000000000LL ,
- 1000000000000000LL ,
- 100000000000000LL ,
- 10000000000000LL ,
- 1000000000000LL ,
- 100000000000LL ,
- 10000000000LL ,
2022-09-19 09:02:50 +03:00
static_cast < std : : int64_t > ( ( std : : numeric_limits < std : : int32_t > : : min ) ( ) ) - 1 ,
2018-10-16 20:13:07 +03:00
} ;
2022-09-13 13:58:26 +03:00
for ( const auto i : numbers )
2018-10-16 20:13:07 +03:00
{
2018-12-23 15:56:18 +03:00
CAPTURE ( i )
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , i } } ;
2018-10-16 20:13:07 +03:00
CHECK ( j . at ( " entry " ) . is_number_integer ( ) ) ;
2022-09-13 13:58:26 +03:00
std : : uint64_t const iu = * reinterpret_cast < const std : : uint64_t * > ( & i ) ;
std : : vector < std : : uint8_t > const expected_bson =
2023-11-29 17:02:51 +03:00
{
0x14u ,
0x00u ,
0x00u ,
0x00u , // size (little endian)
0x12u , /// entry: int64
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 0u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 1u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 2u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 3u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 4u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 5u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 6u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 7u ) ) & 0xffu ) ,
0x00u // end marker
} ;
2018-10-16 20:13:07 +03:00
const auto bson = json : : to_bson ( j ) ;
CHECK ( bson = = expected_bson ) ;
auto j_roundtrip = json : : from_bson ( bson ) ;
CHECK ( j_roundtrip . at ( " entry " ) . is_number_integer ( ) ) ;
CHECK ( j_roundtrip = = j ) ;
CHECK ( json : : from_bson ( bson , true , false ) = = j ) ;
}
}
SECTION ( " signed std::int32_t: INT32_MIN .. INT32_MAX " )
{
2023-11-29 17:02:51 +03:00
std : : vector < int32_t > const numbers {
2022-09-19 09:02:50 +03:00
( std : : numeric_limits < int32_t > : : min ) ( ) ,
2018-10-16 20:13:07 +03:00
- 2147483647L ,
- 1000000000L ,
- 100000000L ,
- 10000000L ,
- 1000000L ,
- 100000L ,
- 10000L ,
- 1000L ,
- 100L ,
- 10L ,
- 1L ,
0L ,
1L ,
10L ,
100L ,
1000L ,
10000L ,
100000L ,
1000000L ,
10000000L ,
100000000L ,
1000000000L ,
2147483646L ,
2023-11-29 17:02:51 +03:00
( std : : numeric_limits < int32_t > : : max ) ( ) } ;
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
for ( const auto i : numbers )
2018-10-16 20:13:07 +03:00
{
2018-12-23 15:56:18 +03:00
CAPTURE ( i )
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , i } } ;
2018-10-16 20:13:07 +03:00
CHECK ( j . at ( " entry " ) . is_number_integer ( ) ) ;
2022-09-13 13:58:26 +03:00
std : : uint32_t const iu = * reinterpret_cast < const std : : uint32_t * > ( & i ) ;
std : : vector < std : : uint8_t > const expected_bson =
2023-11-29 17:02:51 +03:00
{
0x10u ,
0x00u ,
0x00u ,
0x00u , // size (little endian)
0x10u , /// entry: int32
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 0u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 1u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 2u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 3u ) ) & 0xffu ) ,
0x00u // end marker
} ;
2018-10-16 20:13:07 +03:00
const auto bson = json : : to_bson ( j ) ;
CHECK ( bson = = expected_bson ) ;
auto j_roundtrip = json : : from_bson ( bson ) ;
CHECK ( j_roundtrip . at ( " entry " ) . is_number_integer ( ) ) ;
CHECK ( j_roundtrip = = j ) ;
CHECK ( json : : from_bson ( bson , true , false ) = = j ) ;
}
}
SECTION ( " signed std::int64_t: INT32_MAX+1 .. INT64_MAX " )
{
2023-11-29 17:02:51 +03:00
std : : vector < int64_t > const numbers {
2022-09-19 09:02:50 +03:00
( std : : numeric_limits < int64_t > : : max ) ( ) ,
2018-10-16 20:13:07 +03:00
1000000000000000000LL ,
100000000000000000LL ,
10000000000000000LL ,
1000000000000000LL ,
100000000000000LL ,
10000000000000LL ,
1000000000000LL ,
100000000000LL ,
10000000000LL ,
2022-09-19 09:02:50 +03:00
static_cast < std : : int64_t > ( ( std : : numeric_limits < int32_t > : : max ) ( ) ) + 1 ,
2018-10-16 20:13:07 +03:00
} ;
2022-09-13 13:58:26 +03:00
for ( const auto i : numbers )
2018-10-16 20:13:07 +03:00
{
2018-12-23 15:56:18 +03:00
CAPTURE ( i )
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , i } } ;
2018-10-16 20:13:07 +03:00
CHECK ( j . at ( " entry " ) . is_number_integer ( ) ) ;
2022-09-13 13:58:26 +03:00
std : : uint64_t const iu = * reinterpret_cast < const std : : uint64_t * > ( & i ) ;
std : : vector < std : : uint8_t > const expected_bson =
2023-11-29 17:02:51 +03:00
{
0x14u ,
0x00u ,
0x00u ,
0x00u , // size (little endian)
0x12u , /// entry: int64
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 0u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 1u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 2u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 3u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 4u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 5u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 6u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 7u ) ) & 0xffu ) ,
0x00u // end marker
} ;
2018-10-16 20:13:07 +03:00
const auto bson = json : : to_bson ( j ) ;
CHECK ( bson = = expected_bson ) ;
auto j_roundtrip = json : : from_bson ( bson ) ;
CHECK ( j_roundtrip . at ( " entry " ) . is_number_integer ( ) ) ;
CHECK ( j_roundtrip = = j ) ;
CHECK ( json : : from_bson ( bson , true , false ) = = j ) ;
}
}
}
SECTION ( " unsigned " )
{
SECTION ( " unsigned std::uint64_t: 0 .. INT32_MAX " )
{
2023-11-29 17:02:51 +03:00
std : : vector < std : : uint64_t > const numbers {
2018-10-16 20:13:07 +03:00
0ULL ,
1ULL ,
10ULL ,
100ULL ,
1000ULL ,
10000ULL ,
100000ULL ,
1000000ULL ,
10000000ULL ,
100000000ULL ,
1000000000ULL ,
2147483646ULL ,
2023-11-29 17:02:51 +03:00
static_cast < std : : uint64_t > ( ( std : : numeric_limits < int32_t > : : max ) ( ) ) } ;
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
for ( const auto i : numbers )
2018-10-16 20:13:07 +03:00
{
2018-12-23 15:56:18 +03:00
CAPTURE ( i )
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , i } } ;
2018-10-16 20:13:07 +03:00
2018-10-25 15:27:55 +03:00
auto iu = i ;
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected_bson =
2023-11-29 17:02:51 +03:00
{
0x10u ,
0x00u ,
0x00u ,
0x00u , // size (little endian)
0x10u , /// entry: int32
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 0u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 1u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 2u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 3u ) ) & 0xffu ) ,
0x00u // end marker
} ;
2018-10-16 20:13:07 +03:00
const auto bson = json : : to_bson ( j ) ;
CHECK ( bson = = expected_bson ) ;
auto j_roundtrip = json : : from_bson ( bson ) ;
CHECK ( j . at ( " entry " ) . is_number_unsigned ( ) ) ;
CHECK ( j_roundtrip . at ( " entry " ) . is_number_integer ( ) ) ;
CHECK ( j_roundtrip = = j ) ;
CHECK ( json : : from_bson ( bson , true , false ) = = j ) ;
}
}
SECTION ( " unsigned std::uint64_t: INT32_MAX+1 .. INT64_MAX " )
{
2023-11-29 17:02:51 +03:00
std : : vector < std : : uint64_t > const numbers {
2022-09-19 09:02:50 +03:00
static_cast < std : : uint64_t > ( ( std : : numeric_limits < std : : int32_t > : : max ) ( ) ) + 1 ,
2018-10-16 20:13:07 +03:00
4000000000ULL ,
2022-09-19 09:02:50 +03:00
static_cast < std : : uint64_t > ( ( std : : numeric_limits < std : : uint32_t > : : max ) ( ) ) ,
2018-10-16 20:13:07 +03:00
10000000000ULL ,
100000000000ULL ,
1000000000000ULL ,
10000000000000ULL ,
100000000000000ULL ,
1000000000000000ULL ,
10000000000000000ULL ,
100000000000000000ULL ,
1000000000000000000ULL ,
2022-09-19 09:02:50 +03:00
static_cast < std : : uint64_t > ( ( std : : numeric_limits < std : : int64_t > : : max ) ( ) ) ,
2018-10-16 20:13:07 +03:00
} ;
2022-09-13 13:58:26 +03:00
for ( const auto i : numbers )
2018-10-16 20:13:07 +03:00
{
2018-12-23 15:56:18 +03:00
CAPTURE ( i )
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , i } } ;
2018-10-16 20:13:07 +03:00
2018-10-25 15:27:55 +03:00
auto iu = i ;
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected_bson =
2023-11-29 17:02:51 +03:00
{
0x14u ,
0x00u ,
0x00u ,
0x00u , // size (little endian)
0x12u , /// entry: int64
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 0u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 1u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 2u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 3u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 4u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 5u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 6u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 7u ) ) & 0xffu ) ,
0x00u // end marker
} ;
2018-10-16 20:13:07 +03:00
const auto bson = json : : to_bson ( j ) ;
CHECK ( bson = = expected_bson ) ;
auto j_roundtrip = json : : from_bson ( bson ) ;
CHECK ( j . at ( " entry " ) . is_number_unsigned ( ) ) ;
CHECK ( j_roundtrip . at ( " entry " ) . is_number_integer ( ) ) ;
CHECK ( j_roundtrip = = j ) ;
CHECK ( json : : from_bson ( bson , true , false ) = = j ) ;
}
}
SECTION ( " unsigned std::uint64_t: INT64_MAX+1 .. UINT64_MAX " )
{
2023-11-29 17:02:51 +03:00
std : : vector < std : : uint64_t > const numbers {
2022-09-19 09:02:50 +03:00
static_cast < std : : uint64_t > ( ( std : : numeric_limits < std : : int64_t > : : max ) ( ) ) + 1ULL ,
2018-10-16 20:13:07 +03:00
10000000000000000000ULL ,
18000000000000000000ULL ,
2022-09-19 09:02:50 +03:00
( std : : numeric_limits < std : : uint64_t > : : max ) ( ) - 1ULL ,
( std : : numeric_limits < std : : uint64_t > : : max ) ( ) ,
2018-10-16 20:13:07 +03:00
} ;
2022-09-13 13:58:26 +03:00
for ( const auto i : numbers )
2018-10-16 20:13:07 +03:00
{
2018-12-23 15:56:18 +03:00
CAPTURE ( i )
2018-10-16 20:13:07 +03:00
2022-09-13 13:58:26 +03:00
json const j =
2023-11-29 17:02:51 +03:00
{
{ " entry " , i } } ;
2018-10-16 20:13:07 +03:00
2018-10-25 15:27:55 +03:00
auto iu = i ;
2022-09-13 13:58:26 +03:00
std : : vector < std : : uint8_t > const expected_bson =
2023-11-29 17:02:51 +03:00
{
0x14u ,
0x00u ,
0x00u ,
0x00u , // size (little endian)
0x12u , /// entry: int64
' e ' ,
' n ' ,
' t ' ,
' r ' ,
' y ' ,
' \x00 ' ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 0u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 1u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 2u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 3u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 4u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 5u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 6u ) ) & 0xffu ) ,
static_cast < std : : uint8_t > ( ( iu > > ( 8u * 7u ) ) & 0xffu ) ,
0x00u // end marker
} ;
2018-10-16 20:13:07 +03:00
CHECK_THROWS_AS ( json : : to_bson ( j ) , json : : out_of_range & ) ;
2021-01-02 18:13:04 +03:00
# if JSON_DIAGNOSTICS
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 " ) ;
# else
2019-01-13 19:41:21 +03:00
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 " ) ;
2021-01-02 18:13:04 +03:00
# endif
2018-10-16 20:13:07 +03:00
}
}
}
}
}
2018-10-24 16:43:37 +03:00
2019-01-13 19:41:21 +03:00
TEST_CASE ( " BSON roundtrips " * doctest : : skip ( ) )
2018-10-24 16:43:37 +03:00
{
SECTION ( " reference files " )
{
2022-09-13 13:58:26 +03:00
for ( const std : : string filename :
2023-11-29 17:02:51 +03:00
{
TEST_DATA_DIRECTORY " /json.org/1.json " ,
TEST_DATA_DIRECTORY " /json.org/2.json " ,
TEST_DATA_DIRECTORY " /json.org/3.json " ,
TEST_DATA_DIRECTORY " /json.org/4.json " ,
TEST_DATA_DIRECTORY " /json.org/5.json " } )
2018-10-24 16:43:37 +03:00
{
2018-12-23 15:56:18 +03:00
CAPTURE ( filename )
2018-10-24 16:43:37 +03:00
{
2020-05-06 22:24:00 +03:00
INFO_WITH_TEMP ( filename + " : std::vector<std::uint8_t> " ) ;
2018-10-24 16:43:37 +03:00
// parse JSON file
std : : ifstream f_json ( filename ) ;
json j1 = json : : parse ( f_json ) ;
// parse BSON file
2020-07-23 15:02:12 +03:00
auto packed = utils : : read_binary_file ( filename + " .bson " ) ;
2018-10-24 16:43:37 +03:00
json j2 ;
CHECK_NOTHROW ( j2 = json : : from_bson ( packed ) ) ;
// compare parsed JSON values
CHECK ( j1 = = j2 ) ;
}
{
2019-01-13 19:41:21 +03:00
INFO_WITH_TEMP ( filename + " : std::ifstream " ) ;
2018-10-24 16:43:37 +03:00
// parse JSON file
std : : ifstream f_json ( filename ) ;
json j1 = json : : parse ( f_json ) ;
// parse BSON file
std : : ifstream f_bson ( filename + " .bson " , std : : ios : : binary ) ;
json j2 ;
CHECK_NOTHROW ( j2 = json : : from_bson ( f_bson ) ) ;
// compare parsed JSON values
CHECK ( j1 = = j2 ) ;
}
{
2019-01-13 19:41:21 +03:00
INFO_WITH_TEMP ( filename + " : uint8_t* and size " ) ;
2018-10-24 16:43:37 +03:00
// parse JSON file
std : : ifstream f_json ( filename ) ;
json j1 = json : : parse ( f_json ) ;
// parse BSON file
2020-07-23 15:02:12 +03:00
auto packed = utils : : read_binary_file ( filename + " .bson " ) ;
2018-10-24 16:43:37 +03:00
json j2 ;
CHECK_NOTHROW ( j2 = json : : from_bson ( { packed . data ( ) , packed . size ( ) } ) ) ;
// compare parsed JSON values
CHECK ( j1 = = j2 ) ;
}
{
2019-01-13 19:41:21 +03:00
INFO_WITH_TEMP ( filename + " : output to output adapters " ) ;
2018-10-24 16:43:37 +03:00
// parse JSON file
std : : ifstream f_json ( filename ) ;
2022-09-13 13:58:26 +03:00
json const j1 = json : : parse ( f_json ) ;
2018-10-24 16:43:37 +03:00
// parse BSON file
2020-07-23 15:02:12 +03:00
auto packed = utils : : read_binary_file ( filename + " .bson " ) ;
2018-10-24 16:43:37 +03:00
2018-10-25 00:39:30 +03:00
{
2020-05-06 22:24:00 +03:00
INFO_WITH_TEMP ( filename + " : output adapters: std::vector<std::uint8_t> " ) ;
std : : vector < std : : uint8_t > vec ;
2018-10-25 00:39:30 +03:00
json : : to_bson ( j1 , vec ) ;
2018-10-25 14:01:18 +03:00
if ( vec ! = packed )
{
// the exact serializations may differ due to the order of
// object keys; in these cases, just compare whether both
// serializations create the same JSON value
CHECK ( json : : from_bson ( vec ) = = json : : from_bson ( packed ) ) ;
}
2018-10-25 00:39:30 +03:00
}
2018-10-24 16:43:37 +03:00
}
}
}
}