#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::move; using std::string; 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 &); 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 { 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; } 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(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 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 (const auto & x : xs) if (i->precedes(x)) { add = false; break; } if (add) 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, 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(); } 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, val); } template Head Storage::storeHead(const Stored & val) const { auto id = storeHead(T::headTypeId, val.ref()); return Head(id, val); } 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 { auto cur = ref().storage().template head(id()); return make_shared>(cur ? *cur : *this); } template WatchedHead::~WatchedHead() { if (watcherId >= 0) Head::stored().ref().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; } }; }