low level sync stuff
This commit is contained in:
@@ -3,11 +3,13 @@ cmake_minimum_required(VERSION 3.24 FATAL_ERROR)
|
||||
add_library(solanaceae_crdtnotes
|
||||
./solanaceae/crdtnotes/crdtnotes.hpp
|
||||
./solanaceae/crdtnotes/crdtnotes.cpp
|
||||
./solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp
|
||||
)
|
||||
target_include_directories(solanaceae_crdtnotes PUBLIC .)
|
||||
target_compile_features(solanaceae_crdtnotes PUBLIC cxx_std_17)
|
||||
target_link_libraries(solanaceae_crdtnotes PUBLIC
|
||||
crdt_version3
|
||||
solanaceae_contact
|
||||
#solanaceae_util
|
||||
)
|
||||
|
||||
@@ -29,18 +31,21 @@ target_link_libraries(solanaceae_crdtnotes_imgui PUBLIC
|
||||
|
||||
########################################
|
||||
|
||||
add_library(solanaceae_crdtnotes_toxsync
|
||||
./solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp
|
||||
./solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.cpp
|
||||
)
|
||||
target_include_directories(solanaceae_crdtnotes_toxsync PUBLIC .)
|
||||
target_compile_features(solanaceae_crdtnotes_toxsync PUBLIC cxx_std_17)
|
||||
target_link_libraries(solanaceae_crdtnotes_toxsync PUBLIC
|
||||
solanaceae_crdtnotes
|
||||
#solanaceae_util
|
||||
solanaceae_contact
|
||||
if (TARGET solanaceae_toxcore AND TARGET solanaceae_tox_contacts)
|
||||
add_library(solanaceae_crdtnotes_toxsync
|
||||
./solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp
|
||||
./solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.cpp
|
||||
)
|
||||
target_include_directories(solanaceae_crdtnotes_toxsync PUBLIC .)
|
||||
target_compile_features(solanaceae_crdtnotes_toxsync PUBLIC cxx_std_17)
|
||||
target_link_libraries(solanaceae_crdtnotes_toxsync PUBLIC
|
||||
solanaceae_crdtnotes
|
||||
#solanaceae_util
|
||||
solanaceae_contact
|
||||
|
||||
#tox
|
||||
)
|
||||
solanaceae_toxcore
|
||||
solanaceae_tox_contacts
|
||||
)
|
||||
endif()
|
||||
|
||||
########################################
|
||||
|
@@ -8,6 +8,9 @@
|
||||
#include <unordered_map>
|
||||
#include <random>
|
||||
|
||||
// fwd
|
||||
struct CRDTNotesContactSyncModelI;
|
||||
|
||||
using ID32 = std::array<uint8_t, 32>;
|
||||
|
||||
template<>
|
||||
@@ -33,6 +36,10 @@ class CRDTNotes {
|
||||
using CRDTAgent = ID32;
|
||||
using DocID = ID32;
|
||||
using Doc = GreenCRDT::V3::TextDocument<CRDTAgent>;
|
||||
struct Frontier { // newest known seq for given agent
|
||||
CRDTAgent agent;
|
||||
uint64_t seq{0};
|
||||
};
|
||||
|
||||
private:
|
||||
// TODO: add metadata to docs
|
||||
|
50
src/solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp
Normal file
50
src/solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "./crdtnotes.hpp"
|
||||
|
||||
#include <solanaceae/contact/contact_model3.hpp>
|
||||
|
||||
// send api
|
||||
struct CRDTNotesContactSyncModelI {
|
||||
virtual ~CRDTNotesContactSyncModelI(void) {}
|
||||
|
||||
// gossip
|
||||
public:
|
||||
// notify of doc existing
|
||||
virtual void SendGossip(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id
|
||||
) = 0;
|
||||
|
||||
virtual void SendGossip(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const std::vector<CRDTNotes::Frontier>& selected_frontier
|
||||
) = 0;
|
||||
|
||||
// fetch
|
||||
public:
|
||||
// causes the other peer to send gossip with all known frontiers (on cool down)
|
||||
virtual void SendFetchCompleteFrontier(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id
|
||||
) = 0;
|
||||
|
||||
// action range request
|
||||
virtual void SendFetchOps(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const CRDTNotes::CRDTAgent& agent,
|
||||
const uint64_t seq_from,
|
||||
const uint64_t seq_to
|
||||
) = 0;
|
||||
|
||||
public: // ops response
|
||||
virtual void SendOps(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
// TODO: optimize this
|
||||
const std::vector<CRDTNotes::Doc::Op>&
|
||||
) = 0;
|
||||
};
|
||||
|
@@ -1,6 +1,265 @@
|
||||
#include "./crdtnotes_toxsync.hpp"
|
||||
|
||||
#include <solanaceae/toxcore/tox_interface.hpp>
|
||||
|
||||
CRDTNotesToxSync::CRDTNotesToxSync(CRDTNotes& notes, Contact3Registry& cr) : _notes(notes), _cr(cr) {
|
||||
#include <solanaceae/tox_contacts/components.hpp>
|
||||
|
||||
enum class NGCEXT_Event : uint8_t {
|
||||
// - DocID
|
||||
CRDTN_GOSSIP = 0x80 | 0x10,
|
||||
|
||||
// - DocID
|
||||
// - array [
|
||||
// - AgentID
|
||||
// - seq (frontier)
|
||||
// - ]
|
||||
CRDTN_GOSSIP_FRONTIER,
|
||||
|
||||
// - DocID
|
||||
CRDTN_FETCH_COMPLETE_FRONTIER,
|
||||
|
||||
// - DocID
|
||||
// - AgentID
|
||||
// - seq_from
|
||||
// - seq_to
|
||||
CRDTN_FETCH_OP_RANGE,
|
||||
|
||||
// - DocID
|
||||
// - array [
|
||||
// - seq
|
||||
// - action date
|
||||
// - ]
|
||||
CRDTN_OPS,
|
||||
};
|
||||
|
||||
|
||||
CRDTNotesToxSync::CRDTNotesToxSync(
|
||||
CRDTNotes& notes,
|
||||
Contact3Registry& cr,
|
||||
ToxI& t,
|
||||
ToxEventProviderI& tep
|
||||
) : _notes(notes), _cr(cr), _t(t), _tep(tep) {
|
||||
}
|
||||
|
||||
CRDTNotesToxSync::~CRDTNotesToxSync(void) {
|
||||
}
|
||||
|
||||
float CRDTNotesToxSync::iterate(float time_delta) {
|
||||
return 1.f; // TODO: 1sec for now, needs better logic
|
||||
}
|
||||
|
||||
void CRDTNotesToxSync::SendGossip(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id
|
||||
) {
|
||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pkg;
|
||||
|
||||
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_GOSSIP));
|
||||
|
||||
for (const uint8_t v : doc_id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
|
||||
// send
|
||||
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
||||
_t.toxGroupSendCustomPrivatePacket(
|
||||
gp.group_number, gp.peer_number,
|
||||
true,
|
||||
pkg
|
||||
);
|
||||
}
|
||||
|
||||
void CRDTNotesToxSync::SendGossip(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const std::vector<CRDTNotes::Frontier>& selected_frontier
|
||||
) {
|
||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pkg;
|
||||
|
||||
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_GOSSIP_FRONTIER));
|
||||
// 1
|
||||
|
||||
for (const uint8_t v : doc_id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
// +32
|
||||
|
||||
for (const auto& [f_id, f_seq] : selected_frontier) {
|
||||
for (const uint8_t v : f_id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
// +32
|
||||
|
||||
for (size_t i = 0; i < sizeof(f_seq); i++) {
|
||||
pkg.push_back((f_seq >> i*8) & 0xff);
|
||||
}
|
||||
// +8
|
||||
}
|
||||
// +40
|
||||
|
||||
// send
|
||||
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
||||
_t.toxGroupSendCustomPrivatePacket(
|
||||
gp.group_number, gp.peer_number,
|
||||
true,
|
||||
pkg
|
||||
);
|
||||
}
|
||||
|
||||
void CRDTNotesToxSync::SendFetchCompleteFrontier(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id
|
||||
) {
|
||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pkg;
|
||||
|
||||
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_FETCH_COMPLETE_FRONTIER));
|
||||
|
||||
for (const uint8_t v : doc_id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
|
||||
// send
|
||||
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
||||
_t.toxGroupSendCustomPrivatePacket(
|
||||
gp.group_number, gp.peer_number,
|
||||
true,
|
||||
pkg
|
||||
);
|
||||
}
|
||||
|
||||
void CRDTNotesToxSync::SendFetchOps(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const CRDTNotes::CRDTAgent& agent,
|
||||
const uint64_t seq_from,
|
||||
const uint64_t seq_to
|
||||
) {
|
||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pkg;
|
||||
|
||||
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_FETCH_OP_RANGE));
|
||||
|
||||
for (const uint8_t v : doc_id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
|
||||
for (const uint8_t v : agent) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(seq_from); i++) {
|
||||
pkg.push_back((seq_from >> i*8) & 0xff);
|
||||
}
|
||||
// +8
|
||||
|
||||
for (size_t i = 0; i < sizeof(seq_to); i++) {
|
||||
pkg.push_back((seq_to >> i*8) & 0xff);
|
||||
}
|
||||
// +8
|
||||
|
||||
|
||||
// send
|
||||
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
||||
_t.toxGroupSendCustomPrivatePacket(
|
||||
gp.group_number, gp.peer_number,
|
||||
true,
|
||||
pkg
|
||||
);
|
||||
}
|
||||
|
||||
void CRDTNotesToxSync::SendOps(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const std::vector<CRDTNotes::Doc::Op>& ops
|
||||
) {
|
||||
// ideally this is a file transfer/stream
|
||||
|
||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pkg;
|
||||
|
||||
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_OPS));
|
||||
|
||||
for (const uint8_t v : doc_id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
|
||||
// this is very inefficent
|
||||
// a full add op is 124bytes like this
|
||||
for (const auto& op : ops) {
|
||||
if(std::holds_alternative<CRDTNotes::Doc::OpAdd>(op)) {
|
||||
const auto& add_op = std::get<CRDTNotes::Doc::OpAdd>(op);
|
||||
pkg.push_back(0x00); // wasteful 1 byte for 1 bit
|
||||
|
||||
for (const uint8_t v : add_op.id.id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(add_op.id.seq); i++) {
|
||||
pkg.push_back((add_op.id.seq >> i*8) & 0xff);
|
||||
}
|
||||
pkg.push_back(add_op.value); // what we actually care for
|
||||
|
||||
if (add_op.parent_left.has_value()) {
|
||||
// exists
|
||||
pkg.push_back(0x01); // wasteful 1 byte for 1 bit
|
||||
for (const uint8_t v : add_op.parent_left.value().id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(add_op.parent_left.value().seq); i++) {
|
||||
pkg.push_back((add_op.parent_left.value().seq >> i*8) & 0xff);
|
||||
}
|
||||
} else {
|
||||
pkg.push_back(0x00); // wasteful 1 byte for 1 bit
|
||||
}
|
||||
|
||||
if (add_op.parent_right.has_value()) {
|
||||
// exists
|
||||
pkg.push_back(0x01); // wasteful 1 byte for 1 bit
|
||||
for (const uint8_t v : add_op.parent_right.value().id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(add_op.parent_right.value().seq); i++) {
|
||||
pkg.push_back((add_op.parent_right.value().seq >> i*8) & 0xff);
|
||||
}
|
||||
} else {
|
||||
pkg.push_back(0x00); // wasteful 1 byte for 1 bit
|
||||
}
|
||||
} else if (std::holds_alternative<CRDTNotes::Doc::OpDel>(op)) {
|
||||
const auto& del_op = std::get<CRDTNotes::Doc::OpDel>(op);
|
||||
pkg.push_back(0x01); // wasteful 1 byte for 1 bit
|
||||
for (const uint8_t v : del_op.id.id) {
|
||||
pkg.push_back(v);
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(del_op.id.seq); i++) {
|
||||
pkg.push_back((del_op.id.seq >> i*8) & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send
|
||||
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
||||
_t.toxGroupSendCustomPrivatePacket(
|
||||
gp.group_number, gp.peer_number,
|
||||
true,
|
||||
pkg
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,15 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <solanaceae/crdtnotes/crdtnotes.hpp>
|
||||
#include <solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp>
|
||||
#include <solanaceae/contact/contact_model3.hpp>
|
||||
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
||||
|
||||
class CRDTNotesToxSync {
|
||||
// fwd
|
||||
struct ToxI;
|
||||
struct ToxEventProviderI;
|
||||
|
||||
// implements CRDTNotesContactSyncModelI and attaches itself to tox contacts
|
||||
class CRDTNotesToxSync : public CRDTNotesContactSyncModelI, public ToxEventI {
|
||||
CRDTNotes& _notes;
|
||||
Contact3Registry& _cr;
|
||||
ToxI& _t;
|
||||
ToxEventProviderI& _tep;
|
||||
|
||||
public:
|
||||
CRDTNotesToxSync(CRDTNotes& notes, Contact3Registry& cr);
|
||||
CRDTNotesToxSync(
|
||||
CRDTNotes& notes,
|
||||
Contact3Registry& cr,
|
||||
ToxI& t,
|
||||
ToxEventProviderI& tep
|
||||
);
|
||||
~CRDTNotesToxSync(void);
|
||||
|
||||
float iterate(void);
|
||||
float iterate(float time_delta);
|
||||
|
||||
public: // sync api
|
||||
void SendGossip(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id
|
||||
) override;
|
||||
|
||||
void SendGossip(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const std::vector<CRDTNotes::Frontier>& selected_frontier
|
||||
) override;
|
||||
|
||||
void SendFetchCompleteFrontier(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id
|
||||
) override;
|
||||
|
||||
void SendFetchOps(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const CRDTNotes::CRDTAgent& agent,
|
||||
const uint64_t seq_from,
|
||||
const uint64_t seq_to
|
||||
) override;
|
||||
|
||||
void SendOps(
|
||||
Contact3Handle c,
|
||||
const CRDTNotes::DocID& doc_id,
|
||||
const std::vector<CRDTNotes::Doc::Op>&
|
||||
) override;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user