doc_binary.hpp 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. #pragma once
  2. // v1.11 Phase B — binary in-memory document storage.
  3. //
  4. // Wraps a yyjson_mut_doc as the canonical Document::data representation,
  5. // exposing encode/decode/get_field/to_json_text. The yyjson types are
  6. // forward-declared here so this header does NOT transitively pull in
  7. // <yyjson.h> — only doc_binary.cpp links against yyjson. Callsites that
  8. // only need the public surface stay yyjson-agnostic.
  9. //
  10. // See docs/superpowers/specs/2026-05-15-binary-doc-format-decision.md for
  11. // the format-choice rationale and lifecycle contract.
  12. #include <optional>
  13. #include <string>
  14. #include <string_view>
  15. #include <nlohmann/json.hpp>
  16. // Forward declarations — keep <yyjson.h> out of the public header.
  17. struct yyjson_mut_doc;
  18. struct yyjson_mut_val;
  19. namespace smartbotic::db::doc_binary {
  20. // Owning, move-only RAII wrapper around a yyjson_mut_doc*. The destructor
  21. // calls yyjson_mut_doc_free, so any Doc constructed from a raw pointer takes
  22. // ownership of that pointer.
  23. class Doc {
  24. public:
  25. Doc() = default;
  26. explicit Doc(yyjson_mut_doc* doc);
  27. Doc(Doc&& other) noexcept;
  28. Doc& operator=(Doc&& other) noexcept;
  29. // Deep-copy via yyjson_mut_val_mut_copy. Phase B T3 deviation: the
  30. // decision doc originally listed Doc as move-only, but the surrounding
  31. // codebase copies Document by value in too many places (vector<Document>
  32. // returns, query results, replication paths) to refactor safely. Copies
  33. // are O(size) and allocate a fresh yyjson_mut_doc.
  34. Doc(const Doc& other);
  35. Doc& operator=(const Doc& other);
  36. ~Doc();
  37. // True if this Doc holds no underlying yyjson document.
  38. bool empty() const noexcept;
  39. // Root value of the underlying doc, or nullptr if empty.
  40. yyjson_mut_val* root() const noexcept;
  41. // Raw underlying pointer (or nullptr). Caller must not free.
  42. yyjson_mut_doc* raw() const noexcept;
  43. private:
  44. yyjson_mut_doc* doc_ = nullptr;
  45. };
  46. // Encode an nlohmann::json tree into a freshly-allocated yyjson mut_doc.
  47. // Strings are COPIED into yyjson (yyjson_mut_strncpy) so the source can be
  48. // destroyed safely. Throws std::bad_alloc on yyjson allocator failure.
  49. Doc encode(const nlohmann::json& j);
  50. // Decode a Doc back to nlohmann::json (full-tree materialise). Used by the
  51. // lazy view and any caller that needs full-tree semantics. An empty Doc
  52. // returns nlohmann::json(nullptr).
  53. nlohmann::json decode(const Doc& doc);
  54. // Field-fast-path. Returns nullopt if the field is absent or if the root is
  55. // not an object. Walks only the immediate children of the root (NOT
  56. // recursive). For nested access, callers should decode() and walk the
  57. // resulting nlohmann tree.
  58. std::optional<nlohmann::json> get_field(const Doc& doc, std::string_view field_name);
  59. // Serialise the Doc back to compact JSON text. Used at WAL/snapshot write
  60. // sites and gRPC egress. An empty Doc returns "null".
  61. std::string to_json_text(const Doc& doc);
  62. } // namespace smartbotic::db::doc_binary