#pragma once #include #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; using std::bind; using std::call_once; using std::make_unique; using std::monostate; using std::move; using std::optional; using std::shared_ptr; using std::string; using std::variant; using std::vector; 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) {} public: // For test usage const Priv & priv() const { return *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 &); static PartialRef zcreate(const PartialStorage &); 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 &) const; bool operator!=(const Ref &) const; 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; vector previous() const; class Generation generation() const; vector roots() const; private: class Generation generationLocked() const; class vector rootsLocked() const; protected: Ref(const std::shared_ptr p): PartialRef(p) {} }; template class RecordT { public: class Item; class Items; private: RecordT(const std::shared_ptr> & ptr): ptr(ptr) {} public: RecordT(): RecordT(std::vector {}) {} RecordT(const std::vector &); RecordT(std::vector &&); std::vector encode() const; Items items() const; Item item(const std::string & name) const; Item operator[](const std::string & name) const; Items 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; }; template class RecordT::Item { public: struct UnknownType { string type; string value; }; struct Empty {}; using Integer = int; using Text = string; using Binary = vector; using Date = ZonedTime; using UUID = erebos::UUID; using Ref = typename S::Ref; using Variant = variant< monostate, Empty, int, string, vector, ZonedTime, UUID, typename S::Ref, UnknownType>; Item(const string & name): Item(name, monostate()) {} Item(const string & name, Variant value): name(name), value(value) {} template Item(const string & name, const Stored & value): Item(name, value.ref()) {} Item(const Item &) = default; Item & operator=(const Item &) = delete; operator bool() const; optional asEmpty() const; optional asInteger() const; optional asText() const; optional asBinary() const; optional asDate() const; optional asUUID() const; optional asRef() const; optional asUnknown() const; template optional> as() const; const string name; const Variant value; }; template class RecordT::Items { public: using Empty = typename Item::Empty; using Integer = typename Item::Integer; using Text = typename Item::Text; using Binary = typename Item::Binary; using Date = typename Item::Date; using UUID = typename Item::UUID; using Ref = typename Item::Ref; using UnknownType = typename Item::UnknownType; Items(shared_ptr> items); Items(shared_ptr> items, string filter); class Iterator { Iterator(const Items & source, size_t idx); friend Items; public: using iterator_category = std::forward_iterator_tag; using value_type = Item; using difference_type = ssize_t; using pointer = const Item *; using reference = const Item &; Iterator(const Iterator &) = default; ~Iterator() = default; Iterator & operator=(const Iterator &) = default; Iterator & operator++(); value_type operator*() const { return (*source.items)[idx]; } bool operator==(const Iterator & other) const { return idx == other.idx; } bool operator!=(const Iterator & other) const { return idx != other.idx; } private: const Items & source; size_t idx; }; Iterator begin() const; Iterator end() const; vector asEmpty() const; vector asInteger() const; vector asText() const; vector asBinary() const; vector asDate() const; vector asUUID() const; vector asRef() const; vector asUnknown() const; template vector> as() const; private: const shared_ptr> items; const optional filter; }; 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 &); operator bool() const; 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 template vector> RecordT::Items::as() const { auto refs = asRef(); vector> res; res.reserve(refs.size()); for (const auto & ref : refs) res.push_back(Stored::load(ref)); return res; } class Generation { public: Generation(); static Generation next(const vector &); explicit operator string() const; private: Generation(size_t); size_t gen; }; template class Stored { Stored(Ref ref, T x); friend class Storage; friend class Head; public: Stored() = default; Stored(const Stored &) = default; Stored(Stored &&) = default; Stored & operator=(const Stored &) = default; Stored & operator=(Stored &&) = default; Stored(Ref); static Stored load(const Ref &); Ref store(const Storage &) const; bool operator==(const Stored & other) const { return p->ref.digest() == other.p->ref.digest(); } bool operator!=(const Stored & other) const { return p->ref.digest() != other.p->ref.digest(); } bool operator<(const Stored & other) const { return p->ref.digest() < other.p->ref.digest(); } bool operator<=(const Stored & other) const { return p->ref.digest() <= other.p->ref.digest(); } bool operator>(const Stored & other) const { return p->ref.digest() > other.p->ref.digest(); } bool operator>=(const Stored & other) const { return p->ref.digest() >= other.p->ref.digest(); } void init() const; const T & operator*() const { init(); return *p->val; } const T * operator->() const { init(); return p->val.get(); } Generation generation() const { return p->ref.generation(); } std::vector> previous() const; bool precedes(const Stored &) const; std::vector roots() const { return p->ref.roots(); } const Ref & ref() const { return p->ref; } private: struct Priv { const Ref ref; mutable std::once_flag once {}; mutable std::unique_ptr val {}; mutable std::function init {}; }; std::shared_ptr p; }; template void Stored::init() const { call_once(p->once, [this]() { p->val = std::make_unique(p->init()); p->init = decltype(p->init)(); }); } template Stored Storage::store(const T & val) const { return Stored(val.store(*this), val); } template Stored::Stored(Ref ref, T x): p(new Priv { .ref = move(ref), .val = make_unique(move(x)), }) { call_once(p->once, [](){}); } template Stored::Stored(Ref ref): p(new Priv { .ref = move(ref), }) { p->init = [p = p.get()]() { return T::load(p->ref); }; } template Stored Stored::load(const Ref & ref) { return Stored(ref); } template Ref Stored::store(const Storage & st) const { if (st == p->ref.storage()) return p->ref; return st.storeObject(*p->ref); } template std::vector> Stored::previous() const { auto refs = p->ref.previous(); vector> res; res.reserve(refs.size()); for (const auto & r : refs) res.push_back(Stored::load(r)); return res; } template bool precedes(const T & ancestor, const T & descendant) { for (const auto & x : descendant.previous()) { if (ancestor == x || precedes(ancestor, x)) return true; } return false; } template bool Stored::precedes(const Stored & other) const { return erebos::precedes(*this, other); } 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 (const auto & x : xs) if (precedes(*i, x)) { add = false; break; } if (add) for (auto j = i + 1; j != old.end(); j++) if (precedes(*i, *j)) { add = false; break; } if (add) xs.push_back(std::move(*i)); } } template class WatchedHead; template class HeadBhv; template class Head { Head(UUID id, Stored stored): mid(id), mstored(move(stored)) {} Head(UUID id, Ref ref, T val): mid(id), mstored(move(ref), 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(); } const Storage & storage() const { return mstored.ref().storage(); } optional> reload() const; std::optional> update(const std::function(const Stored &)> &) const; WatchedHead watch(const std::function &)> &) const; Bhv behavior() const; private: UUID mid; Stored mstored; }; /** * Manages registered watch callbacks to Head object using RAII principle. */ template class WatchedHead : public Head { friend class Head; friend class HeadBhv; WatchedHead(const Head & h): Head(h), watcherId(-1) {} 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; } /// Destructor stops the watching started with Head::watch call. /** * Once the WatchedHead object is destroyed, no further Head changes * will trigger the associated callback. * * The destructor also ensures that any scheduled callback run * triggered by a previous change to the head is executed and finished * before the destructor returns. The exception is when the destructor * is called directly from the callback itself, in which case the * destructor returns immediately. */ ~WatchedHead(); }; template class HeadBhv : public BhvSource { public: HeadBhv(const Head & head): whead(head) {} T get(const BhvCurTime &, const std::monostate &) const { return *whead; } private: friend class Head; void init() { whead = whead.watch([wp = weak_ptr(BhvImplBase::shared_from_this()), this] (const Head & cur) { // make sure this object still exists if (auto ptr = wp.lock()) { BhvCurTime ctime; whead = cur; BhvImplBase::updated(ctime); } }); } 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, val); } template Head Storage::storeHead(const Stored & val) const { auto id = storeHead(T::headTypeId, val.ref()); return Head(id, val); } template optional> Head::reload() const { return storage().template head(id()); } 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 = storage().watchHead(T::headTypeId, id(), [id = id(), watcher] (const Ref & ref) { watcher(Head(id, ref)); }); return WatchedHead(*this, wid); } template Bhv Head::behavior() const { auto cur = reload(); auto ret = make_shared>(cur ? *cur : *this); ret->init(); return ret; } template WatchedHead::~WatchedHead() { if (watcherId >= 0) Head::storage().unwatchHead( T::headTypeId, Head::id(), watcherId); } template vector storedRefs(const vector> & v) { vector res; res.reserve(v.size()); for (const auto & x : v) res.push_back(x.ref()); return res; } } 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; } }; }