summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/erebos/storage.h163
-rw-r--r--src/storage.cpp430
-rw-r--r--src/storage.h95
3 files changed, 510 insertions, 178 deletions
diff --git a/include/erebos/storage.h b/include/erebos/storage.h
index edb0aca..51fa3a4 100644
--- a/include/erebos/storage.h
+++ b/include/erebos/storage.h
@@ -12,39 +12,75 @@
namespace erebos {
class Storage;
+class PartialStorage;
class Digest;
class Ref;
-class Object;
+class PartialRef;
+
+template<class S> class RecordT;
+typedef RecordT<Storage> Record;
+typedef RecordT<PartialStorage> PartialRecord;
+template<class S> class ObjectT;
+typedef ObjectT<Storage> Object;
+typedef ObjectT<PartialStorage> PartialObject;
+class Blob;
+
template<typename T> class Stored;
-class Storage
+class PartialStorage
{
public:
- Storage(const Storage &) = default;
- Storage & operator=(const Storage &) = delete;
+ typedef erebos::PartialRef Ref;
+
+ PartialStorage(const PartialStorage &) = default;
+ PartialStorage & operator=(const PartialStorage &) = default;
+ virtual ~PartialStorage() = default;
+
+ bool operator==(const PartialStorage &) const;
+ bool operator!=(const PartialStorage &) const;
- static std::optional<Storage> open(std::filesystem::path path);
+ PartialRef ref(const Digest &) const;
- bool operator==(const Storage &) const;
- bool operator!=(const Storage &) const;
+ std::optional<PartialObject> 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<const Priv> p;
+ PartialStorage(const std::shared_ptr<const Priv> 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 &) = default;
+
+ Storage deriveEphemeralStorage() const;
+ PartialStorage derivePartialStorage() const;
std::optional<Ref> ref(const Digest &) const;
std::optional<Object> loadObject(const Digest &) const;
Ref storeObject(const Object &) const;
- Ref storeObject(const class Record &) const;
- Ref storeObject(const class Blob &) const;
+ Ref storeObject(const Record &) const;
+ Ref storeObject(const Blob &) const;
template<typename T> Stored<T> store(const T &) const;
void storeKey(Ref pubref, const std::vector<uint8_t> &) const;
std::optional<std::vector<uint8_t>> loadKey(Ref pubref) const;
-private:
- friend class Ref;
- struct Priv;
- const std::shared_ptr<const Priv> p;
- Storage(const std::shared_ptr<const Priv> p): p(p) {}
+protected:
+ Storage(const std::shared_ptr<const Priv> p): PartialStorage(p) {}
};
class Digest
@@ -72,28 +108,49 @@ private:
std::array<uint8_t, size> value;
};
-class Ref
+class PartialRef
{
public:
- Ref(const Ref &) = default;
- Ref & operator=(const Ref &) = delete;
+ PartialRef(const PartialRef &) = default;
+ PartialRef & operator=(const PartialRef &) = default;
- static std::optional<Ref> create(Storage, const Digest &);
+ static PartialRef create(PartialStorage, const Digest &);
const Digest & digest() const;
- const Object & operator*() const;
- const Object * operator->() const;
- const Storage & storage() const;
+ operator bool() const;
+ const PartialObject operator*() const;
+ std::unique_ptr<PartialObject> operator->() const;
-private:
+ const PartialStorage & storage() const;
+
+protected:
friend class Storage;
struct Priv;
const std::shared_ptr<const Priv> p;
- Ref(const std::shared_ptr<const Priv> p): p(p) {}
+ PartialRef(const std::shared_ptr<const Priv> p): p(p) {}
+};
+
+class Ref : public PartialRef
+{
+public:
+ Ref(const Ref &) = default;
+ Ref & operator=(const Ref &) = default;
+
+ static std::optional<Ref> create(Storage, const Digest &);
+
+ constexpr operator bool() const { return true; }
+ const Object operator*() const;
+ std::unique_ptr<Object> operator->() const;
+
+ const Storage & storage() const;
+
+protected:
+ Ref(const std::shared_ptr<const Priv> p): PartialRef(p) {}
};
-class Record
+template<class S>
+class RecordT
{
public:
class Item {
@@ -103,7 +160,7 @@ public:
int,
std::string,
std::vector<uint8_t>,
- Ref> Variant;
+ typename S::Ref> Variant;
Item(const std::string & name):
Item(name, std::monostate()) {}
@@ -121,7 +178,7 @@ public:
std::optional<int> asInteger() const;
std::optional<std::string> asText() const;
std::optional<std::vector<uint8_t>> asBinary() const;
- std::optional<Ref> asRef() const;
+ std::optional<typename S::Ref> asRef() const;
template<typename T> std::optional<Stored<T>> as() const;
@@ -130,12 +187,12 @@ public:
};
private:
- Record(const std::shared_ptr<std::vector<Item>> & ptr):
+ RecordT(const std::shared_ptr<std::vector<Item>> & ptr):
ptr(ptr) {}
public:
- Record(const std::vector<Item> &);
- Record(std::vector<Item> &&);
+ RecordT(const std::vector<Item> &);
+ RecordT(std::vector<Item> &&);
std::vector<uint8_t> encode() const;
const std::vector<Item> & items() const;
@@ -144,15 +201,18 @@ public:
std::vector<Item> items(const std::string & name) const;
private:
- friend class Object;
+ friend ObjectT<S>;
std::vector<uint8_t> encodeInner() const;
- static Record decode(Storage,
+ static std::optional<RecordT<S>> decode(const S &,
std::vector<uint8_t>::const_iterator,
std::vector<uint8_t>::const_iterator);
const std::shared_ptr<const std::vector<Item>> ptr;
};
+extern template class RecordT<Storage>;
+extern template class RecordT<PartialStorage>;
+
class Blob
{
public:
@@ -162,9 +222,10 @@ public:
std::vector<uint8_t> encode() const;
private:
- friend class Object;
+ friend Object;
+ friend PartialObject;
std::vector<uint8_t> encodeInner() const;
- static Blob decode(Storage,
+ static Blob decode(
std::vector<uint8_t>::const_iterator,
std::vector<uint8_t>::const_iterator);
@@ -173,41 +234,47 @@ private:
const std::shared_ptr<const std::vector<uint8_t>> ptr;
};
-class Object
+template<class S>
+class ObjectT
{
public:
typedef std::variant<
- Record,
+ RecordT<S>,
Blob> Variants;
- Object(const Object &) = default;
- Object(Variants content): content(content) {}
- Object & operator=(const Object &) = delete;
+ ObjectT(const ObjectT<S> &) = default;
+ ObjectT(Variants content): content(content) {}
+ ObjectT<S> & operator=(const ObjectT<S> &) = default;
- static std::optional<std::tuple<Object, std::vector<uint8_t>::const_iterator>>
- decodePrefix(Storage, std::vector<uint8_t>::const_iterator,
+ static std::optional<std::tuple<ObjectT<S>, std::vector<uint8_t>::const_iterator>>
+ decodePrefix(const S &,
+ std::vector<uint8_t>::const_iterator,
std::vector<uint8_t>::const_iterator);
- static std::optional<Object> decode(Storage, const std::vector<uint8_t> &);
- static std::optional<Object> decode(Storage,
+ static std::optional<ObjectT<S>> decode(const S &, const std::vector<uint8_t> &);
+ static std::optional<ObjectT<S>> decode(const S &,
std::vector<uint8_t>::const_iterator,
std::vector<uint8_t>::const_iterator);
- static std::vector<Object> decodeMany(Storage, const std::vector<uint8_t> &);
+ static std::vector<ObjectT<S>> decodeMany(const S &, const std::vector<uint8_t> &);
std::vector<uint8_t> encode() const;
- static std::optional<Object> load(const Ref &);
+ static std::optional<ObjectT<S>> load(const typename S::Ref &);
- std::optional<Record> asRecord() const;
+ std::optional<RecordT<S>> asRecord() const;
std::optional<Blob> asBlob() const;
private:
- friend class Record;
- friend class Blob;
+ friend RecordT<S>;
+ friend Blob;
Variants content;
};
+extern template class ObjectT<Storage>;
+extern template class ObjectT<PartialStorage>;
+
+template<class S>
template<typename T>
-std::optional<Stored<T>> Record::Item::as() const
+std::optional<Stored<T>> RecordT<S>::Item::as() const
{
if (auto ref = asRef())
return Stored<T>::load(ref.value());
diff --git a/src/storage.cpp b/src/storage.cpp
index 0d9b52e..2b4e027 100644
--- a/src/storage.cpp
+++ b/src/storage.cpp
@@ -21,7 +21,9 @@ using std::array;
using std::copy;
using std::holds_alternative;
using std::ifstream;
+using std::is_same_v;
using std::make_shared;
+using std::make_unique;
using std::monostate;
using std::nullopt;
using std::ofstream;
@@ -31,7 +33,8 @@ using std::string;
using std::to_string;
using std::tuple;
-optional<Storage> Storage::open(fs::path path)
+FilesystemStorage::FilesystemStorage(const fs::path & path):
+ root(path)
{
if (!fs::is_directory(path))
fs::create_directory(path);
@@ -41,42 +44,16 @@ optional<Storage> Storage::open(fs::path path)
if (!fs::is_directory(path/"heads"))
fs::create_directory(path/"heads");
-
- return Storage(shared_ptr<const Priv>(new Priv { path }));
-}
-
-bool Storage::operator==(const Storage & other) const
-{
- return p == other.p;
-}
-
-bool Storage::operator!=(const Storage & other) const
-{
- return p != other.p;
-}
-
-fs::path Storage::Priv::objectPath(const Digest & digest) const
-{
- string name(digest);
- return root/"objects"/
- fs::path(name.begin(), name.begin() + 2)/
- fs::path(name.begin() + 2, name.end());
-}
-
-fs::path Storage::Priv::keyPath(const Digest & digest) const
-{
- string name(digest);
- return root/"keys"/fs::path(name.begin(), name.end());
}
-optional<Ref> Storage::ref(const Digest & digest) const
+bool FilesystemStorage::contains(const Digest & digest) const
{
- return Ref::create(*this, digest);
+ return fs::exists(objectPath(digest));
}
-optional<vector<uint8_t>> Storage::Priv::loadBytes(const Digest & digest) const
+optional<vector<uint8_t>> FilesystemStorage::loadBytes(const Digest & digest) const
{
- vector<uint8_t> in(Priv::CHUNK);
+ vector<uint8_t> in(CHUNK);
vector<uint8_t> out;
size_t decoded = 0;
@@ -133,25 +110,9 @@ optional<vector<uint8_t>> Storage::Priv::loadBytes(const Digest & digest) const
return out;
}
-optional<Object> Storage::loadObject(const Digest & digest) const
-{
- auto ocontent = p->loadBytes(digest);
- if (!ocontent.has_value())
- return nullopt;
- auto content = ocontent.value();
-
- array<uint8_t, Digest::size> arr;
- int ret = blake2b(arr.data(), content.data(), nullptr,
- Digest::size, content.size(), 0);
- if (ret != 0 || digest != Digest(arr))
- throw runtime_error("digest verification failed");
-
- return Object::decode(*this, content);
-}
-
-void Storage::Priv::storeBytes(const Digest & digest, const vector<uint8_t> & in) const
+void FilesystemStorage::storeBytes(const Digest & digest, const vector<uint8_t> & in)
{
- vector<uint8_t> out(Priv::CHUNK);
+ vector<uint8_t> out(CHUNK);
z_stream strm;
strm.zalloc = Z_NULL;
@@ -211,6 +172,180 @@ void Storage::Priv::storeBytes(const Digest & digest, const vector<uint8_t> & in
fs::rename(lock, path);
}
+optional<vector<uint8_t>> FilesystemStorage::loadKey(const Digest & pubref) const
+{
+ fs::path path = keyPath(pubref);
+ std::error_code err;
+ size_t size = fs::file_size(path, err);
+ if (err)
+ return nullopt;
+
+ vector<uint8_t> key(size);
+ ifstream file(keyPath(pubref));
+ file.read((char *) key.data(), size);
+ return key;
+}
+
+void FilesystemStorage::storeKey(const Digest & pubref, const vector<uint8_t> & key)
+{
+ fs::path path = keyPath(pubref);
+ fs::create_directories(path.parent_path());
+ ofstream file(path);
+ file.write((const char *) key.data(), key.size());
+}
+
+fs::path FilesystemStorage::objectPath(const Digest & digest) const
+{
+ string name(digest);
+ return root/"objects"/
+ fs::path(name.begin(), name.begin() + 2)/
+ fs::path(name.begin() + 2, name.end());
+}
+
+fs::path FilesystemStorage::keyPath(const Digest & digest) const
+{
+ string name(digest);
+ return root/"keys"/fs::path(name.begin(), name.end());
+}
+
+bool MemoryStorage::contains(const Digest & digest) const
+{
+ return storage.find(digest) != storage.end();
+}
+
+optional<vector<uint8_t>> MemoryStorage::loadBytes(const Digest & digest) const
+{
+ auto it = storage.find(digest);
+ if (it != storage.end())
+ return it->second;
+ return nullopt;
+}
+
+void MemoryStorage::storeBytes(const Digest & digest, const vector<uint8_t> & content)
+{
+ storage.emplace(digest, content);
+}
+
+optional<vector<uint8_t>> MemoryStorage::loadKey(const Digest & digest) const
+{
+ auto it = keys.find(digest);
+ if (it != keys.end())
+ return it->second;
+ return nullopt;
+}
+
+void MemoryStorage::storeKey(const Digest & digest, const vector<uint8_t> & content)
+{
+ keys.emplace(digest, content);
+}
+
+bool ChainStorage::contains(const Digest & digest) const
+{
+ return storage->contains(digest) ||
+ (parent && parent->contains(digest));
+}
+
+optional<vector<uint8_t>> ChainStorage::loadBytes(const Digest & digest) const
+{
+ if (auto res = storage->loadBytes(digest))
+ return res;
+ if (parent)
+ return parent->loadBytes(digest);
+ return nullopt;
+}
+
+void ChainStorage::storeBytes(const Digest & digest, const vector<uint8_t> & content)
+{
+ storage->storeBytes(digest, content);
+}
+
+optional<vector<uint8_t>> ChainStorage::loadKey(const Digest & digest) const
+{
+ if (auto res = storage->loadKey(digest))
+ return res;
+ if (parent)
+ return parent->loadKey(digest);
+ return nullopt;
+}
+
+void ChainStorage::storeKey(const Digest & digest, const vector<uint8_t> & content)
+{
+ storage->storeKey(digest, content);
+}
+
+
+Storage::Storage(const fs::path & path):
+ PartialStorage(shared_ptr<Priv>(new Priv { .backend = make_shared<FilesystemStorage>(path) }))
+{}
+
+Storage Storage::deriveEphemeralStorage() const
+{
+ return Storage(shared_ptr<Priv>(new Priv { .backend =
+ make_shared<ChainStorage>(
+ make_shared<MemoryStorage>(),
+ make_unique<ChainStorage>(p->backend)
+ )}));
+}
+
+PartialStorage Storage::derivePartialStorage() const
+{
+ return PartialStorage(shared_ptr<Priv>(new Priv { .backend =
+ make_shared<ChainStorage>(
+ make_shared<MemoryStorage>(),
+ make_unique<ChainStorage>(p->backend)
+ )}));
+}
+
+bool PartialStorage::operator==(const PartialStorage & other) const
+{
+ return p == other.p;
+}
+
+bool PartialStorage::operator!=(const PartialStorage & other) const
+{
+ return p != other.p;
+}
+
+PartialRef PartialStorage::ref(const Digest & digest) const
+{
+ return PartialRef::create(*this, digest);
+}
+
+optional<Ref> Storage::ref(const Digest & digest) const
+{
+ return Ref::create(*this, digest);
+}
+
+optional<vector<uint8_t>> PartialStorage::Priv::loadBytes(const Digest & digest) const
+{
+ auto ocontent = backend->loadBytes(digest);
+ if (!ocontent.has_value())
+ return nullopt;
+ auto content = ocontent.value();
+
+ array<uint8_t, Digest::size> arr;
+ int ret = blake2b(arr.data(), content.data(), nullptr,
+ Digest::size, content.size(), 0);
+ if (ret != 0 || digest != Digest(arr))
+ throw runtime_error("digest verification failed");
+
+ return content;
+}
+
+optional<PartialObject> PartialStorage::loadObject(const Digest & digest) const
+{
+ if (auto content = p->loadBytes(digest))
+ return PartialObject::decode(*this, *content);
+ return nullopt;
+}
+
+optional<Object> Storage::loadObject(const Digest & digest) const
+{
+ if (auto content = p->loadBytes(digest))
+ return Object::decode(*this, *content);
+ return nullopt;
+}
+
Ref Storage::storeObject(const Object & object) const
{
// TODO: ensure storage transitively
@@ -223,34 +358,24 @@ Ref Storage::storeObject(const Object & object) const
throw runtime_error("failed to compute digest");
Digest digest(arr);
- p->storeBytes(digest, content);
+ p->backend->storeBytes(digest, content);
return Ref::create(*this, digest).value();
}
-Ref Storage::storeObject(const class Record & val) const
+Ref Storage::storeObject(const Record & val) const
{ return storeObject(Object(val)); }
-Ref Storage::storeObject(const class Blob & val) const
+Ref Storage::storeObject(const Blob & val) const
{ return storeObject(Object(val)); }
void Storage::storeKey(Ref pubref, const vector<uint8_t> & key) const
{
- ofstream file(p->keyPath(pubref.digest()));
- file.write((const char *) key.data(), key.size());
+ p->backend->storeKey(pubref.digest(), key);
}
optional<vector<uint8_t>> Storage::loadKey(Ref pubref) const
{
- fs::path path = p->keyPath(pubref.digest());
- std::error_code err;
- size_t size = fs::file_size(path, err);
- if (err)
- return nullopt;
-
- vector<uint8_t> key(size);
- ifstream file(p->keyPath(pubref.digest()));
- file.read((char *) key.data(), size);
- return key;
+ return p->backend->loadKey(pubref.digest());
}
@@ -276,92 +401,125 @@ Digest::operator string() const
}
-optional<Ref> Ref::create(Storage st, const Digest & digest)
+PartialRef PartialRef::create(PartialStorage st, const Digest & digest)
{
- if (!fs::exists(st.p->objectPath(digest)))
- return nullopt;
-
auto p = new Priv {
- .storage = st,
+ .storage = make_unique<PartialStorage>(st),
.digest = digest,
- .object = {},
};
- p->object = std::async(std::launch::deferred, [p] {
- auto obj = p->storage.loadObject(p->digest);
- if (!obj.has_value())
- throw runtime_error("failed to decode bytes");
+ return PartialRef(shared_ptr<Priv>(p));
+}
- return obj.value();
- });
+const Digest & PartialRef::digest() const
+{
+ return p->digest;
+}
- return Ref(shared_ptr<Priv>(p));
+PartialRef::operator bool() const
+{
+ return storage().p->backend->contains(p->digest);
}
-const Digest & Ref::digest() const
+const PartialObject PartialRef::operator*() const
{
- return p->digest;
+ if (auto res = p->storage->loadObject(p->digest))
+ return *res;
+ throw runtime_error("failed to load object from partial storage");
}
-const Object & Ref::operator*() const
+unique_ptr<PartialObject> PartialRef::operator->() const
{
- return p->object.get();
+ return make_unique<PartialObject>(**this);
}
-const Object * Ref::operator->() const
+const PartialStorage & PartialRef::storage() const
{
- return &p->object.get();
+ return *p->storage;
+}
+
+optional<Ref> Ref::create(Storage st, const Digest & digest)
+{
+ if (!st.p->backend->contains(digest))
+ return nullopt;
+
+ auto p = new Priv {
+ .storage = make_unique<PartialStorage>(st),
+ .digest = digest,
+ };
+
+ return Ref(shared_ptr<Priv>(p));
+}
+
+const Object Ref::operator*() const
+{
+ if (auto res = static_cast<Storage*>(p->storage.get())->loadObject(p->digest))
+ return *res;
+ throw runtime_error("falied to load object - corrupted storage");
+}
+
+unique_ptr<Object> Ref::operator->() const
+{
+ return make_unique<Object>(**this);
}
const Storage & Ref::storage() const
{
- return p->storage;
+ return *static_cast<const Storage*>(p->storage.get());
}
-Record::Item::operator bool() const
+template<class S>
+RecordT<S>::Item::operator bool() const
{
return !holds_alternative<monostate>(value);
}
-optional<int> Record::Item::asInteger() const
+template<class S>
+optional<int> RecordT<S>::Item::asInteger() const
{
if (holds_alternative<int>(value))
return std::get<int>(value);
return nullopt;
}
-optional<string> Record::Item::asText() const
+template<class S>
+optional<string> RecordT<S>::Item::asText() const
{
if (holds_alternative<string>(value))
return std::get<string>(value);
return nullopt;
}
-optional<vector<uint8_t>> Record::Item::asBinary() const
+template<class S>
+optional<vector<uint8_t>> RecordT<S>::Item::asBinary() const
{
if (holds_alternative<vector<uint8_t>>(value))
return std::get<vector<uint8_t>>(value);
return nullopt;
}
-optional<Ref> Record::Item::asRef() const
+template<class S>
+optional<typename S::Ref> RecordT<S>::Item::asRef() const
{
- if (holds_alternative<Ref>(value))
- return std::get<Ref>(value);
+ if (holds_alternative<typename S::Ref>(value))
+ return std::get<typename S::Ref>(value);
return nullopt;
}
-Record::Record(const vector<Item> & from):
+template<class S>
+RecordT<S>::RecordT(const vector<Item> & from):
ptr(new vector<Item>(from))
{}
-Record::Record(vector<Item> && from):
+template<class S>
+RecordT<S>::RecordT(vector<Item> && from):
ptr(new vector<Item>(std::move(from)))
{}
-Record Record::decode(Storage st,
+template<class S>
+optional<RecordT<S>> RecordT<S>::decode(const S & st,
vector<uint8_t>::const_iterator begin,
vector<uint8_t>::const_iterator end)
{
@@ -390,28 +548,38 @@ Record Record::decode(Storage st,
items->emplace_back(name, value);
else if (type == "b")
items->emplace_back(name, base64::decode(value));
- else if (type == "r.b2")
- items->emplace_back(name, Ref::create(st, Digest(value)).value());
- else
+ else if (type == "r.b2") {
+ if constexpr (is_same_v<S, Storage>) {
+ if (auto ref = st.ref(Digest(value)))
+ items->emplace_back(name, ref.value());
+ else
+ return nullopt;
+ } else if constexpr (std::is_same_v<S, PartialStorage>) {
+ items->emplace_back(name, st.ref(Digest(value)));
+ }
+ } else
throw runtime_error("unknown record item type");
begin = newline + 1;
}
- return Record(items);
+ return RecordT<S>(items);
}
-vector<uint8_t> Record::encode() const
+template<class S>
+vector<uint8_t> RecordT<S>::encode() const
{
- return Object(*this).encode();
+ return ObjectT<S>(*this).encode();
}
-const vector<Record::Item> & Record::items() const
+template<class S>
+const vector<typename RecordT<S>::Item> & RecordT<S>::items() const
{
return *ptr;
}
-Record::Item Record::item(const string & name) const
+template<class S>
+typename RecordT<S>::Item RecordT<S>::item(const string & name) const
{
for (auto item : *ptr) {
if (item.name == name)
@@ -420,12 +588,14 @@ Record::Item Record::item(const string & name) const
return Item("", monostate());
}
-Record::Item Record::operator[](const string & name) const
+template<class S>
+typename RecordT<S>::Item RecordT<S>::operator[](const string & name) const
{
return item(name);
}
-vector<Record::Item> Record::items(const string & name) const
+template<class S>
+vector<typename RecordT<S>::Item> RecordT<S>::items(const string & name) const
{
vector<Item> res;
for (auto item : *ptr) {
@@ -435,8 +605,8 @@ vector<Record::Item> Record::items(const string & name) const
return res;
}
-
-vector<uint8_t> Record::encodeInner() const
+template<class S>
+vector<uint8_t> RecordT<S>::encodeInner() const
{
vector<uint8_t> res;
auto inserter = std::back_inserter(res);
@@ -471,6 +641,9 @@ vector<uint8_t> Record::encodeInner() const
return res;
}
+template class RecordT<Storage>;
+template class RecordT<PartialStorage>;
+
Blob::Blob(const vector<uint8_t> & vec):
ptr(make_shared<vector<uint8_t>>(vec))
@@ -486,15 +659,16 @@ vector<uint8_t> Blob::encodeInner() const
return *ptr;
}
-Blob Blob::decode(Storage,
+Blob Blob::decode(
vector<uint8_t>::const_iterator begin,
vector<uint8_t>::const_iterator end)
{
return Blob(make_shared<vector<uint8_t>>(begin, end));
}
-optional<tuple<Object, vector<uint8_t>::const_iterator>>
-Object::decodePrefix(Storage st,
+template<class S>
+optional<tuple<ObjectT<S>, vector<uint8_t>::const_iterator>>
+ObjectT<S>::decodePrefix(const S & st,
vector<uint8_t>::const_iterator begin,
vector<uint8_t>::const_iterator end)
{
@@ -512,11 +686,14 @@ Object::decodePrefix(Storage st,
auto cend = newline + 1 + size;
string type(begin, space);
- optional<Object> obj;
+ optional<ObjectT<S>> obj;
if (type == "rec")
- obj.emplace(Record::decode(st, newline + 1, cend));
+ if (auto rec = RecordT<S>::decode(st, newline + 1, cend))
+ obj.emplace(*rec);
+ else
+ return nullopt;
else if (type == "blob")
- obj.emplace(Blob::decode(st, newline + 1, cend));
+ obj.emplace(Blob::decode(newline + 1, cend));
else
throw runtime_error("unknown object type '" + type + "'");
@@ -525,12 +702,14 @@ Object::decodePrefix(Storage st,
return nullopt;
}
-optional<Object> Object::decode(Storage st, const vector<uint8_t> & data)
+template<class S>
+optional<ObjectT<S>> ObjectT<S>::decode(const S & st, const vector<uint8_t> & data)
{
return decode(st, data.begin(), data.end());
}
-optional<Object> Object::decode(Storage st,
+template<class S>
+optional<ObjectT<S>> ObjectT<S>::decode(const S & st,
vector<uint8_t>::const_iterator begin,
vector<uint8_t>::const_iterator end)
{
@@ -542,7 +721,8 @@ optional<Object> Object::decode(Storage st,
return nullopt;
}
-vector<uint8_t> Object::encode() const
+template<class S>
+vector<uint8_t> ObjectT<S>::encode() const
{
vector<uint8_t> res, inner;
string type;
@@ -569,25 +749,33 @@ vector<uint8_t> Object::encode() const
return res;
}
-optional<Object> Object::load(const Ref & ref)
+template<class S>
+optional<ObjectT<S>> ObjectT<S>::load(const typename S::Ref & ref)
{
- return *ref;
+ if (ref)
+ return *ref;
+ return nullopt;
}
-optional<Record> Object::asRecord() const
+template<class S>
+optional<RecordT<S>> ObjectT<S>::asRecord() const
{
- if (holds_alternative<Record>(content))
- return std::get<Record>(content);
+ if (holds_alternative<RecordT<S>>(content))
+ return std::get<RecordT<S>>(content);
return nullopt;
}
-optional<Blob> Object::asBlob() const
+template<class S>
+optional<Blob> ObjectT<S>::asBlob() const
{
if (holds_alternative<Blob>(content))
return std::get<Blob>(content);
return nullopt;
}
+template class ObjectT<Storage>;
+template class ObjectT<PartialStorage>;
+
vector<Stored<Object>> erebos::collectStoredObjects(const Stored<Object> & from)
{
unordered_set<Digest> seen;
diff --git a/src/storage.h b/src/storage.h
index 68002fa..cdb8984 100644
--- a/src/storage.h
+++ b/src/storage.h
@@ -3,35 +3,112 @@
#include "erebos/storage.h"
#include <future>
+#include <unordered_map>
#include <unordered_set>
namespace fs = std::filesystem;
using std::optional;
using std::shared_future;
+using std::shared_ptr;
+using std::unique_ptr;
+using std::unordered_map;
using std::unordered_set;
+using std::variant;
using std::vector;
namespace erebos {
-struct Storage::Priv
+class StorageBackend
{
- static constexpr size_t CHUNK = 16384;
+public:
+ StorageBackend() = default;
+ virtual ~StorageBackend() = default;
- fs::path root;
+ virtual bool contains(const Digest &) const = 0;
+
+ virtual optional<vector<uint8_t>> loadBytes(const Digest &) const = 0;
+ virtual void storeBytes(const Digest &, const vector<uint8_t> &) = 0;
+
+ virtual optional<vector<uint8_t>> loadKey(const Digest &) const = 0;
+ virtual void storeKey(const Digest &, const vector<uint8_t> &) = 0;
+};
+
+class FilesystemStorage : public StorageBackend
+{
+public:
+ FilesystemStorage(const fs::path &);
+ virtual ~FilesystemStorage() = default;
+
+ virtual bool contains(const Digest &) const override;
+
+ virtual optional<vector<uint8_t>> loadBytes(const Digest &) const override;
+ virtual void storeBytes(const Digest &, const vector<uint8_t> &) override;
+
+ virtual optional<vector<uint8_t>> loadKey(const Digest &) const override;
+ virtual void storeKey(const Digest &, const vector<uint8_t> &) override;
+
+private:
+ static constexpr size_t CHUNK = 16384;
fs::path objectPath(const Digest &) const;
fs::path keyPath(const Digest &) const;
- optional<vector<uint8_t>> loadBytes(const Digest &) const;
- void storeBytes(const Digest &, const vector<uint8_t> &) const;
+
+ fs::path root;
};
-struct Ref::Priv
+class MemoryStorage : public StorageBackend
{
- Storage storage;
- Digest digest;
+public:
+ MemoryStorage() = default;
+ virtual ~MemoryStorage() = default;
+
+ virtual bool contains(const Digest &) const override;
+
+ virtual optional<vector<uint8_t>> loadBytes(const Digest &) const override;
+ virtual void storeBytes(const Digest &, const vector<uint8_t> &) override;
- shared_future<Object> object;
+ virtual optional<vector<uint8_t>> loadKey(const Digest &) const override;
+ virtual void storeKey(const Digest &, const vector<uint8_t> &) override;
+
+private:
+ unordered_map<Digest, vector<uint8_t>> storage;
+ unordered_map<Digest, vector<uint8_t>> keys;
+};
+
+class ChainStorage : public StorageBackend
+{
+public:
+ ChainStorage(shared_ptr<StorageBackend> storage):
+ ChainStorage(std::move(storage), nullptr) {}
+ ChainStorage(shared_ptr<StorageBackend> storage, unique_ptr<ChainStorage> parent):
+ storage(std::move(storage)), parent(std::move(parent)) {}
+ virtual ~ChainStorage() = default;
+
+ virtual bool contains(const Digest &) const override;
+
+ virtual optional<vector<uint8_t>> loadBytes(const Digest &) const override;
+ virtual void storeBytes(const Digest &, const vector<uint8_t> &) override;
+
+ virtual optional<vector<uint8_t>> loadKey(const Digest &) const override;
+ virtual void storeKey(const Digest &, const vector<uint8_t> &) override;
+
+private:
+ shared_ptr<StorageBackend> storage;
+ unique_ptr<ChainStorage> parent;
+};
+
+struct Storage::Priv
+{
+ shared_ptr<StorageBackend> backend;
+
+ optional<vector<uint8_t>> loadBytes(const Digest & digest) const;
+};
+
+struct Ref::Priv
+{
+ const unique_ptr<PartialStorage> storage;
+ const Digest digest;
};
vector<Stored<Object>> collectStoredObjects(const Stored<Object> &);