diff options
author | Roman Smrž <roman.smrz@seznam.cz> | 2021-06-05 23:10:24 +0200 |
---|---|---|
committer | Roman Smrž <roman.smrz@seznam.cz> | 2021-06-06 21:48:46 +0200 |
commit | 6c58f1e095f7dbe1e7e1654c1807a76276a2f3f2 (patch) | |
tree | a4e083c6dca3e6567a0d7983c7c316b85316bf4c | |
parent | d563500c915de2f0a652513af03f101c99715db3 (diff) |
Contact list in shared state
-rw-r--r-- | include/erebos/attach.h | 2 | ||||
-rw-r--r-- | include/erebos/contact.h | 47 | ||||
-rw-r--r-- | include/erebos/identity.h | 6 | ||||
-rw-r--r-- | include/erebos/list.h | 116 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/contact.cpp | 153 | ||||
-rw-r--r-- | src/contact.h | 42 | ||||
-rw-r--r-- | src/identity.cpp | 12 | ||||
-rw-r--r-- | src/identity.h | 3 |
9 files changed, 379 insertions, 3 deletions
diff --git a/include/erebos/attach.h b/include/erebos/attach.h index 14e6af3..dab0110 100644 --- a/include/erebos/attach.h +++ b/include/erebos/attach.h @@ -39,7 +39,7 @@ template<class T> class Signed; struct AttachIdentity { - Stored<Signed<class IdentityData>> identity; + Stored<Signed<struct IdentityData>> identity; vector<vector<uint8_t>> keys; static AttachIdentity load(const Ref &); diff --git a/include/erebos/contact.h b/include/erebos/contact.h new file mode 100644 index 0000000..e56346a --- /dev/null +++ b/include/erebos/contact.h @@ -0,0 +1,47 @@ +#pragma once + +#include <erebos/identity.h> +#include <erebos/list.h> +#include <erebos/state.h> +#include <erebos/storage.h> + +#include <memory> +#include <optional> +#include <string> +#include <vector> + +namespace erebos { + +using std::optional; +using std::shared_ptr; +using std::string; +using std::vector; + +class Contact +{ +public: + Contact(const Contact &) = default; + Contact(Contact &&) = default; + Contact & operator=(const Contact &) = default; + Contact & operator=(Contact &&) = default; + + static List<Contact> prepend(const Storage &, Identity, List<Contact>); + + Identity identity() const; + optional<string> name() const; + + bool operator==(const Contact &) const; + bool operator!=(const Contact &) const; + + static List<Contact> loadList(const vector<Ref> &); + vector<Ref> refs() const; + +private: + struct Priv; + shared_ptr<Priv> p; + Contact(shared_ptr<Priv> p): p(p) {} +}; + +DECLARE_SHARED_TYPE(List<Contact>) + +} diff --git a/include/erebos/identity.h b/include/erebos/identity.h index d0b60d5..888f162 100644 --- a/include/erebos/identity.h +++ b/include/erebos/identity.h @@ -6,6 +6,10 @@ namespace erebos { using std::optional; +using std::vector; + +template<class T> class Signed; +struct IdentityData; class Identity { @@ -17,8 +21,10 @@ public: static std::optional<Identity> load(const Ref &); static std::optional<Identity> load(const std::vector<Ref> &); + static std::optional<Identity> load(const std::vector<Stored<Signed<IdentityData>>> &); std::vector<Ref> store() const; std::vector<Ref> store(const Storage & st) const; + const vector<Stored<Signed<IdentityData>>> & data() const; std::optional<std::string> name() const; std::optional<Identity> owner() const; diff --git a/include/erebos/list.h b/include/erebos/list.h new file mode 100644 index 0000000..f5f2d3f --- /dev/null +++ b/include/erebos/list.h @@ -0,0 +1,116 @@ +#pragma once + +#include <functional> +#include <memory> +#include <mutex> +#include <variant> + +namespace erebos { + +using std::function; +using std::make_shared; +using std::make_unique; +using std::move; +using std::shared_ptr; +using std::unique_ptr; +using std::variant; + +template<typename T> +class List +{ +public: + struct Nil { bool operator==(const Nil &) const { return true; } }; + struct Cons { + T head; List<T> tail; + bool operator==(const Cons & x) const { return head == x.head && tail == x.tail; } + }; + + List(); + List(const T head, List<T> tail); + + const T & front() const; + const List & tail() const; + + bool empty() const; + + bool operator==(const List<T> &) const; + bool operator!=(const List<T> &) const; + + List push_front(T x) const; + +private: + struct Priv; + shared_ptr<Priv> p; +}; + +template<typename T> +struct List<T>::Priv +{ + variant<Nil, Cons> value; + + function<void()> eval = {}; + mutable std::once_flag once = {}; +}; + +template<typename T> +List<T>::List(): + p(shared_ptr<Priv>(new Priv { Nil() })) +{ + std::call_once(p->once, [](){}); +} + +template<typename T> +List<T>::List(T head, List<T> tail): + p(shared_ptr<Priv>(new Priv { + Cons { move(head), move(tail) } + })) +{ + std::call_once(p->once, [](){}); +} + +template<typename T> +const T & List<T>::front() const +{ + std::call_once(p->once, p->eval); + return std::get<Cons>(p->value).head; +} + +template<typename T> +const List<T> & List<T>::tail() const +{ + std::call_once(p->once, p->eval); + return std::get<Cons>(p->value).tail; +} + +template<typename T> +bool List<T>::empty() const +{ + std::call_once(p->once, p->eval); + return std::holds_alternative<Nil>(p->value); +} + +template<typename T> +bool List<T>::operator==(const List<T> & other) const +{ + if (p == other.p) + return true; + + std::call_once(p->once, p->eval); + std::call_once(other.p->once, other.p->eval); + return p->value == other.p->value; + +} + +template<typename T> +bool List<T>::operator!=(const List<T> & other) const +{ + return !(*this == other); +} + +template<typename T> +List<T> List<T>::push_front(T x) const +{ + return List<T>(move(x), *this); +} + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8f65555..0fd7125 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories( add_library(erebos attach channel + contact frp identity message diff --git a/src/contact.cpp b/src/contact.cpp new file mode 100644 index 0000000..edc33ea --- /dev/null +++ b/src/contact.cpp @@ -0,0 +1,153 @@ +#include "contact.h" + +#include "identity.h" + +using namespace erebos; + +using std::move; + +DEFINE_SHARED_TYPE(List<Contact>, + "34fbb61e-6022-405f-b1b3-a5a1abecd25e", + &Contact::loadList, + [](const List<Contact> & list) { + if (list.empty()) + return vector<Ref>(); + return list.front().refs(); + }) + + +List<Contact> Contact::prepend(const Storage & st, Identity id, List<Contact> list) +{ + auto cd = st.store(ContactData { + .prev = list.empty() ? vector<Stored<ContactData>>() : list.front().p->data, + .identity = id.data(), + .name = nullopt, + }); + return list.push_front( + Contact(shared_ptr<Priv>(new Priv { + .data = { cd }, + .identity = move(id), + })) + ); +} + +Identity Contact::identity() const +{ + return p->identity; +} + +optional<string> Contact::name() const +{ + p->init(); + return p->name; +} + +bool Contact::operator==(const Contact & other) const +{ + return p->data == other.p->data; +} + +bool Contact::operator!=(const Contact & other) const +{ + return p->data != other.p->data; +} + +List<Contact> Contact::loadList(const vector<Ref> & refs) +{ + vector<Stored<ContactData>> cdata; + cdata.reserve(refs.size()); + + for (const auto & r : refs) + cdata.push_back(Stored<ContactData>::load(r)); + return Priv::loadList(move(cdata), {}); +} + +List<Contact> Contact::Priv::loadList(vector<Stored<ContactData>> && cdata, vector<Identity> && seen) +{ + if (cdata.empty()) + return {}; + + filterAncestors(cdata); + + for (size_t i = 0; i < cdata.size(); i++) { + auto id = Identity::load(cdata[i]->identity); + if (!id) + continue; + + bool skip = false; + for (const auto & sid : seen) { + if (id->sameAs(sid)) { + skip = true; + break; + } + } + if (skip) + continue; + + vector<Stored<ContactData>> next; + next.reserve(cdata.size() - i - 1 + cdata[i]->prev.size()); + for (size_t j = i + 1; j < cdata.size(); j++) + next.push_back(cdata[j]); + for (const auto & x : cdata[i]->prev) + next.push_back(x); + + seen.push_back(*id); + auto p = shared_ptr<Priv>(new Priv { .data = move(cdata), .identity = move(*id) }); + return List(Contact(p), loadList(move(next), move(seen))); + } + + return {}; +} + +vector<Ref> Contact::refs() const +{ + vector<Ref> res; + res.reserve(p->data.size()); + for (const auto & x : p->data) + res.push_back(x.ref()); + return res; +} + +void Contact::Priv::init() +{ + std::call_once(initFlag, [this]() { + name = identity.name(); + }); +} + +ContactData ContactData::load(const Ref & ref) +{ + auto rec = ref->asRecord(); + if (!rec) + return ContactData(); + + vector<Stored<ContactData>> prev; + for (const auto & x : rec->items("PREV")) + if (const auto & p = x.as<ContactData>()) + prev.push_back(*p); + + vector<Stored<Signed<IdentityData>>> identity; + for (const auto & x : rec->items("identity")) + if (const auto & i = x.asRef()) + identity.push_back(*i); + + return ContactData { + .prev = std::move(prev), + .identity = std::move(identity), + .name = rec->item("name").asText(), + }; +} + +Ref ContactData::store(const Storage & st) const +{ + vector<Record::Item> items; + + for (const auto & prev : prev) + items.emplace_back("PREV", prev.ref()); + for (const auto & idt : identity) + items.emplace_back("identity", idt); + if (name) + items.emplace_back("name", *name); + + return st.storeObject(Record(std::move(items))); +} diff --git a/src/contact.h b/src/contact.h new file mode 100644 index 0000000..31deceb --- /dev/null +++ b/src/contact.h @@ -0,0 +1,42 @@ +#pragma once + +#include <erebos/contact.h> + +#include <mutex> +#include <optional> +#include <string> +#include <vector> + +namespace erebos { + +using std::optional; +using std::string; +using std::vector; + +struct ContactData; +struct IdentityData; + +struct Contact::Priv +{ + vector<Stored<ContactData>> data; + Identity identity; + + void init(); + std::once_flag initFlag {}; + + optional<string> name {}; + + static List<Contact> loadList(vector<Stored<ContactData>> &&, vector<Identity> &&); +}; + +struct ContactData +{ + static ContactData load(const Ref &); + Ref store(const Storage &) const; + + vector<Stored<ContactData>> prev; + vector<Stored<Signed<IdentityData>>> identity; + optional<string> name; +}; + +} diff --git a/src/identity.cpp b/src/identity.cpp index f55f6dd..0d35122 100644 --- a/src/identity.cpp +++ b/src/identity.cpp @@ -6,6 +6,8 @@ #include <set> #include <stdexcept> +#include <iostream> + using namespace erebos; using std::async; @@ -38,6 +40,11 @@ optional<Identity> Identity::load(const vector<Ref> & refs) for (const auto & ref : refs) data.push_back(Stored<Signed<IdentityData>>::load(ref)); + return load(data); +} + +optional<Identity> Identity::load(const vector<Stored<Signed<IdentityData>>> & data) +{ if (auto ptr = Priv::validate(data)) return Identity(ptr); return nullopt; @@ -61,6 +68,11 @@ vector<Ref> Identity::store(const Storage & st) const return res; } +const vector<Stored<Signed<IdentityData>>> & Identity::data() const +{ + return p->data; +} + optional<string> Identity::name() const { return p->name.get(); diff --git a/src/identity.h b/src/identity.h index 1dfc193..98db80a 100644 --- a/src/identity.h +++ b/src/identity.h @@ -10,9 +10,8 @@ using std::vector; namespace erebos { -class IdentityData +struct IdentityData { -public: static IdentityData load(const Ref &); Ref store(const Storage & st) const; |