inital commit (message v3)

This commit is contained in:
Green Sky 2023-07-22 23:31:56 +02:00
commit b08d2bf366
No known key found for this signature in database
15 changed files with 744 additions and 0 deletions

31
CMakeLists.txt Normal file
View File

@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(solanaceae)
add_library(solanaceae_message3
./solanaceae/message3/file.hpp
./solanaceae/message3/file_r_mem.hpp
./solanaceae/message3/file_r_file.hpp
./solanaceae/message3/file_w_file.hpp
./solanaceae/message3/file_rw_file.hpp
./solanaceae/message3/components.hpp
./solanaceae/message3/components_id.inl
./solanaceae/message3/message_model3.hpp
./solanaceae/message3/registry_message_model.hpp
./solanaceae/message3/registry_message_model.cpp
./solanaceae/message3/message_time_sort.hpp
./solanaceae/message3/message_time_sort.cpp
)
target_include_directories(solanaceae_message3 PUBLIC .)
target_compile_features(solanaceae_message3 PUBLIC cxx_std_17)
target_link_libraries(solanaceae_message3 PUBLIC
solanaceae_util
solanaceae_contact
EnTT::EnTT
)

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
The Code is under the following License, if not stated otherwise:
MIT License
Copyright (c) 2023 Erik Scholz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

6
README.md Normal file
View File

@ -0,0 +1,6 @@
`plant !`
provides `message` functionallity for solanaceae code.
relies on [EnTT](https://github.com/skypjack/entt)

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;
};