| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- // v1.11 Phase B — binary in-memory document storage backed by yyjson mut_doc.
- //
- // All yyjson includes are confined to this translation unit; the public
- // header forward-declares yyjson_mut_doc / yyjson_mut_val so callsites stay
- // yyjson-agnostic.
- #include "doc_binary.hpp"
- #include <yyjson.h>
- #include <cstdlib>
- #include <new>
- #include <string>
- #include <utility>
- namespace smartbotic::db::doc_binary {
- namespace {
- // Build a yyjson_mut_val for `j` inside `doc`. Returns nullptr on allocator
- // failure; the caller treats nullptr as bad_alloc.
- yyjson_mut_val* build_value(yyjson_mut_doc* doc, const nlohmann::json& j) {
- using value_t = nlohmann::detail::value_t;
- switch (j.type()) {
- case value_t::null:
- return yyjson_mut_null(doc);
- case value_t::boolean:
- return yyjson_mut_bool(doc, j.get<bool>());
- case value_t::number_integer:
- return yyjson_mut_sint(doc, j.get<int64_t>());
- case value_t::number_unsigned:
- return yyjson_mut_uint(doc, j.get<uint64_t>());
- case value_t::number_float:
- return yyjson_mut_real(doc, j.get<double>());
- case value_t::string: {
- const std::string& s = j.get_ref<const std::string&>();
- return yyjson_mut_strncpy(doc, s.data(), s.size());
- }
- case value_t::array: {
- yyjson_mut_val* arr = yyjson_mut_arr(doc);
- if (!arr) return nullptr;
- for (const auto& elem : j) {
- yyjson_mut_val* v = build_value(doc, elem);
- if (!v) return nullptr;
- if (!yyjson_mut_arr_add_val(arr, v)) return nullptr;
- }
- return arr;
- }
- case value_t::object: {
- yyjson_mut_val* obj = yyjson_mut_obj(doc);
- if (!obj) return nullptr;
- for (auto it = j.begin(); it != j.end(); ++it) {
- const std::string& key = it.key();
- yyjson_mut_val* k = yyjson_mut_strncpy(doc, key.data(), key.size());
- if (!k) return nullptr;
- yyjson_mut_val* v = build_value(doc, it.value());
- if (!v) return nullptr;
- if (!yyjson_mut_obj_add(obj, k, v)) return nullptr;
- }
- return obj;
- }
- case value_t::binary:
- // nlohmann's binary type has no direct JSON mapping; fall through
- // to null. No production callsite uses binary().
- return yyjson_mut_null(doc);
- case value_t::discarded:
- default:
- return yyjson_mut_null(doc);
- }
- }
- // Convert a yyjson_mut_val back into an nlohmann::json. Mirrors json_parse.cpp
- // but operates on the mut tree.
- nlohmann::json convert_to_nlohmann(yyjson_mut_val* v) {
- if (!v) {
- return nullptr;
- }
- switch (yyjson_mut_get_type(v)) {
- case YYJSON_TYPE_NULL:
- return nullptr;
- case YYJSON_TYPE_BOOL:
- return yyjson_mut_get_bool(v);
- case YYJSON_TYPE_NUM:
- switch (yyjson_mut_get_subtype(v)) {
- case YYJSON_SUBTYPE_UINT:
- return yyjson_mut_get_uint(v);
- case YYJSON_SUBTYPE_SINT:
- return yyjson_mut_get_sint(v);
- case YYJSON_SUBTYPE_REAL:
- default:
- return yyjson_mut_get_real(v);
- }
- case YYJSON_TYPE_STR:
- return std::string(yyjson_mut_get_str(v), yyjson_mut_get_len(v));
- case YYJSON_TYPE_ARR: {
- nlohmann::json out = nlohmann::json::array();
- size_t idx = 0, max = 0;
- yyjson_mut_val* elem = nullptr;
- yyjson_mut_arr_foreach(v, idx, max, elem) {
- out.push_back(convert_to_nlohmann(elem));
- }
- return out;
- }
- case YYJSON_TYPE_OBJ: {
- nlohmann::json out = nlohmann::json::object();
- size_t idx = 0, max = 0;
- yyjson_mut_val* key = nullptr;
- yyjson_mut_val* val = nullptr;
- yyjson_mut_obj_foreach(v, idx, max, key, val) {
- out[std::string(yyjson_mut_get_str(key), yyjson_mut_get_len(key))] =
- convert_to_nlohmann(val);
- }
- return out;
- }
- default:
- return nullptr;
- }
- }
- // RAII guard for the malloc'd buffer returned by yyjson_mut_write.
- struct WriteBuf {
- char* data = nullptr;
- ~WriteBuf() {
- if (data) {
- std::free(data);
- }
- }
- };
- } // namespace
- // ---------------- Doc ----------------
- Doc::Doc(yyjson_mut_doc* doc) : doc_(doc) {}
- Doc::Doc(Doc&& other) noexcept : doc_(other.doc_) {
- other.doc_ = nullptr;
- }
- Doc& Doc::operator=(Doc&& other) noexcept {
- if (this != &other) {
- if (doc_) {
- yyjson_mut_doc_free(doc_);
- }
- doc_ = other.doc_;
- other.doc_ = nullptr;
- }
- return *this;
- }
- Doc::~Doc() {
- if (doc_) {
- yyjson_mut_doc_free(doc_);
- doc_ = nullptr;
- }
- }
- bool Doc::empty() const noexcept {
- return doc_ == nullptr;
- }
- yyjson_mut_val* Doc::root() const noexcept {
- if (!doc_) return nullptr;
- return yyjson_mut_doc_get_root(doc_);
- }
- yyjson_mut_doc* Doc::raw() const noexcept {
- return doc_;
- }
- // ---------------- free functions ----------------
- Doc encode(const nlohmann::json& j) {
- yyjson_mut_doc* doc = yyjson_mut_doc_new(nullptr);
- if (!doc) {
- throw std::bad_alloc();
- }
- yyjson_mut_val* root = build_value(doc, j);
- if (!root) {
- yyjson_mut_doc_free(doc);
- throw std::bad_alloc();
- }
- yyjson_mut_doc_set_root(doc, root);
- return Doc(doc);
- }
- nlohmann::json decode(const Doc& doc) {
- if (doc.empty()) {
- return nlohmann::json(nullptr);
- }
- return convert_to_nlohmann(doc.root());
- }
- std::optional<nlohmann::json> get_field(const Doc& doc, std::string_view field_name) {
- if (doc.empty()) {
- return std::nullopt;
- }
- yyjson_mut_val* root = doc.root();
- if (!root || yyjson_mut_get_type(root) != YYJSON_TYPE_OBJ) {
- return std::nullopt;
- }
- // yyjson_mut_obj_getn does NOT require a null-terminated key — it takes
- // explicit length, so std::string_view's non-terminated buffer is safe.
- yyjson_mut_val* v = yyjson_mut_obj_getn(root, field_name.data(), field_name.size());
- if (!v) {
- return std::nullopt;
- }
- return convert_to_nlohmann(v);
- }
- std::string to_json_text(const Doc& doc) {
- if (doc.empty()) {
- return "null";
- }
- size_t len = 0;
- WriteBuf buf;
- buf.data = yyjson_mut_write(doc.raw(), 0, &len);
- if (!buf.data) {
- throw std::bad_alloc();
- }
- return std::string(buf.data, len);
- }
- } // namespace smartbotic::db::doc_binary
|