inital commit (message v3)

This commit is contained in:
2023-07-22 23:31:56 +02:00
commit b08d2bf366
15 changed files with 744 additions and 0 deletions

View File

@ -0,0 +1,120 @@
#pragma once
#include <solanaceae/contact/contact_model3.hpp>
//fwd
struct FileI;
#include <string>
#include <memory>
namespace Message::Components {
struct ContactFrom {
Contact3 c;
};
struct ContactTo {
Contact3 c;
};
struct Timestamp {
uint64_t ts {0};
};
struct TimestampProcessed {
uint64_t ts {0};
};
struct TimestampWritten {
uint64_t ts {0};
};
struct MessageText {
std::string text;
MessageText(const std::string_view& view) : text(view) {}
};
struct TagMessageIsAction {};
namespace Transfer {
//struct TransferState {
//enum State {
//running,
//paused,
//finished,
//failed,
//} state = paused;
//};
struct TagHaveAll {};
struct BytesSent {
uint64_t total {0u};
};
struct BytesReceived {
uint64_t total {0u};
};
// TODO: rename to stream?
using File = std::unique_ptr<FileI>;
struct TagReceiving {};
struct TagSending {};
// TODO: add both?
// convert to enum?
// TODO: local/remote
// TODO: invert?
struct TagPaused {};
struct StateCanceled {
enum Reason {
disconnected,
remote_canceld,
local_canceld,
} reason = disconnected;
};
#if 0
struct FileID {
// persistent ID
// sometimes called file_id or hash
ToxKey id;
// TODO: variable length
};
struct FileKind {
// TODO: use tox file kind
uint64_t kind {0};
};
#endif
struct FileInfo {
struct FileDirEntry {
std::string file_name; // full path relative to base
uint64_t file_size {0};
};
std::vector<FileDirEntry> file_list;
uint64_t total_size {0};
};
// describes the files locally
// filename might be different to non local FileInfo
// order is the same
struct FileInfoLocal {
std::vector<std::string> file_list;
};
// TODO: rename to start? or set or ...
struct ActionAccept {
std::string save_to_path;
};
} // Transfer
} // Message::Components
#include "./components_id.inl"

View File

@ -0,0 +1,36 @@
#include "./components.hpp"
#include <entt/core/type_info.hpp>
// TODO: move more central
#define DEFINE_COMP_ID(x) \
template<> \
constexpr entt::id_type entt::type_hash<x>::value() noexcept { \
using namespace entt::literals; \
return #x##_hs; \
}
// cross compile(r) stable ids
DEFINE_COMP_ID(Message::Components::ContactFrom)
DEFINE_COMP_ID(Message::Components::ContactTo)
DEFINE_COMP_ID(Message::Components::Timestamp)
DEFINE_COMP_ID(Message::Components::TimestampProcessed)
DEFINE_COMP_ID(Message::Components::TimestampWritten)
DEFINE_COMP_ID(Message::Components::MessageText)
DEFINE_COMP_ID(Message::Components::TagMessageIsAction)
DEFINE_COMP_ID(Message::Components::Transfer::TagHaveAll)
DEFINE_COMP_ID(Message::Components::Transfer::BytesSent)
DEFINE_COMP_ID(Message::Components::Transfer::BytesReceived)
DEFINE_COMP_ID(Message::Components::Transfer::File)
DEFINE_COMP_ID(Message::Components::Transfer::TagReceiving)
DEFINE_COMP_ID(Message::Components::Transfer::TagSending)
DEFINE_COMP_ID(Message::Components::Transfer::TagPaused)
DEFINE_COMP_ID(Message::Components::Transfer::StateCanceled)
DEFINE_COMP_ID(Message::Components::Transfer::FileInfo)
DEFINE_COMP_ID(Message::Components::Transfer::FileInfoLocal)
DEFINE_COMP_ID(Message::Components::Transfer::ActionAccept)
#undef DEFINE_COMP_ID

View File

@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
#include <vector>
struct FileI {
uint64_t _file_size {0};
// TODO: remove?
uint64_t _bytes_read {0};
uint64_t _bytes_written {0};
virtual ~FileI(void) {}
virtual bool isGood(void) = 0;
// TODO: move to owning/nonowning pointers
virtual std::vector<uint8_t> read(uint64_t pos, uint32_t size) = 0;
virtual bool write(uint64_t pos, const std::vector<uint8_t>& data) = 0;
};

View File

@ -0,0 +1,53 @@
#pragma once
#include "./file.hpp"
#include <fstream>
struct FileRFile : public FileI {
std::ifstream _file;
FileRFile(std::string_view file_path) : _file(static_cast<std::string>(file_path), std::ios::binary) {
if (!_file.is_open()) {
return; // TODO: error
}
// figure out size
_file.seekg(0, _file.end);
_file_size = _file.tellg();
_file.seekg(0, _file.beg);
}
//FileRFile(std::ifstream&& other_file) : _file(std::move(other_file)) {}
//FileRFile(std::ifstream&& other_file, size_t file_size) : _file(std::move(other_file)), _file_size(file_size) {}
virtual ~FileRFile(void) {}
bool isGood(void) override {
return _file.is_open() && _file.good();
}
std::vector<uint8_t> read(uint64_t pos, uint32_t size) override {
if (_file_size > 0 && pos >= _file_size) {
return {};
}
// TODO: error check
_file.seekg(pos);
// TODO: optimize
std::vector<uint8_t> chunk;
int read_char;
for (size_t i = 0; i < size && (_file_size == 0 || i+pos < _file_size) && (read_char = _file.get()) != std::ifstream::traits_type::eof(); i++) {
chunk.push_back(read_char);
}
_bytes_read += chunk.size();
return chunk;
}
// read only
bool write(uint64_t, const std::vector<uint8_t>&) override { return false; }
};

View File

@ -0,0 +1,38 @@
#pragma once
#include "./file.hpp"
struct FileRMem : public FileI {
std::vector<uint8_t> _data;
FileRMem(void) = delete;
FileRMem(const std::vector<uint8_t>& data) : _data(data) {
_file_size = _data.size();
}
virtual ~FileRMem(void) {}
bool isGood(void) override {
return true;
}
std::vector<uint8_t> read(uint64_t pos, uint32_t size) override {
if (_file_size > 0 && pos >= _data.size()) {
return {};
}
// TODO: optimize
std::vector<uint8_t> chunk;
for (size_t i = 0; i < size && i+pos < _data.size(); i++) {
chunk.push_back(_data[pos+i]);
}
//chunk.insert(chunk.begin(), _data.cbegin()+pos, _data.cbegin()+pos+size);
_bytes_read += chunk.size();
return chunk;
}
// read only
bool write(uint64_t, const std::vector<uint8_t>&) override { return false; }
};

View File

@ -0,0 +1,60 @@
#pragma once
#include "./file.hpp"
#include <fstream>
struct FileRWFile : public FileI {
std::fstream _file;
//FileWFile(std::string_view file_path, uint64_t file_size) : _file(static_cast<std::string>(file_path), std::ios::binary) {
//_file_size = file_size;
//if (!_file.is_open()) {
//return; // TODO: error
//}
//}
virtual ~FileRWFile(void) {}
bool isGood(void) override {
return _file.is_open() && _file.good();
}
std::vector<uint8_t> read(uint64_t pos, uint32_t size) override {
if (pos >= _file_size) {
return {};
}
// TODO: error check
_file.seekg(pos);
// TODO: optimize
std::vector<uint8_t> chunk;
int read_char;
for (size_t i = 0; i < size && (_file_size == 0 || i+pos < _file_size) && (read_char = _file.get()) != std::ifstream::traits_type::eof(); i++) {
chunk.push_back(read_char);
}
_bytes_read += chunk.size();
return chunk;
}
bool write(uint64_t pos, const std::vector<uint8_t>& data) override {
if (pos >= _file_size) {
return false;
}
// if out-of-order, seek
if (_file.tellp() != int64_t(pos)) {
// TODO: error check
_file.seekp(pos);
}
_file.write(reinterpret_cast<const char*>(data.data()), data.size());
return _file.good();
}
};

View File

@ -0,0 +1,46 @@
#pragma once
#include "./file.hpp"
#include <string_view>
#include <fstream>
struct FileWFile : public FileI {
std::ofstream _file;
FileWFile(std::string_view file_path, uint64_t file_size) : _file(static_cast<std::string>(file_path), std::ios::binary) {
_file_size = file_size;
if (!_file.is_open()) {
return; // TODO: error
}
}
virtual ~FileWFile(void) {}
bool isGood(void) override {
return _file.is_open() && _file.good();
}
// write only
std::vector<uint8_t> read(uint64_t, uint32_t) override { return {}; }
bool write(uint64_t pos, const std::vector<uint8_t>& data) override {
if (_file_size > 0 && pos >= _file_size) {
return false;
}
// if out-of-order, seek
if (_file.tellp() != int64_t(pos)) {
// TODO: error check
_file.seekp(pos);
}
_file.write(reinterpret_cast<const char*>(data.data()), data.size());
_bytes_written += data.size();
return _file.good();
}
};

View File

@ -0,0 +1,27 @@
#pragma once
#include <solanaceae/contact/contact_model3.hpp>
//#include "./file.hpp"
// TODO: move, rename, do something?, change in favor of tox?
//enum class FileKind : uint32_t {
//file = 0u,
//avatar = 1u,
//};
// interface to send messages
struct MessageModel3I {
virtual ~MessageModel3I(void) {}
// return true if a handler was found for the contact
virtual bool sendText(const Contact3 c, std::string_view message, bool action = false) { (void)c,(void)message,(void)action; return false; }
//virtual bool sendFile(const Contact& c, std::string_view file_name, std::unique_ptr<FileI> file) { (void)c,(void)message,(void)action; return false; }
virtual bool sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) { (void)c,(void)file_name,(void)file_path; return false; }
//virtual bool sendFileMem(const Contact& c, std::string_view file_name, const std::vector<uint8_t>& file) = 0;
};

View File

@ -0,0 +1,29 @@
#include "./message_time_sort.hpp"
#include <solanaceae/registry_message_model/components.hpp>
MessageTimeSort::MessageTimeSort(RegistryMessageModel& rmm) : _rmm(rmm) {
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
_rmm.subscribe(this, RegistryMessageModel_Event::message_updated);
}
void MessageTimeSort::iterate(void) {
// TODO: maybe only every x for updated
for (auto* reg : _to_sort) {
reg->sort<Message::Components::Timestamp>([](const auto& lhs, const auto& rhs) -> bool {
return lhs.ts < rhs.ts;
}, entt::insertion_sort{});
}
_to_sort.clear();
}
bool MessageTimeSort::onEvent(const Message::Events::MessageConstruct& e) {
_to_sort.emplace(e.e.registry());
return false;
}
bool MessageTimeSort::onEvent(const Message::Events::MessageUpdated& e) {
_to_sort.emplace(e.e.registry());
return false;
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "./registry_message_model.hpp"
#include <entt/container/dense_set.hpp>
class MessageTimeSort : public RegistryMessageModelEventI {
RegistryMessageModel& _rmm;
// TODO: use contact instead
entt::dense_set<Message3Registry*> _to_sort;
public:
MessageTimeSort(RegistryMessageModel& rmm);
// do the sorting
void iterate(void);
protected: // mm
bool onEvent(const Message::Events::MessageConstruct& e) override;
bool onEvent(const Message::Events::MessageUpdated& e) override;
};

View File

@ -0,0 +1,131 @@
#include "./registry_message_model.hpp"
#include <solanaceae/contact/components.hpp>
#include <iostream>
Message3Registry* RegistryMessageModel::get(Contact3 c) {
if (_cr.valid(c) && !_cr.all_of<Contact::Components::TagBig>(c)) {
// TODO: loop upwards
if (!_cr.all_of<Contact::Components::Parent>(c)) {
return nullptr;
}
c = _cr.get<Contact::Components::Parent>(c).parent;
}
if (!_cr.valid(c)) {
// TODO: throw error
return nullptr;
}
const auto it = _contact_messages.find(c);
if (it != _contact_messages.end()) {
return it->second.get();
}
auto& reg_sh = _contact_messages[c] = std::make_unique<Message3Registry>();
return reg_sh.get();
}
Message3Registry* RegistryMessageModel::get(Contact3 c) const {
if (_cr.valid(c) && !_cr.all_of<Contact::Components::TagBig>(c)) {
// TODO: loop upwards
if (!_cr.all_of<Contact::Components::Parent>(c)) {
return nullptr;
}
c = _cr.get<Contact::Components::Parent>(c).parent;
}
if (!_cr.valid(c)) {
// TODO: throw error
return nullptr;
}
const auto it = _contact_messages.find(c);
if (it != _contact_messages.cend()) {
return it->second.get();
}
return nullptr;
}
void RegistryMessageModel::throwEventConstruct(Message3Registry& reg, Message3 e) {
std::cout << "RMM debug: event construct " << entt::to_integral(e) << "\n";
dispatch(
RegistryMessageModel_Event::message_construct,
Message::Events::MessageConstruct{
Message3Handle{reg, e}
}
);
}
void RegistryMessageModel::throwEventUpdate(Message3Registry& reg, Message3 e) {
// the update while update lock is hacky
_update_queue.push_back({reg, e});
if (!_update_in_progess) {
_update_in_progess = true;
for (size_t i = 0; i < _update_queue.size(); i++) {
std::cout << "RMM debug: event update " << entt::to_integral(e) << "\n";
dispatch(
RegistryMessageModel_Event::message_updated,
Message::Events::MessageUpdated{
_update_queue.at(i)
}
);
}
_update_queue.clear();
_update_in_progess = false;
}
}
void RegistryMessageModel::throwEventDestroy(Message3Registry& reg, Message3 e) {
std::cout << "RMM debug: event destroy " << entt::to_integral(e) << "\n";
dispatch(
RegistryMessageModel_Event::message_destroy,
Message::Events::MessageDestory{
Message3Handle{reg, e}
}
);
}
void RegistryMessageModel::throwEventConstruct(const Contact3 c, Message3 e) {
if (auto* reg_ptr = get(c); reg_ptr) {
throwEventConstruct(*reg_ptr, e);
}
}
void RegistryMessageModel::throwEventUpdate(const Contact3 c, Message3 e) {
if (auto* reg_ptr = get(c); reg_ptr) {
throwEventUpdate(*reg_ptr, e);
}
}
void RegistryMessageModel::throwEventDestroy(const Contact3 c, Message3 e) {
if (auto* reg_ptr = get(c); reg_ptr) {
throwEventDestroy(*reg_ptr, e);
}
}
bool RegistryMessageModel::sendText(const Contact3 c, std::string_view message, bool action) {
std::cout << "RMM debug: event send text\n";
// manual, bc its not an "event"
for (auto* zei : _subscribers.at(size_t(RegistryMessageModel_Event::send_text))) {
if (zei->sendText(c, message, action)) {
return true;
}
}
return false;
}
bool RegistryMessageModel::sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) {
std::cout << "RMM debug: event send file path\n";
// manual, bc its not an "event"
for (auto* zei : _subscribers.at(size_t(RegistryMessageModel_Event::send_file_path))) {
if (zei->sendFilePath(c, file_name, file_path)) {
return true;
}
}
return false;
}

View File

@ -0,0 +1,100 @@
#pragma once
#include <solanaceae/util/event_provider.hpp>
#include "./message_model3.hpp"
#include <entt/entity/registry.hpp>
#include <entt/entity/handle.hpp>
#include <entt/container/dense_map.hpp>
#include <memory>
// strong typing
enum class Message3 : uint32_t {};
using Message3Registry = entt::basic_registry<Message3>;
using Message3Handle = entt::basic_handle<Message3Registry>;
namespace Message::Events {
struct MessageConstruct {
const Message3Handle e;
};
struct MessageUpdated {
const Message3Handle e;
// hint?
// like component list?
};
struct MessageDestory {
const Message3Handle e;
};
} // Events
enum class RegistryMessageModel_Event : uint32_t {
message_construct,
message_updated,
message_destroy,
send_text,
send_file_path,
MAX
};
// 2 in 1
struct RegistryMessageModelEventI : public MessageModel3I {
using enumType = RegistryMessageModel_Event;
virtual ~RegistryMessageModelEventI(void) {}
virtual bool onEvent(const Message::Events::MessageConstruct&) { return false; }
virtual bool onEvent(const Message::Events::MessageUpdated&) { return false; }
virtual bool onEvent(const Message::Events::MessageDestory&) { return false; }
// mm3
// send text
// send file path
};
using RegistryMessageModelEventProviderI = EventProviderI<RegistryMessageModelEventI>;
class RegistryMessageModel : public RegistryMessageModelEventProviderI, public MessageModel3I {
protected:
Contact3Registry& _cr;
entt::dense_map<Contact3, std::unique_ptr<Message3Registry>> _contact_messages;
bool _update_in_progess {false};
std::vector<Message3Handle> _update_queue {};
public:
RegistryMessageModel(Contact3Registry& cr) : _cr(cr) {}
virtual ~RegistryMessageModel(void) {}
// TODO: iterate?
public:
Message3Registry* get(Contact3 c);
Message3Registry* get(Contact3 c) const;
public: // dispatcher
// !!! remember to manually throw these externally
void throwEventConstruct(Message3Registry& reg, Message3 e);
void throwEventUpdate(Message3Registry& reg, Message3 e);
void throwEventDestroy(Message3Registry& reg, Message3 e);
void throwEventConstruct(Message3Handle h) { throwEventConstruct(*h.registry(), h.entity()); }
void throwEventUpdate(Message3Handle h) { throwEventUpdate(*h.registry(), h.entity()); }
void throwEventDestroy(Message3Handle h) { throwEventDestroy(*h.registry(), h.entity()); }
void throwEventConstruct(const Contact3 c, Message3 e);
void throwEventUpdate(const Contact3 c, Message3 e);
void throwEventDestroy(const Contact3 c, Message3 e);
public: // mm3
bool sendText(const Contact3 c, std::string_view message, bool action = false) override;
bool sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) override;
};