Skip to content

Commit 6f44ae6

Browse files
Began implementing UBJSON support
1 parent 88cf229 commit 6f44ae6

File tree

17 files changed

+730
-5
lines changed

17 files changed

+730
-5
lines changed

CMakeLists.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ option(REFLECTCPP_FLEXBUFFERS "Enable flexbuffers support" OFF)
99
option(REFLECTCPP_MSGPACK "Enable msgpack support" OFF)
1010
option(REFLECTCPP_XML "Enable XML support" OFF)
1111
option(REFLECTCPP_TOML "Enable TOML support" OFF)
12+
option(REFLECTCPP_UBJSON "Enable UBJSON support" OFF)
1213
option(REFLECTCPP_YAML "Enable YAML support" OFF)
1314

1415
option(REFLECTCPP_BUILD_BENCHMARKS "Build benchmarks" OFF)
@@ -25,11 +26,12 @@ if(REFLECTCPP_BUILD_BENCHMARKS)
2526
set(REFLECTCPP_MSGPACK ON CACHE BOOL "" FORCE)
2627
set(REFLECTCPP_XML ON CACHE BOOL "" FORCE)
2728
set(REFLECTCPP_TOML ON CACHE BOOL "" FORCE)
29+
set(REFLECTCPP_UBJSON ON CACHE BOOL "" FORCE)
2830
set(REFLECTCPP_YAML ON CACHE BOOL "" FORCE)
2931
endif()
3032
if (REFLECTCPP_BUILD_TESTS OR REFLECTCPP_BUILD_BENCHMARKS OR
3133
(REFLECTCPP_JSON AND NOT REFLECTCPP_USE_BUNDLED_DEPENDENCIES) OR
32-
REFLECTCPP_BSON OR REFLECTCPP_CBOR OR REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_MSGPACK OR REFLECTCPP_XML OR REFLECTCPP_TOML OR REFLECTCPP_YAML)
34+
REFLECTCPP_BSON OR REFLECTCPP_CBOR OR REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_MSGPACK OR REFLECTCPP_XML OR REFLECTCPP_TOML OR REFLECTCPP_UBJSON OR REFLECTCPP_YAML)
3335
# enable vcpkg per default if require features other than JSON
3436
set(REFLECTCPP_USE_VCPKG_DEFAULT ON)
3537
endif()
@@ -147,6 +149,14 @@ if (REFLECTCPP_TOML)
147149
endif ()
148150
endif()
149151

152+
if (REFLECTCPP_UBJSON)
153+
list(APPEND REFLECT_CPP_SOURCES
154+
src/reflectcpp_ubjson.cpp
155+
)
156+
target_include_directories(reflectcpp SYSTEM PRIVATE "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include")
157+
find_package(jsoncons CONFIG REQUIRED)
158+
endif ()
159+
150160
if (REFLECTCPP_XML)
151161
list(APPEND REFLECT_CPP_SOURCES
152162
src/reflectcpp_xml.cpp

include/rfl/ubjson.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef RFL_UBJSON_HPP_
2+
#define RFL_UBJSON_HPP_
3+
4+
#include "../rfl.hpp"
5+
#include "ubjson/Parser.hpp"
6+
#include "ubjson/Reader.hpp"
7+
#include "ubjson/Writer.hpp"
8+
#include "ubjson/load.hpp"
9+
#include "ubjson/read.hpp"
10+
#include "ubjson/save.hpp"
11+
#include "ubjson/write.hpp"
12+
13+
#endif

include/rfl/ubjson/Parser.hpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#ifndef RFL_UBJSON_PARSER_HPP_
2+
#define RFL_UBJSON_PARSER_HPP_
3+
4+
#include "../Tuple.hpp"
5+
#include "../parsing/Parser.hpp"
6+
#include "Reader.hpp"
7+
#include "Writer.hpp"
8+
9+
namespace rfl::parsing {
10+
11+
/// UBJSON requires us to explicitly set the number of fields in advance.
12+
/// Because of that, we require all of the fields and then set them to nullptr,
13+
/// if necessary.
14+
template <class ProcessorsType, class... FieldTypes>
15+
requires AreReaderAndWriter<ubjson::Reader, ubjson::Writer,
16+
NamedTuple<FieldTypes...>>
17+
struct Parser<ubjson::Reader, ubjson::Writer, NamedTuple<FieldTypes...>,
18+
ProcessorsType>
19+
: public NamedTupleParser<
20+
ubjson::Reader, ubjson::Writer,
21+
/*_ignore_empty_containers=*/false,
22+
/*_all_required=*/true,
23+
/*_no_field_names=*/ProcessorsType::no_field_names_, ProcessorsType,
24+
FieldTypes...> {
25+
};
26+
27+
template <class ProcessorsType, class... Ts>
28+
requires AreReaderAndWriter<ubjson::Reader, ubjson::Writer, rfl::Tuple<Ts...>>
29+
struct Parser<ubjson::Reader, ubjson::Writer, rfl::Tuple<Ts...>, ProcessorsType>
30+
: public TupleParser<ubjson::Reader, ubjson::Writer,
31+
/*_ignore_empty_containers=*/false,
32+
/*_all_required=*/true, ProcessorsType,
33+
rfl::Tuple<Ts...>> {
34+
};
35+
36+
template <class ProcessorsType, class... Ts>
37+
requires AreReaderAndWriter<ubjson::Reader, ubjson::Writer, std::tuple<Ts...>>
38+
struct Parser<ubjson::Reader, ubjson::Writer, std::tuple<Ts...>, ProcessorsType>
39+
: public TupleParser<ubjson::Reader, ubjson::Writer,
40+
/*_ignore_empty_containers=*/false,
41+
/*_all_required=*/true, ProcessorsType,
42+
std::tuple<Ts...>> {
43+
};
44+
45+
} // namespace rfl::parsing
46+
47+
namespace rfl::ubjson {
48+
49+
template <class T, class ProcessorsType>
50+
using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>;
51+
52+
} // namespace rfl::ubjson
53+
54+
#endif

include/rfl/ubjson/Reader.hpp

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#ifndef RFL_UBJSON_READER_HPP_
2+
#define RFL_UBJSON_READER_HPP_
3+
4+
#include <cstddef>
5+
#include <exception>
6+
#include <jsoncons_ext/ubjson/ubjson_cursor.hpp>
7+
#include <string>
8+
#include <string_view>
9+
#include <type_traits>
10+
#include <vector>
11+
12+
#include "../Bytestring.hpp"
13+
#include "../Result.hpp"
14+
#include "../always_false.hpp"
15+
16+
namespace rfl::ubjson {
17+
18+
struct Reader {
19+
using CursorType = jsoncons::ubjson::ubjson_bytes_cursor;
20+
21+
struct UBJSONInputArray {
22+
CursorType* cursor_;
23+
};
24+
25+
struct UBJSONInputObject {
26+
CursorType* cursor_;
27+
};
28+
29+
struct UBJSONInputVar {
30+
CursorType* cursor_;
31+
};
32+
33+
using InputArrayType = UBJSONInputArray;
34+
using InputObjectType = UBJSONInputObject;
35+
using InputVarType = UBJSONInputVar;
36+
37+
template <class T>
38+
static constexpr bool has_custom_constructor = (requires(InputVarType var) {
39+
T::from_ubjson_obj(var);
40+
});
41+
42+
rfl::Result<InputVarType> get_field_from_array(
43+
const size_t _idx, const InputArrayType& _arr) const noexcept;
44+
45+
rfl::Result<InputVarType> get_field_from_object(
46+
const std::string& _name, const InputObjectType& _obj) const noexcept;
47+
48+
bool is_empty(const InputVarType& _var) const noexcept;
49+
50+
template <class T>
51+
rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
52+
const auto& event = _var.cursor_->current();
53+
const auto event_type = event.event_type();
54+
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
55+
if (event_type != jsoncons::staj_event_type::string_value) {
56+
return Error("Could not cast to string.");
57+
}
58+
return std::string(event.get<std::string_view>());
59+
60+
/* } else if constexpr (std::is_same<std::remove_cvref_t<T>,
61+
rfl::Bytestring>()) {
62+
if (!ubjson_value_is_byte_string(&_var.val_)) {
63+
return Error("Could not cast to bytestring.");
64+
}
65+
rfl::Bytestring bstr;
66+
const auto err = get_bytestring(&_var.val_, &bstr);
67+
if (err != CborNoError) {
68+
return Error(ubjson_error_string(err));
69+
}
70+
return bstr;*/
71+
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
72+
if (event_type != jsoncons::staj_event_type::bool_value) {
73+
return rfl::Error("Could not cast to boolean.");
74+
}
75+
return std::string(event.get<bool>());
76+
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>() ||
77+
std::is_integral<std::remove_cvref_t<T>>()) {
78+
switch (event_type) {
79+
case jsoncons::staj_event_type::int64_value:
80+
return static_cast<T>(event.get<int64_t>());
81+
case jsoncons::staj_event_type::uint64_value:
82+
return static_cast<T>(event.get<uint64_t>());
83+
case jsoncons::staj_event_type::double_value:
84+
return static_cast<T>(event.get<double>());
85+
default:
86+
return rfl::Error(
87+
"Could not cast to numeric value. The type must be integral, "
88+
"float or double.");
89+
}
90+
} else {
91+
static_assert(rfl::always_false_v<T>, "Unsupported type.");
92+
}
93+
}
94+
95+
rfl::Result<InputArrayType> to_array(const InputVarType& _var) const noexcept;
96+
97+
rfl::Result<InputObjectType> to_object(
98+
const InputVarType& _var) const noexcept;
99+
100+
template <class ArrayReader>
101+
std::optional<Error> read_array(const ArrayReader& _array_reader,
102+
const InputArrayType& _arr) const noexcept {
103+
for (const auto& event = _arr.cursor_->current();
104+
event.event_type() != jsoncons::staj_event_type::end_array;
105+
_arr.cursor_->next()) {
106+
const auto err = _array_reader.read(InputVarType{_arr.cursor_});
107+
if (err) {
108+
// TODO: Go to end of cursor
109+
return err;
110+
}
111+
}
112+
_arr.cursor_->next();
113+
return std::nullopt;
114+
}
115+
116+
template <class ObjectReader>
117+
std::optional<Error> read_object(const ObjectReader& _object_reader,
118+
const InputObjectType& _obj) const noexcept {
119+
for (const auto& event = _obj.cursor_->current();
120+
event.event_type() != jsoncons::staj_event_type::end_object;
121+
_obj.cursor_->next()) {
122+
if (event.event_type() != jsoncons::staj_event_type::key) {
123+
_obj.cursor_->reset();
124+
_obj.cursor_->reset();
125+
return Error("Expected a key.");
126+
}
127+
const auto key = event.get<std::string_view>();
128+
_object_reader.read(key, InputVarType{_obj.cursor_});
129+
}
130+
_obj.cursor_->next();
131+
return std::nullopt;
132+
}
133+
134+
template <class T>
135+
rfl::Result<T> use_custom_constructor(
136+
const InputVarType& _var) const noexcept {
137+
try {
138+
return T::from_ubjson_obj(_var);
139+
} catch (std::exception& e) {
140+
return rfl::Error(e.what());
141+
}
142+
}
143+
};
144+
145+
} // namespace rfl::ubjson
146+
147+
#endif

include/rfl/ubjson/Writer.hpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#ifndef RFL_UBJSON_WRITER_HPP_
2+
#define RFL_UBJSON_WRITER_HPP_
3+
4+
#include <bit>
5+
#include <exception>
6+
#include <jsoncons_ext/ubjson/ubjson_encoder.hpp>
7+
#include <map>
8+
#include <sstream>
9+
#include <stdexcept>
10+
#include <string>
11+
#include <string_view>
12+
#include <type_traits>
13+
#include <variant>
14+
#include <vector>
15+
16+
#include "../Box.hpp"
17+
#include "../Bytestring.hpp"
18+
#include "../Ref.hpp"
19+
#include "../Result.hpp"
20+
#include "../always_false.hpp"
21+
22+
namespace rfl::ubjson {
23+
24+
class Writer {
25+
using Encoder = jsoncons::ubjson::ubjson_bytes_encoder;
26+
27+
public:
28+
struct UBJSONOutputArray {};
29+
30+
struct UBJSONOutputObject {};
31+
32+
struct UBJSONOutputVar {};
33+
34+
using OutputArrayType = UBJSONOutputArray;
35+
using OutputObjectType = UBJSONOutputObject;
36+
using OutputVarType = UBJSONOutputVar;
37+
38+
Writer(Encoder* _encoder);
39+
40+
~Writer();
41+
42+
OutputArrayType array_as_root(const size_t _size) const noexcept;
43+
44+
OutputObjectType object_as_root(const size_t _size) const noexcept;
45+
46+
OutputVarType null_as_root() const noexcept;
47+
48+
template <class T>
49+
OutputVarType value_as_root(const T& _var) const noexcept {
50+
return new_value(_var);
51+
}
52+
53+
OutputArrayType add_array_to_array(const size_t _size,
54+
OutputArrayType* _parent) const noexcept;
55+
56+
OutputArrayType add_array_to_object(const std::string_view& _name,
57+
const size_t _size,
58+
OutputObjectType* _parent) const noexcept;
59+
60+
OutputObjectType add_object_to_array(const size_t _size,
61+
OutputArrayType* _parent) const noexcept;
62+
63+
OutputObjectType add_object_to_object(
64+
const std::string_view& _name, const size_t _size,
65+
OutputObjectType* _parent) const noexcept;
66+
67+
template <class T>
68+
OutputVarType add_value_to_array(const T& _var,
69+
OutputArrayType* _parent) const noexcept {
70+
return new_value(_var);
71+
}
72+
73+
template <class T>
74+
OutputVarType add_value_to_object(const std::string_view& _name,
75+
const T& _var,
76+
OutputObjectType* _parent) const noexcept {
77+
encoder_->key(_name);
78+
return new_value(_var);
79+
}
80+
81+
OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept;
82+
83+
OutputVarType add_null_to_object(const std::string_view& _name,
84+
OutputObjectType* _parent) const noexcept;
85+
86+
void end_array(OutputArrayType* _arr) const noexcept;
87+
88+
void end_object(OutputObjectType* _obj) const noexcept;
89+
90+
private:
91+
OutputArrayType new_array(const size_t _size) const noexcept;
92+
93+
OutputObjectType new_object(const size_t _size) const noexcept;
94+
95+
template <class T>
96+
OutputVarType new_value(const T& _var) const noexcept {
97+
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
98+
encoder_->string_value(_var);
99+
} else if constexpr (std::is_same<std::remove_cvref_t<T>,
100+
rfl::Bytestring>()) {
101+
encoder_->byte_string_value(_var);
102+
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
103+
encoder_->bool_value(_var);
104+
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
105+
encoder_->double_value(static_cast<double>(_var));
106+
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
107+
encoder_->int64_value(static_cast<std::int64_t>(_var));
108+
} else {
109+
static_assert(rfl::always_false_v<T>, "Unsupported type.");
110+
}
111+
return OutputVarType{};
112+
}
113+
114+
private:
115+
/// The underlying TinyUBJSON encoder.
116+
Encoder* const encoder_;
117+
};
118+
} // namespace rfl::ubjson
119+
120+
#endif

include/rfl/ubjson/load.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef RFL_UBJSON_LOAD_HPP_
2+
#define RFL_UBJSON_LOAD_HPP_
3+
4+
#include "../Processors.hpp"
5+
#include "../Result.hpp"
6+
#include "../io/load_bytes.hpp"
7+
#include "read.hpp"
8+
9+
namespace rfl::ubjson {
10+
11+
template <class T, class... Ps>
12+
Result<T> load(const std::string& _fname) {
13+
const auto read_bytes = [](const auto& _bytes) {
14+
return read<T, Ps...>(_bytes);
15+
};
16+
return rfl::io::load_bytes(_fname).and_then(read_bytes);
17+
}
18+
19+
} // namespace rfl::ubjson
20+
21+
#endif

0 commit comments

Comments
 (0)