mirror of
https://github.com/nlohmann/json.git
synced 2026-10-26 07:48:55 +00:00
Read ND-arrays of half floats and enforce the char limit in ND-arrays
BJData N-dimensional arrays with element type 'h' (float16) failed to read with a misleading parse_error.112, because the reader's dtype name table lacked 'h'. It now maps to "half", the JData name. The writer still does not pack "half"; such an object is written as a plain object and reads back equal. BJData limits 'C' (char) to 0..127 in Draft 2 and Draft 3, and the 1-D reader enforces this, but ND-arrays with _ArrayType_ "char" wrote values up to 255 as 'C' elements and read them back without a check. The writer now only packs values in 0..127 (others fall back to a plain object that round-trips), and the reader rejects 'C' elements above 127 with parse_error.113 like the 1-D reader. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
3 files changed
+88
-2
No files matched your search
@@ -2916,6 +2916,29 @@ TEST_CASE("BJData")
|
||||
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
||||
}
|
||||
|
||||
SECTION("ndarray with _ArrayType_ \"char\" is limited to 0..127")
|
||||
{
|
||||
// both BJData drafts limit a char to 0..127; larger values must not
|
||||
// be written as 'C' elements (the reader rejects them), so such an
|
||||
// object stays a plain object that round-trips
|
||||
for (const auto version : {json::bjdata_version_t::draft2, json::bjdata_version_t::draft3})
|
||||
{
|
||||
json const j_ok = json({{"_ArrayType_", "char"}, {"_ArraySize_", {2, 2}}, {"_ArrayData_", {0, 97, 98, 127}}});
|
||||
const auto out_ok = json::to_bjdata(j_ok, true, true, version);
|
||||
CHECK(out_ok == std::vector<uint8_t>({'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 2, 2, 0, 97, 98, 127}));
|
||||
CHECK(json::from_bjdata(out_ok) == j_ok);
|
||||
|
||||
for (const int bad : {128, 200, 255, 256, -1})
|
||||
{
|
||||
CAPTURE(bad)
|
||||
json const j_bad = json({{"_ArrayType_", "char"}, {"_ArraySize_", {2, 2}}, {"_ArrayData_", {97, 98, 99, bad}}});
|
||||
const auto out_bad = json::to_bjdata(j_bad, true, true, version);
|
||||
CHECK(out_bad.at(0) == '{');
|
||||
CHECK(json::from_bjdata(out_bad) == j_bad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ndarray annotation keys are read back in the documented order")
|
||||
{
|
||||
// from_bjdata() must emit the annotation object's keys in the order
|
||||
@@ -3324,6 +3347,44 @@ TEST_CASE("BJData")
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ndarray of half-precision floats can be read")
|
||||
{
|
||||
// the 'h' element type is read as _ArrayType_ "half"
|
||||
json _;
|
||||
const std::vector<uint8_t> half = {'[', '$', 'h', '#', '[', '$', 'U', '#', 'U', 2, 2, 2, 0x00, 0x3C, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44};
|
||||
const json j_half = json::parse(R"({"_ArrayType_":"half","_ArraySize_":[2,2],"_ArrayData_":[1.0,2.0,3.0,4.0]})");
|
||||
CHECK(json::from_bjdata(half) == j_half);
|
||||
|
||||
// the writer does not pack "half": it writes a plain object that reads back equal
|
||||
CHECK(json::from_bjdata(json::to_bjdata(j_half)) == j_half);
|
||||
CHECK(json::from_bjdata(json::to_bjdata(j_half, true, true, json::bjdata_version_t::draft3)) == j_half);
|
||||
|
||||
// truncated half data
|
||||
CHECK_THROWS_AS(_ = json::from_bjdata(std::vector<uint8_t>(half.begin(), half.end() - 1)), json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("ndarray of char rejects values above 127")
|
||||
{
|
||||
json _;
|
||||
const std::vector<uint8_t> ok = {'[', '$', 'C', '#', '[', '$', 'U', '#', 'U', 2, 2, 2, 'a', 'b', 'c', 0x7F};
|
||||
CHECK(json::from_bjdata(ok) == json::parse(R"({"_ArrayType_":"char","_ArraySize_":[2,2],"_ArrayData_":[97,98,99,127]})"));
|
||||
|
||||
std::vector<uint8_t> bad = ok;
|
||||
bad.back() = 0xC8;
|
||||
CHECK_THROWS_AS(_ = json::from_bjdata(bad), json::parse_error&);
|
||||
CHECK(json::from_bjdata(bad, true, false).is_discarded());
|
||||
|
||||
// the same restriction holds for 1-D arrays
|
||||
CHECK_THROWS_AS(_ = json::from_bjdata(std::vector<uint8_t>({'[', '$', 'C', '#', 'i', 1, 0xC8})), json::parse_error&);
|
||||
|
||||
// truncated input
|
||||
CHECK_THROWS_AS(_ = json::from_bjdata(std::vector<uint8_t>(ok.begin(), ok.end() - 1)), json::parse_error&);
|
||||
|
||||
// 'B' (byte) elements may use the full range
|
||||
const std::vector<uint8_t> byte_nd = {'[', '$', 'B', '#', '[', '$', 'U', '#', 'U', 2, 2, 2, 1, 2, 3, 0xC8};
|
||||
CHECK(json::from_bjdata(byte_nd) == json::parse(R"({"_ArrayType_":"byte","_ArraySize_":[2,2],"_ArrayData_":[1,2,3,200]})"));
|
||||
}
|
||||
|
||||
SECTION("do not accept NTFZ markers in ndarray optimized type (with count)")
|
||||
{
|
||||
json _;
|
||||
|
||||
Reference in new issue
Block a user