From 27c8307562be84b8588dfe77f3ff393906ac4468 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Sep 2024 22:53:27 +0300 Subject: [PATCH] add dv::value support to binary_json --- src/coders/binary_json.hpp | 5 + src/coders/binary_json_dv.cpp | 168 ++++++++++++++++++++++++++++++++++ src/data/dv.hpp | 4 +- test/coders/binary_json.cpp | 34 +++++++ 4 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 src/coders/binary_json_dv.cpp diff --git a/src/coders/binary_json.hpp b/src/coders/binary_json.hpp index 1a7d9330..d22e1911 100644 --- a/src/coders/binary_json.hpp +++ b/src/coders/binary_json.hpp @@ -3,6 +3,7 @@ #include #include +#include "data/dv.hpp" #include "data/dynamic_fwd.hpp" namespace dynamic { @@ -25,6 +26,10 @@ namespace json { inline constexpr int BJSON_TYPE_NULL = 0xC; inline constexpr int BJSON_TYPE_CDOCUMENT = 0x1F; + std::vector to_binaryDV(const dv::value& obj, bool compress = false); + + dv::value from_binaryDV(const ubyte* src, size_t size); + std::vector to_binary( const dynamic::Map* obj, bool compress = false ); diff --git a/src/coders/binary_json_dv.cpp b/src/coders/binary_json_dv.cpp new file mode 100644 index 00000000..821e6849 --- /dev/null +++ b/src/coders/binary_json_dv.cpp @@ -0,0 +1,168 @@ +#include "binary_json.hpp" + +#include + +#include "data/dv.hpp" +#include "byte_utils.hpp" +#include "gzip.hpp" + +using namespace json; + +static void to_binary(ByteBuilder& builder, const dv::value& value) { + switch (value.getType()) { + case dv::value_type::none: + throw std::runtime_error("none value is not implemented"); + case dv::value_type::object: { + const auto bytes = json::to_binaryDV(value); + builder.put(bytes.data(), bytes.size()); + break; + } + case dv::value_type::list: + builder.put(BJSON_TYPE_LIST); + for (const auto& element : value) { + to_binary(builder, element); + } + builder.put(BJSON_END); + break; + case dv::value_type::bytes: { + const auto& bytes = value.asBytes(); + builder.put(BJSON_TYPE_BYTES); + builder.putInt32(bytes.size()); + builder.put(bytes.data(), bytes.size()); + break; + } + case dv::value_type::integer: { + auto val = value.asInteger(); + if (val >= 0 && val <= 255) { + builder.put(BJSON_TYPE_BYTE); + builder.put(val); + } else if (val >= INT16_MIN && val <= INT16_MAX) { + builder.put(BJSON_TYPE_INT16); + builder.putInt16(val); + } else if (val >= INT32_MIN && val <= INT32_MAX) { + builder.put(BJSON_TYPE_INT32); + builder.putInt32(val); + } else { + builder.put(BJSON_TYPE_INT64); + builder.putInt64(val); + } + break; + } + case dv::value_type::number: + builder.put(BJSON_TYPE_NUMBER); + builder.putFloat64(value.asNumber()); + break; + case dv::value_type::boolean: + builder.put(BJSON_TYPE_FALSE + value.asBoolean()); + break; + case dv::value_type::string: + builder.put(BJSON_TYPE_STRING); + builder.put(value.asString()); + break; + } +} + +std::vector json::to_binaryDV(const dv::value& object, bool compress) { + if (compress) { + auto bytes = to_binaryDV(object, false); + return gzip::compress(bytes.data(), bytes.size()); + } + ByteBuilder builder; + // type byte + builder.put(BJSON_TYPE_DOCUMENT); + // document size + builder.putInt32(0); + + // writing entries + for (const auto& [key, value] : object.asObject()) { + builder.putCStr(key.c_str()); + to_binary(builder, value); + } + // terminating byte + builder.put(BJSON_END); + + // updating document size + builder.setInt32(1, builder.size()); + return builder.build(); +} + +static dv::value list_from_binary(ByteReader& reader); +static dv::value object_from_binary(ByteReader& reader); + +static dv::value value_from_binary(ByteReader& reader) { + ubyte typecode = reader.get(); + switch (typecode) { + case BJSON_TYPE_DOCUMENT: + reader.getInt32(); + return object_from_binary(reader); + case BJSON_TYPE_LIST: + return list_from_binary(reader); + case BJSON_TYPE_BYTE: + return reader.get(); + case BJSON_TYPE_INT16: + return reader.getInt16(); + case BJSON_TYPE_INT32: + return reader.getInt32(); + case BJSON_TYPE_INT64: + return reader.getInt64(); + case BJSON_TYPE_NUMBER: + return reader.getFloat64(); + case BJSON_TYPE_FALSE: + case BJSON_TYPE_TRUE: + return (typecode - BJSON_TYPE_FALSE) != 0; + case BJSON_TYPE_STRING: + return reader.getString(); + case BJSON_TYPE_NULL: + return dv::none; + case BJSON_TYPE_BYTES: { + int32_t size = reader.getInt32(); + if (size < 0) { + throw std::runtime_error( + "invalid byte-buffer size "+std::to_string(size)); + } + if (size > reader.remaining()) { + throw std::runtime_error( + "buffer_size > remaining_size "+std::to_string(size)); + } + auto bytes = std::make_shared>( + reader.pointer(), size); + reader.skip(size); + return bytes; + } + } + throw std::runtime_error( + "type support not implemented for <"+std::to_string(typecode)+">"); +} + +static dv::value list_from_binary(ByteReader& reader) { + auto list = dv::list(); + while (reader.peek() != BJSON_END) { + list.add(value_from_binary(reader)); + } + reader.get(); + return list; +} + +static dv::value object_from_binary(ByteReader& reader) { + auto obj = dv::object(); + while (reader.peek() != BJSON_END) { + const char* key = reader.getCString(); + obj[key] = value_from_binary(reader); + } + reader.get(); + return obj; +} + +dv::value json::from_binaryDV(const ubyte* src, size_t size) { + if (size < 2) { + throw std::runtime_error("bytes length is less than 2"); + } + if (src[0] == gzip::MAGIC[0] && src[1] == gzip::MAGIC[1]) { + // reading compressed document + auto data = gzip::decompress(src, size); + return from_binaryDV(data.data(), data.size()); + } else { + ByteReader reader(src, size); + return value_from_binary(reader); + } +} diff --git a/src/data/dv.hpp b/src/data/dv.hpp index 8dbeab24..d1f907c4 100644 --- a/src/data/dv.hpp +++ b/src/data/dv.hpp @@ -220,7 +220,7 @@ namespace dv { return setBytes(ptr); } value& operator=(const objects::Bytes& bytes); - + inline value& operator=(const value& v) { switch (v.type) { case value_type::object: @@ -308,6 +308,8 @@ namespace dv { } }; + inline value none = value(); + using reference = value&; using const_reference = const value&; } diff --git a/test/coders/binary_json.cpp b/test/coders/binary_json.cpp index 2a3ecea1..aae6f669 100644 --- a/test/coders/binary_json.cpp +++ b/test/coders/binary_json.cpp @@ -36,3 +36,37 @@ TEST(BJSON, EncodeDecode) { } } } + +TEST(BJSON, EncodeDecodeDV) { + const std::string name = "JSON-encoder"; + const int bytesSize = 5000; + const int year = 2019; + const float score = 3.141592; + dynamic::ByteBuffer srcBytes(bytesSize); + for (int i = 0; i < bytesSize; i ++) { + srcBytes[i] = rand(); + } + + std::vector bjsonBytes; + { + auto object = dv::object(); + object["name"] = name; + object["year"] = year; + object["score"] = score; + object["data"] = srcBytes; + + bjsonBytes = json::to_binaryDV(object, false); + } + { + auto object = json::from_binaryDV(bjsonBytes.data(), bjsonBytes.size()); + + EXPECT_EQ(object["name"].asString(), name); + EXPECT_EQ(object["year"].asInteger(), year); + EXPECT_FLOAT_EQ(object["score"].asNumber(), score); + const auto& bytes = object["data"].asBytes(); + EXPECT_EQ(bytes.size(), bytesSize); + for (int i = 0; i < bytesSize; i++) { + EXPECT_EQ(bytes[i], srcBytes[i]); + } + } +}