#pragma once #include #include #include #include #include #include #include #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; template class Head; 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; Ref zref() 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; template std::optional> head(UUID id) const; template std::vector> heads() const; template Head storeHead(const T &) const; template Head storeHead(const Stored &) const; void storeKey(Ref pubref, const std::vector &) const; std::optional> loadKey(Ref pubref) const; protected: template friend class Head; template friend class WatchedHead; Storage(const std::shared_ptr & p): PartialStorage(p) {} std::optional headRef(UUID type, UUID id) const; std::vector> headRefs(UUID type) const; static UUID storeHead(UUID type, const Ref & ref); static bool replaceHead(UUID type, UUID id, const Ref & old, const Ref & ref); static std::optional updateHead(UUID type, UUID id, const Ref & old, const std::function &); int watchHead(UUID type, UUID id, const std::function) const; void unwatchHead(UUID type, UUID id, int watchId) const; }; 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; bool isZero() const; static Digest of(const std::vector & content); template static Digest of(const ObjectT &); 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; }; template Digest Digest::of(const ObjectT & obj) { return Digest::of(obj.encode()); } class PartialRef { public: PartialRef(const PartialRef &) = default; PartialRef(PartialRef &&) = default; PartialRef & operator=(const PartialRef &) = default; PartialRef & operator=(PartialRef &&) = default; static PartialRef create(const 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; std::shared_ptr p; PartialRef(const std::shared_ptr p): p(p) {} }; class Ref : public PartialRef { public: Ref(const Ref &) = default; Ref(Ref &&) = default; Ref & operator=(const Ref &) = default; Ref & operator=(Ref &&) = default; bool operator==(const Ref &) = delete; bool operator!=(const Ref &) = delete; static std::optional create(const Storage &, const Digest &); static Ref zcreate(const Storage &); explicit 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, ZonedTime, UUID, 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 asDate() const; std::optional asUUID() 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, std::monostate> 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 ObjectT 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::future && val): mref(ref), mval(std::move(val)) {} friend class Storage; friend class Head; public: Stored(const Stored &) = default; Stored(Stored &&) = default; Stored & operator=(const Stored &) = default; Stored & operator=(Stored &&) = default; Stored(const Ref &); static Stored load(const Ref &); Ref store(const Storage &) const; bool operator==(const Stored & other) const { return mref.digest() == other.mref.digest(); } bool operator!=(const Stored & other) const { return mref.digest() != other.mref.digest(); } bool operator<(const Stored & other) const { return mref.digest() < other.mref.digest(); } bool operator<=(const Stored & other) const { return mref.digest() <= other.mref.digest(); } bool operator>(const Stored & other) const { return mref.digest() > other.mref.digest(); } bool operator>=(const Stored & other) const { return mref.digest() >= other.mref.digest(); } const T & operator*() const { return mval.get(); } const T * operator->() const { return &mval.get(); } std::vector> previous() const; bool precedes(const Stored &) const; const Ref & ref() const { return mref; } private: Ref mref; std::shared_future mval; }; template Stored Storage::store(const T & val) const { return Stored(val.store(*this), std::async(std::launch::deferred, [val] { return val; })); } template Stored::Stored(const Ref & ref): mref(ref), mval(std::async(std::launch::deferred, [ref] { return T::load(ref); })) {} template Stored Stored::load(const Ref & ref) { return Stored(ref); } template Ref Stored::store(const Storage & st) const { if (st == mref.storage()) return mref; return st.storeObject(*mref); } template std::vector> Stored::previous() const { auto rec = mref->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; } template void filterAncestors(std::vector> & xs) { if (xs.size() < 2) return; std::sort(xs.begin(), xs.end()); xs.erase(std::unique(xs.begin(), xs.end()), xs.end()); std::vector> old; old.swap(xs); for (auto i = old.begin(); i != old.end(); i++) { bool add = true; for (auto j = i + 1; j != old.end(); j++) if (i->precedes(*j)) { add = false; break; } if (add) xs.push_back(std::move(*i)); } } template class WatchedHead; template class Head { Head(UUID id, Ref ref, std::future && val): mid(id), mstored(ref, std::move(val)) {} friend class Storage; public: Head(UUID id, Ref ref): mid(id), mstored(ref) {} const T & operator*() const { return *mstored; } const T * operator->() const { return &(*mstored); } UUID id() const { return mid; } const Stored & stored() const { return mstored; } const Ref & ref() const { return mstored.ref(); } std::optional> update(const std::function(const Stored &)> &) const; WatchedHead watch(const std::function &)> &) const; Bhv behavior() const; private: UUID mid; Stored mstored; }; template class WatchedHead : public Head { friend class Head; WatchedHead(const Head & h, int watcherId): Head(h), watcherId(watcherId) {} int watcherId; public: WatchedHead(WatchedHead && h): Head(h), watcherId(h.watcherId) { h.watcherId = -1; } WatchedHead & operator=(WatchedHead && h) { watcherId = h.watcherId; h.watcherId = -1; return *this; } WatchedHead & operator=(const Head & h) { if (Head::id() != h.id()) throw std::runtime_error("WatchedHead ID mismatch"); static_cast &>(*this) = h; return *this; } ~WatchedHead(); }; template class HeadBhv : public BhvSource { public: HeadBhv(const Head & head): whead(head.watch([this] (const Head & cur) { BhvCurTime ctime; whead = cur; BhvImplBase::updated(ctime); })) {} T get(const BhvCurTime &, const std::monostate &) const { return *whead; } private: WatchedHead whead; }; template std::optional> Storage::head(UUID id) const { if (auto ref = headRef(T::headTypeId, id)) return Head(id, *ref); return std::nullopt; } template std::vector> Storage::heads() const { std::vector> res; for (const auto & x : headRefs(T::headTypeId)) res.emplace_back(std::get(x), std::get(x)); return res; } template Head Storage::storeHead(const T & val) const { auto ref = val.store(*this); auto id = storeHead(T::headTypeId, ref); return Head(id, ref, std::async(std::launch::deferred, [val] { return val; })); } template Head Storage::storeHead(const Stored & val) const { auto id = storeHead(T::headTypeId, val.ref()); return Head(id, val.ref(), val.mval); } template std::optional> Head::update(const std::function(const Stored &)> & f) const { auto res = Storage::updateHead(T::headTypeId, mid, ref(), [&f, this](const Ref & r) { return f(r.digest() == ref().digest() ? stored() : Stored::load(r)).ref(); }); if (!res) return std::nullopt; if (res->digest() == ref().digest()) return *this; return Head(mid, *res); } template WatchedHead Head::watch(const std::function &)> & watcher) const { int wid = stored().ref().storage().watchHead(T::headTypeId, id(), [id = id(), watcher] (const Ref & ref) { watcher(Head(id, ref)); }); return WatchedHead(*this, wid); } template Bhv Head::behavior() const { return make_shared>(*this); } template WatchedHead::~WatchedHead() { if (watcherId >= 0) Head::stored().ref().storage().unwatchHead( T::headTypeId, Head::id(), watcherId); } } 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; } }; }