summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Smrž <roman.smrz@seznam.cz>2021-06-05 23:10:24 +0200
committerRoman Smrž <roman.smrz@seznam.cz>2021-06-06 21:48:46 +0200
commit6c58f1e095f7dbe1e7e1654c1807a76276a2f3f2 (patch)
treea4e083c6dca3e6567a0d7983c7c316b85316bf4c
parentd563500c915de2f0a652513af03f101c99715db3 (diff)
Contact list in shared state
-rw-r--r--include/erebos/attach.h2
-rw-r--r--include/erebos/contact.h47
-rw-r--r--include/erebos/identity.h6
-rw-r--r--include/erebos/list.h116
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/contact.cpp153
-rw-r--r--src/contact.h42
-rw-r--r--src/identity.cpp12
-rw-r--r--src/identity.h3
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;