#pragma once #include #include #include #include #include #include #include #include namespace erebos { class Storage; class PartialStorage; class Digest; class Ref; class PartialRef; template class RecordT; typedef RecordT Record; typedef RecordT PartialRecord; template class ObjectT; typedef ObjectT Object; typedef ObjectT PartialObject; class Blob; template class Stored; class PartialStorage { public: typedef erebos::PartialRef Ref; PartialStorage(const PartialStorage &) = default; PartialStorage & operator=(const PartialStorage &) = delete; virtual ~PartialStorage() = default; bool operator==(const PartialStorage &) const; bool operator!=(const PartialStorage &) const; PartialRef ref(const Digest &) const; std::optional loadObject(const Digest &) const; PartialRef storeObject(const PartialObject &) const; PartialRef storeObject(const PartialRecord &) const; PartialRef storeObject(const Blob &) const; protected: friend class Storage; friend erebos::Ref; friend erebos::PartialRef; struct Priv; const std::shared_ptr p; PartialStorage(const std::shared_ptr p): p(p) {} }; class Storage : public PartialStorage { public: typedef erebos::Ref Ref; Storage(const std::filesystem::path &); Storage(const Storage &) = default; Storage & operator=(const Storage &) = delete; Storage deriveEphemeralStorage() const; PartialStorage derivePartialStorage() const; std::optional ref(const Digest &) const; std::optional loadObject(const Digest &) const; Ref storeObject(const Object &) const; Ref storeObject(const Record &) const; Ref storeObject(const Blob &) const; std::variant> copy(const PartialRef &) const; std::variant> copy(const PartialObject &) const; Ref copy(const Ref &) const; Ref copy(const Object &) const; template Stored store(const T &) const; void storeKey(Ref pubref, const std::vector &) const; std::optional> loadKey(Ref pubref) const; protected: Storage(const std::shared_ptr p): PartialStorage(p) {} }; class Digest { public: static constexpr size_t size = 32; Digest(const Digest &) = default; Digest & operator=(const Digest &) = default; explicit Digest(std::array value): value(value) {} explicit Digest(const std::string &); explicit operator std::string() const; const std::array & arr() const { return value; } bool operator==(const Digest & other) const { return value == other.value; } bool operator!=(const Digest & other) const { return value != other.value; } bool operator<(const Digest & other) const { return value < other.value; } bool operator<=(const Digest & other) const { return value <= other.value; } bool operator>(const Digest & other) const { return value > other.value; } bool operator>=(const Digest & other) const { return value >= other.value; } private: std::array value; }; class PartialRef { public: PartialRef(const PartialRef &) = default; PartialRef & operator=(const PartialRef &) = delete; static PartialRef create(PartialStorage, const Digest &); const Digest & digest() const; operator bool() const; const PartialObject operator*() const; std::unique_ptr operator->() const; const PartialStorage & storage() const; protected: friend class Storage; struct Priv; const std::shared_ptr p; PartialRef(const std::shared_ptr p): p(p) {} }; class Ref : public PartialRef { public: Ref(const Ref &) = default; Ref & operator=(const Ref &) = delete; static std::optional create(Storage, const Digest &); constexpr operator bool() const { return true; } const Object operator*() const; std::unique_ptr operator->() const; const Storage & storage() const; protected: Ref(const std::shared_ptr p): PartialRef(p) {} }; template class RecordT { public: class Item { public: struct UnknownType { std::string type; std::string value; }; typedef std::variant< std::monostate, int, std::string, std::vector, typename S::Ref, UnknownType> Variant; Item(const std::string & name): Item(name, std::monostate()) {} Item(const std::string & name, Variant value): name(name), value(value) {} template Item(const std::string & name, const Stored & value): Item(name, value.ref) {} Item(const Item &) = default; Item & operator=(const Item &) = delete; operator bool() const; std::optional asInteger() const; std::optional asText() const; std::optional> asBinary() const; std::optional asRef() const; std::optional asUnknown() const; template std::optional> as() const; const std::string name; const Variant value; }; private: RecordT(const std::shared_ptr> & ptr): ptr(ptr) {} public: RecordT(const std::vector &); RecordT(std::vector &&); std::vector encode() const; const std::vector & items() const; Item item(const std::string & name) const; Item operator[](const std::string & name) const; std::vector items(const std::string & name) const; private: friend ObjectT; std::vector encodeInner() const; static std::optional> decode(const S &, std::vector::const_iterator, std::vector::const_iterator); const std::shared_ptr> ptr; }; extern template class RecordT; extern template class RecordT; class Blob { public: Blob(const std::vector &); const std::vector & data() const { return *ptr; } std::vector encode() const; private: friend Object; friend PartialObject; std::vector encodeInner() const; static Blob decode( std::vector::const_iterator, std::vector::const_iterator); Blob(std::shared_ptr> ptr): ptr(ptr) {} const std::shared_ptr> ptr; }; template class ObjectT { public: typedef std::variant< RecordT, Blob> Variants; ObjectT(const ObjectT &) = default; ObjectT(Variants content): content(content) {} ObjectT & operator=(const ObjectT &) = default; static std::optional, std::vector::const_iterator>> decodePrefix(const S &, std::vector::const_iterator, std::vector::const_iterator); static std::optional> decode(const S &, const std::vector &); static std::optional> decode(const S &, std::vector::const_iterator, std::vector::const_iterator); static std::vector> decodeMany(const S &, const std::vector &); std::vector encode() const; static std::optional> load(const typename S::Ref &); std::optional> asRecord() const; std::optional asBlob() const; private: friend RecordT; friend Blob; Variants content; }; extern template class ObjectT; extern template class ObjectT; template template std::optional> RecordT::Item::as() const { if (auto ref = asRef()) return Stored::load(ref.value()); return std::nullopt; } template class Stored { Stored(Ref ref, std::shared_ptr val): ref(ref), val(val) {} friend class Storage; public: static std::optional> load(const Ref &); Ref store(const Storage &) const; bool operator==(const Stored & other) const { return ref.digest() == other.ref.digest(); } bool operator!=(const Stored & other) const { return ref.digest() != other.ref.digest(); } bool operator<(const Stored & other) const { return ref.digest() < other.ref.digest(); } bool operator<=(const Stored & other) const { return ref.digest() <= other.ref.digest(); } bool operator>(const Stored & other) const { return ref.digest() > other.ref.digest(); } bool operator>=(const Stored & other) const { return ref.digest() >= other.ref.digest(); } const T & operator*() const { return *val; } const T * operator->() const { return val.get(); } std::vector> previous() const; bool precedes(const Stored &) const; const Ref ref; const std::shared_ptr val; }; template Stored Storage::store(const T & val) const { return Stored(val.store(*this), std::make_shared(val)); } template std::optional> Stored::load(const Ref & ref) { if (auto val = T::load(ref)) return Stored(ref, std::make_shared(val.value())); return std::nullopt; } template Ref Stored::store(const Storage & st) const { if (st == ref.storage()) return ref; return st.storeObject(*ref); } template std::vector> Stored::previous() const { auto rec = ref->asRecord(); if (!rec) return {}; auto sdata = rec->item("SDATA").asRef(); if (sdata) { auto drec = sdata.value()->asRecord(); if (!drec) return {}; std::vector> res; for (const auto & i : drec->items("SPREV")) if (auto x = i.as()) res.push_back(*x); return res; } std::vector> res; for (auto & i : rec->items("PREV")) if (auto x = i.as()) res.push_back(*x); return res; } template bool Stored::precedes(const Stored & other) const { for (const auto & x : other.previous()) { if (*this == x || precedes(x)) return true; } return false; } } namespace std { template<> struct hash { std::size_t operator()(const erebos::Digest & dgst) const noexcept { std::size_t res; std::memcpy(&res, dgst.arr().data(), sizeof res); return res; } }; }