Skip to content

Commit 51a3013

Browse files
Implemented the CBOR writer
1 parent 5a34c9b commit 51a3013

File tree

9 files changed

+578
-1
lines changed

9 files changed

+578
-1
lines changed

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ cmake_minimum_required(VERSION 3.15)
22

33
option(REFLECTCPP_BUILD_SHARED "Build shared library" OFF)
44
option(REFLECTCPP_BSON "Enable BSON support" OFF)
5+
option(REFLECTCPP_CBOR "Enable CBOR support" OFF)
56
option(REFLECTCPP_FLEXBUFFERS "Enable flexbuffers support" OFF)
67
option(REFLECTCPP_XML "Enable XML support" OFF)
78
option(REFLECTCPP_YAML "Enable YAML support" OFF)
89

910
option(REFLECTCPP_BUILD_TESTS "Build tests" OFF)
1011

1112
# enable vcpkg if require features other than JSON
12-
if (REFLECTCPP_BSON OR REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_XML OR REFLECTCPP_YAML)
13+
if (REFLECTCPP_BSON OR REFLECTCPP_CBOR OR REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_XML OR REFLECTCPP_YAML)
1314
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file")
1415
endif ()
1516

@@ -30,6 +31,11 @@ if (REFLECTCPP_BSON)
3031
target_link_libraries(reflectcpp PRIVATE $<IF:$<TARGET_EXISTS:mongo::bson_static>,mongo::bson_static,mongo::bson_shared>)
3132
endif ()
3233

34+
if (REFLECTCPP_CBOR)
35+
find_package(tinycbor CONFIG REQUIRED)
36+
target_link_libraries(reflectcpp INTERFACE tinycbor::tinycbor)
37+
endif ()
38+
3339
if (REFLECTCPP_FLEXBUFFERS)
3440
find_package(flatbuffers CONFIG REQUIRED)
3541
target_link_libraries(reflectcpp INTERFACE flatbuffers::flatbuffers)

include/rfl/cbor/Parser.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef RFL_CBOR_PARSER_HPP_
2+
#define RFL_CBOR_PARSER_HPP_
3+
4+
#include "../parsing/Parser.hpp"
5+
#include "Reader.hpp"
6+
#include "Writer.hpp"
7+
8+
namespace rfl {
9+
namespace cbor {
10+
11+
template <class T>
12+
using Parser = parsing::Parser<Reader, Writer, T>;
13+
14+
}
15+
} // namespace rfl
16+
17+
#endif

include/rfl/cbor/Reader.hpp

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#ifndef RFL_CBOR_READER_HPP_
2+
#define RFL_CBOR_READER_HPP_
3+
4+
#include <bson/bson.h>
5+
6+
#include <array>
7+
#include <concepts>
8+
#include <exception>
9+
#include <map>
10+
#include <memory>
11+
#include <sstream>
12+
#include <stdexcept>
13+
#include <string>
14+
#include <string_view>
15+
#include <type_traits>
16+
#include <unordered_map>
17+
#include <vector>
18+
19+
#include "../Box.hpp"
20+
#include "../Result.hpp"
21+
#include "../always_false.hpp"
22+
23+
namespace rfl {
24+
namespace cbor {
25+
26+
/// Please refer to https://mongoc.org/libbson/current/api.html
27+
struct Reader {
28+
struct CBORValue {
29+
bson_value_t val_;
30+
};
31+
32+
struct CBORInputArray {
33+
CBORValue* val_;
34+
};
35+
36+
struct CBORInputObject {
37+
CBORValue* val_;
38+
};
39+
40+
struct CBORInputVar {
41+
CBORValue* val_;
42+
};
43+
44+
using InputArrayType = CBORInputArray;
45+
using InputObjectType = CBORInputObject;
46+
using InputVarType = CBORInputVar;
47+
48+
template <class T>
49+
static constexpr bool has_custom_constructor = (requires(InputVarType var) {
50+
T::from_bson_obj(var);
51+
});
52+
53+
rfl::Result<InputVarType> get_field(
54+
const std::string& _name, const InputObjectType& _obj) const noexcept {
55+
bson_t b;
56+
bson_iter_t iter;
57+
const auto doc = _obj.val_->val_.value.v_doc;
58+
if (bson_init_static(&b, doc.data, doc.data_len)) {
59+
if (bson_iter_init(&iter, &b)) {
60+
while (bson_iter_next(&iter)) {
61+
auto key = std::string(bson_iter_key(&iter));
62+
if (key == _name) {
63+
return to_input_var(&iter);
64+
}
65+
}
66+
}
67+
}
68+
return Error("No field named '" + _name + "' was found.");
69+
}
70+
71+
bool is_empty(const InputVarType& _var) const noexcept {
72+
return _var.val_->val_.value_type == CBOR_TYPE_NULL;
73+
}
74+
75+
template <class T>
76+
rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
77+
const auto btype = _var.val_->val_.value_type;
78+
const auto value = _var.val_->val_.value;
79+
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
80+
switch (btype) {
81+
case CBOR_TYPE_UTF8:
82+
return std::string(value.v_utf8.str, value.v_utf8.len);
83+
84+
case CBOR_TYPE_SYMBOL:
85+
return std::string(value.v_symbol.symbol, value.v_symbol.len);
86+
87+
default:
88+
return rfl::Error(
89+
"Could not cast to string. The type must be UTF8 or symbol.");
90+
}
91+
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
92+
if (btype != CBOR_TYPE_BOOL) {
93+
return rfl::Error("Could not cast to boolean.");
94+
}
95+
return value.v_bool;
96+
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>() ||
97+
std::is_integral<std::remove_cvref_t<T>>()) {
98+
switch (btype) {
99+
case CBOR_TYPE_DOUBLE:
100+
return static_cast<T>(value.v_double);
101+
102+
case CBOR_TYPE_INT32:
103+
return static_cast<T>(value.v_int32);
104+
105+
case CBOR_TYPE_INT64:
106+
return static_cast<T>(value.v_int64);
107+
108+
case CBOR_TYPE_DATE_TIME:
109+
return static_cast<T>(value.v_datetime);
110+
111+
default:
112+
return rfl::Error(
113+
"Could not cast to numeric value. The type must be double, "
114+
"int32, int64 or date_time.");
115+
}
116+
} else {
117+
static_assert(rfl::always_false_v<T>, "Unsupported type.");
118+
}
119+
}
120+
121+
rfl::Result<InputArrayType> to_array(
122+
const InputVarType& _var) const noexcept {
123+
const auto btype = _var.val_->val_.value_type;
124+
if (btype != CBOR_TYPE_ARRAY && btype != CBOR_TYPE_DOCUMENT) {
125+
return Error("Could not cast to an array.");
126+
}
127+
return InputArrayType{_var.val_};
128+
}
129+
130+
template <size_t size, class FunctionType>
131+
std::array<std::optional<InputVarType>, size> to_fields_array(
132+
const FunctionType& _fct, const InputObjectType& _obj) const noexcept {
133+
std::array<std::optional<InputVarType>, size> f_arr;
134+
bson_t b;
135+
bson_iter_t iter;
136+
const auto doc = _obj.val_->val_.value.v_doc;
137+
if (bson_init_static(&b, doc.data, doc.data_len)) {
138+
if (bson_iter_init(&iter, &b)) {
139+
while (bson_iter_next(&iter)) {
140+
const char* k = bson_iter_key(&iter);
141+
const auto ix = _fct(std::string_view(k));
142+
if (ix != -1) {
143+
f_arr[ix] = to_input_var(&iter);
144+
}
145+
}
146+
}
147+
}
148+
return f_arr;
149+
}
150+
151+
rfl::Result<std::vector<std::pair<std::string, InputVarType>>> to_map(
152+
const InputObjectType& _obj) const noexcept {
153+
std::vector<std::pair<std::string, InputVarType>> map;
154+
bson_t b;
155+
bson_iter_t iter;
156+
const auto doc = _obj.val_->val_.value.v_doc;
157+
if (bson_init_static(&b, doc.data, doc.data_len)) {
158+
if (bson_iter_init(&iter, &b)) {
159+
while (bson_iter_next(&iter)) {
160+
auto key = std::string(bson_iter_key(&iter));
161+
map.emplace_back(std::make_pair(std::move(key), to_input_var(&iter)));
162+
}
163+
}
164+
}
165+
return map;
166+
}
167+
168+
rfl::Result<InputObjectType> to_object(
169+
const InputVarType& _var) const noexcept {
170+
const auto btype = _var.val_->val_.value_type;
171+
if (btype != CBOR_TYPE_DOCUMENT) {
172+
return Error("Could not cast to a document.");
173+
}
174+
return InputObjectType{_var.val_};
175+
}
176+
177+
std::vector<InputVarType> to_vec(const InputArrayType& _arr) const noexcept {
178+
std::vector<InputVarType> vec;
179+
bson_t b;
180+
bson_iter_t iter;
181+
const auto doc = _arr.val_->val_.value.v_doc;
182+
if (bson_init_static(&b, doc.data, doc.data_len)) {
183+
if (bson_iter_init(&iter, &b)) {
184+
while (bson_iter_next(&iter)) {
185+
vec.push_back(to_input_var(&iter));
186+
}
187+
}
188+
}
189+
return vec;
190+
}
191+
192+
template <class T>
193+
rfl::Result<T> use_custom_constructor(
194+
const InputVarType& _var) const noexcept {
195+
try {
196+
return T::from_bson_obj(_var);
197+
} catch (std::exception& e) {
198+
return rfl::Error(e.what());
199+
}
200+
}
201+
202+
private:
203+
struct CBORValues {
204+
std::vector<rfl::Box<CBORValue>> vec_;
205+
~CBORValues() {
206+
for (auto& v : vec_) {
207+
bson_value_destroy(&(v->val_));
208+
}
209+
}
210+
};
211+
212+
private:
213+
InputVarType to_input_var(bson_iter_t* _iter) const noexcept {
214+
values_->vec_.emplace_back(rfl::Box<CBORValue>::make());
215+
auto* last_value = values_->vec_.back().get();
216+
bson_value_copy(bson_iter_value(_iter), &last_value->val_);
217+
return InputVarType{last_value};
218+
}
219+
220+
private:
221+
/// Contains the values inside the object.
222+
rfl::Ref<CBORValues> values_;
223+
};
224+
225+
} // namespace cbor
226+
} // namespace rfl
227+
228+
#endif // JSON_PARSER_HPP_

0 commit comments

Comments
 (0)