Skip to content

Commit

Permalink
Implemented the CBOR writer
Browse files Browse the repository at this point in the history
  • Loading branch information
liuzicheng1987 committed Jan 19, 2024
1 parent 5a34c9b commit 51a3013
Show file tree
Hide file tree
Showing 9 changed files with 578 additions and 1 deletion.
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ cmake_minimum_required(VERSION 3.15)

option(REFLECTCPP_BUILD_SHARED "Build shared library" OFF)
option(REFLECTCPP_BSON "Enable BSON support" OFF)
option(REFLECTCPP_CBOR "Enable CBOR support" OFF)
option(REFLECTCPP_FLEXBUFFERS "Enable flexbuffers support" OFF)
option(REFLECTCPP_XML "Enable XML support" OFF)
option(REFLECTCPP_YAML "Enable YAML support" OFF)

option(REFLECTCPP_BUILD_TESTS "Build tests" OFF)

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

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

if (REFLECTCPP_CBOR)
find_package(tinycbor CONFIG REQUIRED)
target_link_libraries(reflectcpp INTERFACE tinycbor::tinycbor)
endif ()

if (REFLECTCPP_FLEXBUFFERS)
find_package(flatbuffers CONFIG REQUIRED)
target_link_libraries(reflectcpp INTERFACE flatbuffers::flatbuffers)
Expand Down
17 changes: 17 additions & 0 deletions include/rfl/cbor/Parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef RFL_CBOR_PARSER_HPP_
#define RFL_CBOR_PARSER_HPP_

#include "../parsing/Parser.hpp"
#include "Reader.hpp"
#include "Writer.hpp"

namespace rfl {
namespace cbor {

template <class T>
using Parser = parsing::Parser<Reader, Writer, T>;

}
} // namespace rfl

#endif
228 changes: 228 additions & 0 deletions include/rfl/cbor/Reader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#ifndef RFL_CBOR_READER_HPP_
#define RFL_CBOR_READER_HPP_

#include <bson/bson.h>

#include <array>
#include <concepts>
#include <exception>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include "../Box.hpp"
#include "../Result.hpp"
#include "../always_false.hpp"

namespace rfl {
namespace cbor {

/// Please refer to https://mongoc.org/libbson/current/api.html
struct Reader {
struct CBORValue {
bson_value_t val_;
};

struct CBORInputArray {
CBORValue* val_;
};

struct CBORInputObject {
CBORValue* val_;
};

struct CBORInputVar {
CBORValue* val_;
};

using InputArrayType = CBORInputArray;
using InputObjectType = CBORInputObject;
using InputVarType = CBORInputVar;

template <class T>
static constexpr bool has_custom_constructor = (requires(InputVarType var) {
T::from_bson_obj(var);
});

rfl::Result<InputVarType> get_field(
const std::string& _name, const InputObjectType& _obj) const noexcept {
bson_t b;
bson_iter_t iter;
const auto doc = _obj.val_->val_.value.v_doc;
if (bson_init_static(&b, doc.data, doc.data_len)) {
if (bson_iter_init(&iter, &b)) {
while (bson_iter_next(&iter)) {
auto key = std::string(bson_iter_key(&iter));
if (key == _name) {
return to_input_var(&iter);
}
}
}
}
return Error("No field named '" + _name + "' was found.");
}

bool is_empty(const InputVarType& _var) const noexcept {
return _var.val_->val_.value_type == CBOR_TYPE_NULL;
}

template <class T>
rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
const auto btype = _var.val_->val_.value_type;
const auto value = _var.val_->val_.value;
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
switch (btype) {
case CBOR_TYPE_UTF8:
return std::string(value.v_utf8.str, value.v_utf8.len);

case CBOR_TYPE_SYMBOL:
return std::string(value.v_symbol.symbol, value.v_symbol.len);

default:
return rfl::Error(
"Could not cast to string. The type must be UTF8 or symbol.");
}
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
if (btype != CBOR_TYPE_BOOL) {
return rfl::Error("Could not cast to boolean.");
}
return value.v_bool;
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>() ||
std::is_integral<std::remove_cvref_t<T>>()) {
switch (btype) {
case CBOR_TYPE_DOUBLE:
return static_cast<T>(value.v_double);

case CBOR_TYPE_INT32:
return static_cast<T>(value.v_int32);

case CBOR_TYPE_INT64:
return static_cast<T>(value.v_int64);

case CBOR_TYPE_DATE_TIME:
return static_cast<T>(value.v_datetime);

default:
return rfl::Error(
"Could not cast to numeric value. The type must be double, "
"int32, int64 or date_time.");
}
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
}

rfl::Result<InputArrayType> to_array(
const InputVarType& _var) const noexcept {
const auto btype = _var.val_->val_.value_type;
if (btype != CBOR_TYPE_ARRAY && btype != CBOR_TYPE_DOCUMENT) {
return Error("Could not cast to an array.");
}
return InputArrayType{_var.val_};
}

template <size_t size, class FunctionType>
std::array<std::optional<InputVarType>, size> to_fields_array(
const FunctionType& _fct, const InputObjectType& _obj) const noexcept {
std::array<std::optional<InputVarType>, size> f_arr;
bson_t b;
bson_iter_t iter;
const auto doc = _obj.val_->val_.value.v_doc;
if (bson_init_static(&b, doc.data, doc.data_len)) {
if (bson_iter_init(&iter, &b)) {
while (bson_iter_next(&iter)) {
const char* k = bson_iter_key(&iter);
const auto ix = _fct(std::string_view(k));
if (ix != -1) {
f_arr[ix] = to_input_var(&iter);
}
}
}
}
return f_arr;
}

rfl::Result<std::vector<std::pair<std::string, InputVarType>>> to_map(
const InputObjectType& _obj) const noexcept {
std::vector<std::pair<std::string, InputVarType>> map;
bson_t b;
bson_iter_t iter;
const auto doc = _obj.val_->val_.value.v_doc;
if (bson_init_static(&b, doc.data, doc.data_len)) {
if (bson_iter_init(&iter, &b)) {
while (bson_iter_next(&iter)) {
auto key = std::string(bson_iter_key(&iter));
map.emplace_back(std::make_pair(std::move(key), to_input_var(&iter)));
}
}
}
return map;
}

rfl::Result<InputObjectType> to_object(
const InputVarType& _var) const noexcept {
const auto btype = _var.val_->val_.value_type;
if (btype != CBOR_TYPE_DOCUMENT) {
return Error("Could not cast to a document.");
}
return InputObjectType{_var.val_};
}

std::vector<InputVarType> to_vec(const InputArrayType& _arr) const noexcept {
std::vector<InputVarType> vec;
bson_t b;
bson_iter_t iter;
const auto doc = _arr.val_->val_.value.v_doc;
if (bson_init_static(&b, doc.data, doc.data_len)) {
if (bson_iter_init(&iter, &b)) {
while (bson_iter_next(&iter)) {
vec.push_back(to_input_var(&iter));
}
}
}
return vec;
}

template <class T>
rfl::Result<T> use_custom_constructor(
const InputVarType& _var) const noexcept {
try {
return T::from_bson_obj(_var);
} catch (std::exception& e) {
return rfl::Error(e.what());
}
}

private:
struct CBORValues {
std::vector<rfl::Box<CBORValue>> vec_;
~CBORValues() {
for (auto& v : vec_) {
bson_value_destroy(&(v->val_));
}
}
};

private:
InputVarType to_input_var(bson_iter_t* _iter) const noexcept {
values_->vec_.emplace_back(rfl::Box<CBORValue>::make());
auto* last_value = values_->vec_.back().get();
bson_value_copy(bson_iter_value(_iter), &last_value->val_);
return InputVarType{last_value};
}

private:
/// Contains the values inside the object.
rfl::Ref<CBORValues> values_;
};

} // namespace cbor
} // namespace rfl

#endif // JSON_PARSER_HPP_
Loading

0 comments on commit 51a3013

Please sign in to comment.