diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index b1c82208a..cd7e58946 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -2111,11 +2111,31 @@ class binary_reader { return false; } - result = ( (!dim.empty()) ? 1 : 0 ); - for (auto i : dim) + if (dim.size() == 1 || (dim.size() > 1 && dim.at(0) == 1)) // return normal array size if 1D vector { - result *= i; + result = dim.at(dim.size() - 1); + return true; } + if (!dim.empty()) // if ndarray, convert to a object in JData annotated array format + { + string_t key = "_ArraySize_"; + if (JSON_HEDLEY_UNLIKELY((!sax->start_object(static_cast(-1)) || !sax->key(key) || !sax->start_array(dim.size())))) + { + return false; + } + result = 1; + for (auto i : dim) + { + result *= i; + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(i))) + { + return false; + } + } + result |= 0x8000000000000000ull; // low 63 bit of result stores the total element count, sign-bit indicates ndarray + return sax->end_array(); + } + result = 0; return true; } @@ -2379,6 +2399,48 @@ class binary_reader return false; } + // detect and encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata): + // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]} + + if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= 0x8000000000000000ull) + { + std::map bjdtype = {{'U', "uint8"}, {'i', "int8"}, {'u', "uint16"}, {'I', "int16"}, + {'m', "uint32"}, {'l', "int32"}, {'M', "uint64"}, {'L', "int64"}, {'d', "single"}, {'D', "double"}, {'C', "char"} + }; + + string_t key = "_ArrayType_"; + if (JSON_HEDLEY_UNLIKELY(bjdtype.find(size_and_type.second) == bjdtype.end() || !sax->key(key) || !sax->string(bjdtype[size_and_type.second]) )) + { + return false; + } + + if (size_and_type.second == 'C') + { + size_and_type.second = 'U'; + } + + size_and_type.first -= 0x8000000000000000ull; + key = "_ArrayData_"; + if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) )) + { + return false; + } + + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + + if (JSON_HEDLEY_UNLIKELY((!sax->end_array() || !sax->end_object()))) + { + return false; + } + return true; + } + if (size_and_type.first != string_t::npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 2bfb4714f..9e45cb3de 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -873,6 +873,14 @@ class binary_writer case value_t::object: { + if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end()) + { + if (write_bjdata_ndarray(*j.m_value.object, use_count, use_type) == 0) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + { + break; + } + } + if (add_prefix) { oa->write_character(to_char_type('{')); @@ -1590,6 +1598,117 @@ class binary_writer return 'D'; // float 64 } + /*! + @return 0 if the object is successfully converted to a bjdata ndarray, 1 if the type or size is invalid + */ + int write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type) + { + std::map bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'}, + {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'} + }; + + string_t key = "_ArrayType_"; + if (bjdtype.find(value.at(key)) == bjdtype.end()) + { + return 1; + } + CharType dtype = bjdtype[value.at(key)]; + + key = "_ArraySize_"; + std::size_t len = (value.at(key).empty() ? 0 : 1); + for (const auto& el : value.at(key)) + { + len *= el.m_value.number_unsigned; + } + + key = "_ArrayData_"; + if (value.at(key).size() != len) + { + return 1; + } + + oa->write_character('['); + oa->write_character('$'); + oa->write_character(dtype); + oa->write_character('#'); + + key = "_ArraySize_"; + write_ubjson(value.at(key), use_count, use_type, true, true); + + key = "_ArrayData_"; + if (dtype == 'U' || dtype == 'C') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'i') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'u') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'I') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'm') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'l') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'M') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'L') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'd') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + else if (dtype == 'D') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + return 0; + } + /////////////////////// // Utility functions // /////////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a425adcb7..e03c8a58d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -10487,11 +10487,31 @@ class binary_reader { return false; } - result = ( (!dim.empty()) ? 1 : 0 ); - for (auto i : dim) + if (dim.size() == 1 || (dim.size() > 1 && dim.at(0) == 1)) // return normal array size if 1D vector { - result *= i; + result = dim.at(dim.size() - 1); + return true; } + if (!dim.empty()) // if ndarray, convert to a object in JData annotated array format + { + string_t key = "_ArraySize_"; + if (JSON_HEDLEY_UNLIKELY((!sax->start_object(static_cast(-1)) || !sax->key(key) || !sax->start_array(dim.size())))) + { + return false; + } + result = 1; + for (auto i : dim) + { + result *= i; + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(i))) + { + return false; + } + } + result |= 0x8000000000000000ull; // low 63 bit of result stores the total element count, sign-bit indicates ndarray + return sax->end_array(); + } + result = 0; return true; } @@ -10755,6 +10775,48 @@ class binary_reader return false; } + // detect and encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata): + // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]} + + if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= 0x8000000000000000ull) + { + std::map bjdtype = {{'U', "uint8"}, {'i', "int8"}, {'u', "uint16"}, {'I', "int16"}, + {'m', "uint32"}, {'l', "int32"}, {'M', "uint64"}, {'L', "int64"}, {'d', "single"}, {'D', "double"}, {'C', "char"} + }; + + string_t key = "_ArrayType_"; + if (JSON_HEDLEY_UNLIKELY(bjdtype.find(size_and_type.second) == bjdtype.end() || !sax->key(key) || !sax->string(bjdtype[size_and_type.second]) )) + { + return false; + } + + if (size_and_type.second == 'C') + { + size_and_type.second = 'U'; + } + + size_and_type.first -= 0x8000000000000000ull; + key = "_ArrayData_"; + if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) )) + { + return false; + } + + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + + if (JSON_HEDLEY_UNLIKELY((!sax->end_array() || !sax->end_object()))) + { + return false; + } + return true; + } + if (size_and_type.first != string_t::npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) @@ -14706,6 +14768,14 @@ class binary_writer case value_t::object: { + if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end()) + { + if (write_bjdata_ndarray(*j.m_value.object, use_count, use_type) == 0) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + { + break; + } + } + if (add_prefix) { oa->write_character(to_char_type('{')); @@ -15423,6 +15493,117 @@ class binary_writer return 'D'; // float 64 } + /*! + @return 0 if the object is successfully converted to a bjdata ndarray, 1 if the type or size is invalid + */ + int write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type) + { + std::map bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'}, + {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'} + }; + + string_t key = "_ArrayType_"; + if (bjdtype.find(value.at(key)) == bjdtype.end()) + { + return 1; + } + CharType dtype = bjdtype[value.at(key)]; + + key = "_ArraySize_"; + std::size_t len = (value.at(key).empty() ? 0 : 1); + for (const auto& el : value.at(key)) + { + len *= el.m_value.number_unsigned; + } + + key = "_ArrayData_"; + if (value.at(key).size() != len) + { + return 1; + } + + oa->write_character('['); + oa->write_character('$'); + oa->write_character(dtype); + oa->write_character('#'); + + key = "_ArraySize_"; + write_ubjson(value.at(key), use_count, use_type, true, true); + + key = "_ArrayData_"; + if (dtype == 'U' || dtype == 'C') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'i') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'u') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'I') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'm') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'l') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'M') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'L') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'd') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + else if (dtype == 'D') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + return 0; + } + /////////////////////// // Utility functions // /////////////////////// diff --git a/test/src/unit-bjdata.cpp b/test/src/unit-bjdata.cpp index c21e5a7bb..de7a224fb 100644 --- a/test/src/unit-bjdata.cpp +++ b/test/src/unit-bjdata.cpp @@ -2318,6 +2318,8 @@ TEST_CASE("BJData") SECTION("optimized ndarray (type and vector-size as optimized 1D array)") { // create vector with two elements of the same type + std::vector v_0 = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 1, 0}; + std::vector v_1 = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 1, 2, 0x7F, 0x7F}; std::vector v_N = {'[', '$', 'N', '#', '[', '$', 'i', '#', 'i', 2, 1, 2}; std::vector v_T = {'[', '$', 'T', '#', '[', '$', 'i', '#', 'i', 2, 1, 2}; std::vector v_F = {'[', '$', 'F', '#', '[', '$', 'i', '#', 'i', 2, 1, 2}; @@ -2335,6 +2337,8 @@ TEST_CASE("BJData") std::vector v_C = {'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 'a', 'a'}; // check if vector is parsed correctly + CHECK(json::from_bjdata(v_0) == json::array()); + CHECK(json::from_bjdata(v_1) == json({127, 127})); CHECK(json::from_bjdata(v_N) == json::array()); CHECK(json::from_bjdata(v_T) == json({true, true})); CHECK(json::from_bjdata(v_F) == json({false, false})); @@ -2350,26 +2354,52 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + } + + SECTION("optimized ndarray (type and vector-size ndarray with JData annotations)") + { + // create vector with 0, 1, 2 elements of the same type + std::vector v_e = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 0xFE, 0xFF}; + std::vector v_U = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + std::vector v_i = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + std::vector v_u = {'[', '$', 'u', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00}; + std::vector v_I = {'[', '$', 'I', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00}; + std::vector v_m = {'[', '$', 'm', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00}; + std::vector v_l = {'[', '$', 'l', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00}; + std::vector v_M = {'[', '$', 'M', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector v_L = {'[', '$', 'L', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector v_d = {'[', '$', 'd', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0xA0, 0x40, 0x00, 0x00, 0xC0, 0x40}; + std::vector v_D = {'[', '$', 'D', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40}; + std::vector v_C = {'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 'a', 'b', 'c', 'd', 'e', 'f'}; + + // check if vector is parsed correctly + CHECK(json::from_bjdata(v_e) == json({{"_ArrayData_", {254, 255}}, {"_ArraySize_", {2, 1}}, {"_ArrayType_", "uint8"}})); + CHECK(json::from_bjdata(v_U) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint8"}})); + CHECK(json::from_bjdata(v_i) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int8"}})); + CHECK(json::from_bjdata(v_i) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int8"}})); + CHECK(json::from_bjdata(v_u) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint16"}})); + CHECK(json::from_bjdata(v_I) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int16"}})); + CHECK(json::from_bjdata(v_m) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint32"}})); + CHECK(json::from_bjdata(v_l) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int32"}})); + CHECK(json::from_bjdata(v_M) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint64"}})); + CHECK(json::from_bjdata(v_L) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int64"}})); + CHECK(json::from_bjdata(v_d) == json({{"_ArrayData_", {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "single"}})); + CHECK(json::from_bjdata(v_D) == json({{"_ArrayData_", {1., 2., 3., 4., 5., 6.}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "double"}})); + CHECK(json::from_bjdata(v_C) == json({{"_ArrayData_", {'a', 'b', 'c', 'd', 'e', 'f'}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "char"}})); -#ifdef BJDATA_TEST_ROUNDTRIP // round-trip to vectorized size (ndarray) is not yet supported // roundtrip: output should be optimized - std::vector v_empty = {'[', '#', 'i', 0}; - CHECK(json::to_bjdata(json::from_bjdata(v_N), true, true) == v_empty); - CHECK(json::to_bjdata(json::from_bjdata(v_T), true, true) == v_T); - CHECK(json::to_bjdata(json::from_bjdata(v_F), true, true) == v_F); - CHECK(json::to_bjdata(json::from_bjdata(v_Z), true, true) == v_Z); - CHECK(json::to_bjdata(json::from_bjdata(v_i), true, true) == v_i); + CHECK(json::to_bjdata(json::from_bjdata(v_e), true, true) == v_e); CHECK(json::to_bjdata(json::from_bjdata(v_U), true, true) == v_U); - CHECK(json::to_bjdata(json::from_bjdata(v_I), true, true) == v_I); + CHECK(json::to_bjdata(json::from_bjdata(v_i), true, true) == v_i); CHECK(json::to_bjdata(json::from_bjdata(v_u), true, true) == v_u); - CHECK(json::to_bjdata(json::from_bjdata(v_l), true, true) == v_l); + CHECK(json::to_bjdata(json::from_bjdata(v_I), true, true) == v_I); CHECK(json::to_bjdata(json::from_bjdata(v_m), true, true) == v_m); - CHECK(json::to_bjdata(json::from_bjdata(v_L), true, true) == v_L); + CHECK(json::to_bjdata(json::from_bjdata(v_l), true, true) == v_l); CHECK(json::to_bjdata(json::from_bjdata(v_M), true, true) == v_M); + CHECK(json::to_bjdata(json::from_bjdata(v_L), true, true) == v_L); + CHECK(json::to_bjdata(json::from_bjdata(v_d), true, true) == v_d); CHECK(json::to_bjdata(json::from_bjdata(v_D), true, true) == v_D); - CHECK(json::to_bjdata(json::from_bjdata(v_S), true, true) == v_S); - CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_S); // char is serialized to string -#endif + CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_C); } SECTION("optimized ndarray (type and vector-size as 1D array)") @@ -2407,27 +2437,6 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); - -#ifdef BJDATA_TEST_ROUNDTRIP // round-trip to vectorized size (ndarray) is not yet supported - // roundtrip: output should be optimized - std::vector v_empty = {'[', '#', 'i', 0}; - CHECK(json::to_bjdata(json::from_bjdata(v_N), true, true) == v_empty); - CHECK(json::to_bjdata(json::from_bjdata(v_T), true, true) == v_T); - CHECK(json::to_bjdata(json::from_bjdata(v_F), true, true) == v_F); - CHECK(json::to_bjdata(json::from_bjdata(v_Z), true, true) == v_Z); - CHECK(json::to_bjdata(json::from_bjdata(v_i), true, true) == v_i); - CHECK(json::to_bjdata(json::from_bjdata(v_U), true, true) == v_U); - CHECK(json::to_bjdata(json::from_bjdata(v_I), true, true) == v_I); - CHECK(json::to_bjdata(json::from_bjdata(v_u), true, true) == v_u); - CHECK(json::to_bjdata(json::from_bjdata(v_l), true, true) == v_l); - CHECK(json::to_bjdata(json::from_bjdata(v_m), true, true) == v_m); - CHECK(json::to_bjdata(json::from_bjdata(v_L), true, true) == v_L); - CHECK(json::to_bjdata(json::from_bjdata(v_M), true, true) == v_M); - CHECK(json::to_bjdata(json::from_bjdata(v_D), true, true) == v_D); - CHECK(json::to_bjdata(json::from_bjdata(v_S), true, true) == v_S); - CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_S); // char is serialized to string -#endif - } SECTION("optimized ndarray (type and vector-size as size-optimized array)") @@ -2465,27 +2474,17 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + } -#ifdef BJDATA_TEST_ROUNDTRIP // round-trip to vectorized size (ndarray) is not yet supported - // roundtrip: output should be optimized - std::vector v_empty = {'[', '#', 'i', 0}; - CHECK(json::to_bjdata(json::from_bjdata(v_N), true, true) == v_empty); - CHECK(json::to_bjdata(json::from_bjdata(v_T), true, true) == v_T); - CHECK(json::to_bjdata(json::from_bjdata(v_F), true, true) == v_F); - CHECK(json::to_bjdata(json::from_bjdata(v_Z), true, true) == v_Z); - CHECK(json::to_bjdata(json::from_bjdata(v_i), true, true) == v_i); - CHECK(json::to_bjdata(json::from_bjdata(v_U), true, true) == v_U); - CHECK(json::to_bjdata(json::from_bjdata(v_I), true, true) == v_I); - CHECK(json::to_bjdata(json::from_bjdata(v_u), true, true) == v_u); - CHECK(json::to_bjdata(json::from_bjdata(v_l), true, true) == v_l); - CHECK(json::to_bjdata(json::from_bjdata(v_m), true, true) == v_m); - CHECK(json::to_bjdata(json::from_bjdata(v_L), true, true) == v_L); - CHECK(json::to_bjdata(json::from_bjdata(v_M), true, true) == v_M); - CHECK(json::to_bjdata(json::from_bjdata(v_D), true, true) == v_D); - CHECK(json::to_bjdata(json::from_bjdata(v_S), true, true) == v_S); - CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_S); // char is serialized to string -#endif + SECTION("invalid ndarray annotations remains as object") + { + // check if invalid ND array annotations stay as object + json j_type = json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "invalidtype"}}); + json j_size = json({{"_ArrayData_", {1, 2, 3, 4, 5}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint8"}}); + // roundtrip: output should stay as object + CHECK(json::from_bjdata(json::to_bjdata(j_type), true, true) == j_type); + CHECK(json::from_bjdata(json::to_bjdata(j_size), true, true) == j_size); } } }