From 2aa2af0007b7e969845642027c635cd3fd9c8aea Mon Sep 17 00:00:00 2001 From: Claudius Holeksa Date: Wed, 3 May 2023 20:34:02 +0200 Subject: Moved dirs and added codec-json dir --- SConstruct | 2 +- default.nix | 17 +- forstio/SConscript | 8 - forstio/async/.nix/derivation.nix | 31 - forstio/async/SConscript | 38 -- forstio/async/SConstruct | 66 --- forstio/async/async.cpp | 419 ------------- forstio/async/async.h | 1023 -------------------------------- forstio/async/async.tmpl.h | 769 ------------------------ forstio/codec-json/.nix/derivation.nix | 34 -- forstio/codec-json/SConscript | 38 -- forstio/codec-json/SConstruct | 66 --- forstio/codec/.nix/derivation.nix | 31 - forstio/codec/SConscript | 38 -- forstio/codec/SConstruct | 66 --- forstio/codec/data.h | 89 --- forstio/codec/proto_kel.h | 41 -- forstio/codec/schema.h | 79 --- forstio/core/.nix/derivation.nix | 26 - forstio/core/SConscript | 38 -- forstio/core/SConstruct | 66 --- forstio/core/buffer.cpp | 434 -------------- forstio/core/buffer.h | 195 ------ forstio/core/common.h | 75 --- forstio/core/error.cpp | 121 ---- forstio/core/error.h | 233 -------- forstio/core/string_literal.h | 40 -- forstio/io-tls/.nix/derivation.nix | 35 -- forstio/io-tls/SConscript | 38 -- forstio/io-tls/SConstruct | 66 --- forstio/io-tls/tls.cpp | 252 -------- forstio/io-tls/tls.h | 68 --- forstio/io/.nix/derivation.nix | 32 - forstio/io/SConscript | 38 -- forstio/io/SConstruct | 66 --- forstio/io/io.cpp | 70 --- forstio/io/io.h | 214 ------- forstio/io/io_helpers.cpp | 85 --- forstio/io/io_helpers.h | 53 -- src/SConscript | 8 + src/async/.nix/derivation.nix | 31 + src/async/SConscript | 38 ++ src/async/SConstruct | 66 +++ src/async/async.cpp | 419 +++++++++++++ src/async/async.h | 1023 ++++++++++++++++++++++++++++++++ src/async/async.tmpl.h | 769 ++++++++++++++++++++++++ src/codec-json/.nix/derivation.nix | 34 ++ src/codec-json/SConscript | 38 ++ src/codec-json/SConstruct | 66 +++ src/codec-json/json.h | 12 + src/codec/.nix/derivation.nix | 31 + src/codec/SConscript | 38 ++ src/codec/SConstruct | 66 +++ src/codec/data.h | 89 +++ src/codec/proto_kel.h | 41 ++ src/codec/schema.h | 79 +++ src/core/.nix/derivation.nix | 26 + src/core/SConscript | 38 ++ src/core/SConstruct | 66 +++ src/core/buffer.cpp | 434 ++++++++++++++ src/core/buffer.h | 195 ++++++ src/core/common.h | 75 +++ src/core/error.cpp | 121 ++++ src/core/error.h | 233 ++++++++ src/core/string_literal.h | 40 ++ src/io-tls/.nix/derivation.nix | 35 ++ src/io-tls/SConscript | 38 ++ src/io-tls/SConstruct | 66 +++ src/io-tls/tls.cpp | 252 ++++++++ src/io-tls/tls.h | 68 +++ src/io/.nix/derivation.nix | 32 + src/io/SConscript | 38 ++ src/io/SConstruct | 66 +++ src/io/io.cpp | 70 +++ src/io/io.h | 214 +++++++ src/io/io_helpers.cpp | 85 +++ src/io/io_helpers.h | 53 ++ 77 files changed, 5106 insertions(+), 5087 deletions(-) delete mode 100644 forstio/SConscript delete mode 100644 forstio/async/.nix/derivation.nix delete mode 100644 forstio/async/SConscript delete mode 100644 forstio/async/SConstruct delete mode 100644 forstio/async/async.cpp delete mode 100644 forstio/async/async.h delete mode 100644 forstio/async/async.tmpl.h delete mode 100644 forstio/codec-json/.nix/derivation.nix delete mode 100644 forstio/codec-json/SConscript delete mode 100644 forstio/codec-json/SConstruct delete mode 100644 forstio/codec/.nix/derivation.nix delete mode 100644 forstio/codec/SConscript delete mode 100644 forstio/codec/SConstruct delete mode 100644 forstio/codec/data.h delete mode 100644 forstio/codec/proto_kel.h delete mode 100644 forstio/codec/schema.h delete mode 100644 forstio/core/.nix/derivation.nix delete mode 100644 forstio/core/SConscript delete mode 100644 forstio/core/SConstruct delete mode 100644 forstio/core/buffer.cpp delete mode 100644 forstio/core/buffer.h delete mode 100644 forstio/core/common.h delete mode 100644 forstio/core/error.cpp delete mode 100644 forstio/core/error.h delete mode 100644 forstio/core/string_literal.h delete mode 100644 forstio/io-tls/.nix/derivation.nix delete mode 100644 forstio/io-tls/SConscript delete mode 100644 forstio/io-tls/SConstruct delete mode 100644 forstio/io-tls/tls.cpp delete mode 100644 forstio/io-tls/tls.h delete mode 100644 forstio/io/.nix/derivation.nix delete mode 100644 forstio/io/SConscript delete mode 100644 forstio/io/SConstruct delete mode 100644 forstio/io/io.cpp delete mode 100644 forstio/io/io.h delete mode 100644 forstio/io/io_helpers.cpp delete mode 100644 forstio/io/io_helpers.h create mode 100644 src/SConscript create mode 100644 src/async/.nix/derivation.nix create mode 100644 src/async/SConscript create mode 100644 src/async/SConstruct create mode 100644 src/async/async.cpp create mode 100644 src/async/async.h create mode 100644 src/async/async.tmpl.h create mode 100644 src/codec-json/.nix/derivation.nix create mode 100644 src/codec-json/SConscript create mode 100644 src/codec-json/SConstruct create mode 100644 src/codec-json/json.h create mode 100644 src/codec/.nix/derivation.nix create mode 100644 src/codec/SConscript create mode 100644 src/codec/SConstruct create mode 100644 src/codec/data.h create mode 100644 src/codec/proto_kel.h create mode 100644 src/codec/schema.h create mode 100644 src/core/.nix/derivation.nix create mode 100644 src/core/SConscript create mode 100644 src/core/SConstruct create mode 100644 src/core/buffer.cpp create mode 100644 src/core/buffer.h create mode 100644 src/core/common.h create mode 100644 src/core/error.cpp create mode 100644 src/core/error.h create mode 100644 src/core/string_literal.h create mode 100644 src/io-tls/.nix/derivation.nix create mode 100644 src/io-tls/SConscript create mode 100644 src/io-tls/SConstruct create mode 100644 src/io-tls/tls.cpp create mode 100644 src/io-tls/tls.h create mode 100644 src/io/.nix/derivation.nix create mode 100644 src/io/SConscript create mode 100644 src/io/SConstruct create mode 100644 src/io/io.cpp create mode 100644 src/io/io.h create mode 100644 src/io/io_helpers.cpp create mode 100644 src/io/io_helpers.h diff --git a/SConstruct b/SConstruct index ead6b58..ba3c56b 100644 --- a/SConstruct +++ b/SConstruct @@ -56,7 +56,7 @@ env.sources = []; env.headers = []; Export('env') -SConscript('forstio/SConscript') +SConscript('src/SConscript') env.Alias('cdb', env.cdb); env.Alias('all', [env.targets]); diff --git a/default.nix b/default.nix index aff0363..1f679d7 100644 --- a/default.nix +++ b/default.nix @@ -5,34 +5,41 @@ let version = "0.0.0"; in rec { forstio = { - core = pkgs.callPackage forstio/core/.nix/derivation.nix { + core = pkgs.callPackage src/core/.nix/derivation.nix { inherit version; clang = pkgs.clang_15; clang-tools = pkgs.clang-tools_15; }; - async = pkgs.callPackage forstio/async/.nix/derivation.nix { + async = pkgs.callPackage src/async/.nix/derivation.nix { inherit version; inherit forstio; clang = pkgs.clang_15; clang-tools = pkgs.clang-tools_15; }; - codec = pkgs.callPackage forstio/codec/.nix/derivation.nix { + codec = pkgs.callPackage src/codec/.nix/derivation.nix { inherit version; inherit forstio; clang = pkgs.clang_15; clang-tools = pkgs.clang-tools_15; }; - io = pkgs.callPackage forstio/io/.nix/derivation.nix { + codec-json = pkgs.callPackage src/codec-json/.nix/derivation.nix { inherit version; inherit forstio; clang = pkgs.clang_15; clang-tools = pkgs.clang-tools_15; }; - io-tls = pkgs.callPackage forstio/io-tls/.nix/derivation.nix { + io = pkgs.callPackage src/io/.nix/derivation.nix { + inherit version; + inherit forstio; + clang = pkgs.clang_15; + clang-tools = pkgs.clang-tools_15; + }; + + io-tls = pkgs.callPackage src/io-tls/.nix/derivation.nix { inherit version; inherit forstio; clang = pkgs.clang_15; diff --git a/forstio/SConscript b/forstio/SConscript deleted file mode 100644 index 8da5a3d..0000000 --- a/forstio/SConscript +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/false - -Import('env') - -# Export to other libs -Export('env'); -SConscript('core/SConscript'); -SConscript('async/SConscript'); diff --git a/forstio/async/.nix/derivation.nix b/forstio/async/.nix/derivation.nix deleted file mode 100644 index 8ceac08..0000000 --- a/forstio/async/.nix/derivation.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ lib -, stdenvNoCC -, scons -, clang -, clang-tools -, version -, forstio -}: - -let - -in stdenvNoCC.mkDerivation { - pname = "forstio-async"; - inherit version; - - src = ./..; - - enableParallelBuilding = true; - - nativeBuildInputs = [ - scons - clang - clang-tools - ]; - - buildInputs = [ - forstio.core - ]; - - outputs = ["out" "dev"]; -} diff --git a/forstio/async/SConscript b/forstio/async/SConscript deleted file mode 100644 index 69f8950..0000000 --- a/forstio/async/SConscript +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/false - -import os -import os.path -import glob - - -Import('env') - -dir_path = Dir('.').abspath - -# Environment for base library -async_env = env.Clone(); - -async_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) -async_env.headers = sorted(glob.glob(dir_path + "/*.h")) - -env.sources += async_env.sources; -env.headers += async_env.headers; - -## Shared lib -objects_shared = [] -async_env.add_source_files(objects_shared, async_env.sources, shared=True); -async_env.library_shared = async_env.SharedLibrary('#build/forstio-async', [objects_shared]); - -## Static lib -objects_static = [] -async_env.add_source_files(objects_static, async_env.sources, shared=False); -async_env.library_static = async_env.StaticLibrary('#build/forstio-async', [objects_static]); - -# Set Alias -env.Alias('library_async', [async_env.library_shared, async_env.library_static]); - -env.targets += ['library_async']; - -# Install -env.Install('$prefix/lib/', [async_env.library_shared, async_env.library_static]); -env.Install('$prefix/include/forstio/async/', [async_env.headers]); diff --git a/forstio/async/SConstruct b/forstio/async/SConstruct deleted file mode 100644 index 0d7b7c6..0000000 --- a/forstio/async/SConstruct +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import os.path -import glob -import re - - -if sys.version_info < (3,): - def isbasestring(s): - return isinstance(s,basestring) -else: - def isbasestring(s): - return isinstance(s, (str,bytes)) - -def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): - - if isbasestring(filetype): - dir_path = self.Dir('.').abspath - filetype = sorted(glob.glob(dir_path+"/"+filetype)) - - for path in filetype: - target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) - if shared: - target_name+='.os' - sources.append( self.SharedObject( target=target_name, source=path ) ) - else: - target_name+='.o' - sources.append( self.StaticObject( target=target_name, source=path ) ) - pass - -def isAbsolutePath(key, dirname, env): - assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) - -env_vars = Variables( - args=ARGUMENTS -) - -env_vars.Add('prefix', - help='Installation target location of build results and headers', - default='/usr/local/', - validator=isAbsolutePath -) - -env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], - CPPDEFINES=['SAW_UNIX'], - CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], - LIBS=['forstio-core']) -env.__class__.add_source_files = add_kel_source_files -env.Tool('compilation_db'); -env.cdb = env.CompilationDatabase('compile_commands.json'); - -env.objects = []; -env.sources = []; -env.headers = []; -env.targets = []; - -Export('env') -SConscript('SConscript') - -env.Alias('cdb', env.cdb); -env.Alias('all', [env.targets]); -env.Default('all'); - -env.Alias('install', '$prefix') diff --git a/forstio/async/async.cpp b/forstio/async/async.cpp deleted file mode 100644 index c53ffa6..0000000 --- a/forstio/async/async.cpp +++ /dev/null @@ -1,419 +0,0 @@ -#include "async.h" -#include -#include - -#include -#include - -namespace saw { -namespace { -thread_local event_loop *local_loop = nullptr; - -event_loop ¤t_event_loop() { - event_loop *loop = local_loop; - assert(loop); - return *loop; -} -} // namespace - -conveyor_node::conveyor_node() {} - -conveyor_node_with_child_mixin::conveyor_node_with_child_mixin( - own &&child_, conveyor_node &owner) - : child{std::move(child_)} { - assert(child); - - child->notify_parent_attached(owner); -} - -error_or> -conveyor_node_with_child_mixin::swap_child(own &&swapee) { - SAW_ASSERT(child) { - return make_error("Child should exist if this function is called"); - } - own old_child = std::move(child); - - /** - * We need the parent of the old_child's next storage - */ - conveyor_storage *old_storage = old_child->next_storage(); - conveyor_storage *old_storage_parent = - old_storage ? old_storage->get_parent() : nullptr; - - /** - * Swap in the new child - */ - if (swapee) { - child = std::move(swapee); - - /** - * Then we need to set the new child's storage parent since the next - * storage has a nullptr set And if the old_storage_parent is a nullptr, - * then it doesn't matter. So we don't check for it - */ - conveyor_storage *swapee_storage = child->next_storage(); - if (swapee_storage) { - swapee_storage->set_parent(old_storage_parent); - } - } - - return old_child; -} - -conveyor_storage::conveyor_storage() {} - -conveyor_storage::~conveyor_storage() {} - -conveyor_storage *conveyor_storage::get_parent() const { return parent_; } - -void conveyor_event_storage::set_parent(conveyor_storage *p) { - /* - * parent check isn't needed, but is used - * for the assert, because the storage should - * be armed if there was an element present - * and a valid parent - */ - if (/*!parent && */ p && !is_armed() && queued() > 0) { - assert(!parent_); - if (p->space() > 0) { - arm_later(); - } - } - - parent_ = p; -} - -conveyor_event_storage::conveyor_event_storage() : conveyor_storage{} {} - -conveyor_base::conveyor_base(own &&node_p) - : node_{std::move(node_p)} {} - -error propagate_error::operator()(const error &error) const { - return error.copy_error(); -} - -error propagate_error::operator()(error &&err) { return std::move(err); } - -event::event() : event(current_event_loop()) {} - -event::event(event_loop &loop) : loop_{loop} {} - -event::~event() { disarm(); } - -void event::arm_next() { - assert(&loop_ == local_loop); - if (prev_ == nullptr) { - // Push the next_insert_point back by one - // and inserts itself before that - next_ = *loop_.next_insert_point_; - prev_ = loop_.next_insert_point_; - *prev_ = this; - if (next_) { - next_->prev_ = &next_; - } - - // Set the new insertion ptr location to next - loop_.next_insert_point_ = &next_; - - // Pushes back the later insert point if it was pointing at the - // previous event - if (loop_.later_insert_point_ == prev_) { - loop_.later_insert_point_ = &next_; - } - - // If tail_ points at the same location then - // we are at the end and have to update tail_ then. - // Technically should be possible by checking if - // next is a `nullptr` - if (loop_.tail_ == prev_) { - loop_.tail_ = &next_; - } - - loop_.set_runnable(true); - } -} - -void event::arm_later() { - assert(&loop_ == local_loop); - - if (prev_ == nullptr) { - next_ = *loop_.later_insert_point_; - prev_ = loop_.later_insert_point_; - *prev_ = this; - if (next_) { - next_->prev_ = &next_; - } - - loop_.later_insert_point_ = &next_; - if (loop_.tail_ == prev_) { - loop_.tail_ = &next_; - } - - loop_.set_runnable(true); - } -} - -void event::arm_last() { - assert(&loop_ == local_loop); - - if (prev_ == nullptr) { - next_ = *loop_.later_insert_point_; - prev_ = loop_.later_insert_point_; - *prev_ = this; - if (next_) { - next_->prev_ = &next_; - } - - if (loop_.tail_ == prev_) { - loop_.tail_ = &next_; - } - - loop_.set_runnable(true); - } -} - -void event::disarm() { - if (prev_ != nullptr) { - if (loop_.tail_ == &next_) { - loop_.tail_ = prev_; - } - - if (loop_.next_insert_point_ == &next_) { - loop_.next_insert_point_ = prev_; - } - - *prev_ = next_; - if (next_) { - next_->prev_ = prev_; - } - - prev_ = nullptr; - next_ = nullptr; - } -} - -bool event::is_armed() const { return prev_ != nullptr; } - -conveyor_sink::conveyor_sink() : node_{nullptr} {} - -conveyor_sink::conveyor_sink(own &&node_p) - : node_{std::move(node_p)} {} - -void event_loop::set_runnable(bool runnable) { is_runnable_ = runnable; } - -event_loop::event_loop() {} - -event_loop::event_loop(own &&ep) - : event_port_{std::move(ep)} {} - -event_loop::~event_loop() { assert(local_loop != this); } - -void event_loop::enter_scope() { - assert(!local_loop); - local_loop = this; -} - -void event_loop::leave_scope() { - assert(local_loop == this); - local_loop = nullptr; -} - -bool event_loop::turn_loop() { - size_t turn_step = 0; - while (head_ && turn_step < 65536) { - if (!turn()) { - return false; - } - ++turn_step; - } - return true; -} - -bool event_loop::turn() { - event *event = head_; - - if (!event) { - return false; - } - - head_ = event->next_; - if (head_) { - head_->prev_ = &head_; - } - - next_insert_point_ = &head_; - if (later_insert_point_ == &event->next_) { - later_insert_point_ = &head_; - } - if (tail_ == &event->next_) { - tail_ = &head_; - } - - event->next_ = nullptr; - event->prev_ = nullptr; - - next_insert_point_ = &head_; - - event->fire(); - - return true; -} - -bool event_loop::wait(const std::chrono::steady_clock::duration &duration) { - if (event_port_) { - event_port_->wait(duration); - } - - return turn_loop(); -} - -bool event_loop::wait(const std::chrono::steady_clock::time_point &time_point) { - if (event_port_) { - event_port_->wait(time_point); - } - - return turn_loop(); -} - -bool event_loop::wait() { - if (event_port_) { - event_port_->wait(); - } - - return turn_loop(); -} - -bool event_loop::poll() { - if (event_port_) { - event_port_->poll(); - } - - return turn_loop(); -} - -event_port *event_loop::event_port() { return event_port_.get(); } - -conveyor_sink_set &event_loop::daemon() { - if (!daemon_sink_) { - daemon_sink_ = heap(); - } - return *daemon_sink_; -} - -wait_scope::wait_scope(event_loop &loop) : loop_{loop} { loop_.enter_scope(); } - -wait_scope::~wait_scope() { loop_.leave_scope(); } - -void wait_scope::wait() { loop_.wait(); } - -void wait_scope::wait(const std::chrono::steady_clock::duration &duration) { - loop_.wait(duration); -} - -void wait_scope::wait(const std::chrono::steady_clock::time_point &time_point) { - loop_.wait(time_point); -} - -void wait_scope::poll() { loop_.poll(); } - -error_or> -convert_conveyor_node_base::swap_child(own &&swapee) noexcept { - return child_mixin_.swap_child(std::move(swapee)); -} - -conveyor_storage *convert_conveyor_node_base::next_storage() noexcept { - if (!child_mixin_.child) { - return nullptr; - } - return child_mixin_.child->next_storage(); -} - -immediate_conveyor_node_base::immediate_conveyor_node_base() - : conveyor_event_storage{} {} - -merge_conveyor_node_base::merge_conveyor_node_base() - : conveyor_event_storage{} {} - -error_or> queue_buffer_conveyor_node_base::swap_child( - own &&swapee_) noexcept { - return child_mixin_.swap_child(std::move(swapee_)); -} - -void conveyor_sink_set::destroy_sink_conveyor_node(conveyor_node &node) { - if (!is_armed()) { - arm_last(); - } - - delete_nodes_.push(&node); -} - -void conveyor_sink_set::fail(error &&error) { - /// @todo call error_handler -} - -conveyor_sink_set::conveyor_sink_set(event_loop &event_loop) - : event{event_loop} {} - -void conveyor_sink_set::add(conveyor &&sink) { - auto nas = conveyor::from_conveyor(std::move(sink)); - SAW_ASSERT(nas) { return; } - conveyor_storage *storage = nas->next_storage(); - - own sink_node = nullptr; - try { - sink_node = heap(std::move(nas), *this); - } catch (std::bad_alloc &) { - return; - } - if (storage) { - storage->set_parent(sink_node.get()); - } - - sink_nodes_.emplace_back(std::move(sink_node)); -} - -void conveyor_sink_set::fire() { - while (!delete_nodes_.empty()) { - conveyor_node *node = delete_nodes_.front(); - /*auto erased = */ std::remove_if(sink_nodes_.begin(), - sink_nodes_.end(), - [node](own &element) { - return node == element.get(); - }); - delete_nodes_.pop(); - } -} - -convert_conveyor_node_base::convert_conveyor_node_base(own &&dep) - : child_mixin_{std::move(dep), *this} {} - -void convert_conveyor_node_base::get_result(error_or_value &err_or_val) { - get_impl(err_or_val); -} - -void attach_conveyor_node_base::get_result( - error_or_value &err_or_val) noexcept { - if (child_mixin_.child) { - child_mixin_.child->get_result(err_or_val); - } -} - -error_or> -attach_conveyor_node_base::swap_child(own &&swapee_) noexcept { - return child_mixin_.swap_child(std::move(swapee_)); -} - -conveyor_storage *attach_conveyor_node_base::next_storage() noexcept { - if (!child_mixin_.child) { - return nullptr; - } - - return child_mixin_.child->next_storage(); -} - -void detach_conveyor(conveyor &&conveyor) { - event_loop &loop = current_event_loop(); - conveyor_sink_set &sink = loop.daemon(); - sink.add(std::move(conveyor)); -} -} // namespace saw diff --git a/forstio/async/async.h b/forstio/async/async.h deleted file mode 100644 index 4cfed60..0000000 --- a/forstio/async/async.h +++ /dev/null @@ -1,1023 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace saw { -class conveyor_storage; -class conveyor_node { -public: - conveyor_node(); - virtual ~conveyor_node() = default; - - /** - * Internal method to retrieve results from children - */ - virtual void get_result(error_or_value &err_or_val) = 0; - - /** - * Swap out child with another one - */ - virtual error_or> - swap_child(own &&swapee_) = 0; - - /** - * Retrieve the next storage node - */ - virtual conveyor_storage *next_storage() = 0; - - /** - * Notify that a new parent was attached - * Only relevant for the feeding nodes - */ - virtual void notify_parent_attached(conveyor_node &){}; -}; - -class conveyor_node_with_child_mixin final { -public: - own child = nullptr; - - conveyor_node_with_child_mixin(own &&child_, - conveyor_node &owner_); - ~conveyor_node_with_child_mixin() = default; - - /** - * Swap out children and return the child ptr, since the caller is the child - * itself. Stack needs to be cleared before the child is destroyed, so the - * swapped out node is returned as well. - */ - error_or> swap_child(own &&swapee); -}; - -class conveyor_node_with_parent_mixin final { -public: - conveyor_node *parent = nullptr; - - error_or> - swap_child_of_parent(own &&swapee) { - SAW_ASSERT(parent) { - return make_error( - "Can't swap child, because parent doesn't exist"); - } - - return parent->swap_child(std::move(swapee)); - } - void change_parent(conveyor_node *p) { parent = p; } -}; - -class event_loop; -class wait_scope; -/* - * Event class similar to capn'proto. - * https://github.com/capnproto/capnproto - */ -class event { -private: - event_loop &loop_; - event **prev_ = nullptr; - event *next_ = nullptr; - - friend class event_loop; - -public: - event(); - event(event_loop &loop); - virtual ~event(); - - virtual void fire() = 0; - - void arm_next(); - void arm_later(); - void arm_last(); - void disarm(); - - bool is_armed() const; -}; - -class conveyor_storage { -protected: - conveyor_storage *parent_ = nullptr; - -public: - conveyor_storage(); - virtual ~conveyor_storage(); - - virtual size_t space() const = 0; - virtual size_t queued() const = 0; - virtual void child_has_fired() = 0; - virtual void parent_has_fired() = 0; - - virtual void set_parent(conveyor_storage *parent) = 0; - conveyor_storage *get_parent() const; -}; - -class conveyor_event_storage : public conveyor_storage, public event { -public: - conveyor_event_storage(); - virtual ~conveyor_event_storage() = default; - - void set_parent(conveyor_storage *parent) override; -}; - -class conveyor_base { -protected: - own node_; - -public: - conveyor_base(own &&node_p); - virtual ~conveyor_base() = default; - - conveyor_base(conveyor_base &&) = default; - conveyor_base &operator=(conveyor_base &&) = default; - - void get(error_or_value &err_or_val); -}; - -template class conveyor; - -template conveyor chained_conveyor_type(T *); - -// template Conveyor chainedConveyorType(Conveyor *); - -template T remove_error_or_type(T *); - -template T remove_error_or_type(error_or *); - -template -using remove_error_or = decltype(remove_error_or_type((T *)nullptr)); - -template -using chained_conveyors = decltype(chained_conveyor_type((T *)nullptr)); - -template -using conveyor_result = - chained_conveyors>>; - -struct propagate_error { -public: - error operator()(const error &err) const; - error operator()(error &&err); -}; - -class conveyor_sink { -private: - own node_; - -public: - conveyor_sink(); - conveyor_sink(own &&node); - - conveyor_sink(conveyor_sink &&) = default; - conveyor_sink &operator=(conveyor_sink &&) = default; -}; - -template class merge_conveyor_node_data; - -template class merge_conveyor { -private: - lent> data_; - -public: - merge_conveyor() = default; - merge_conveyor(lent> d); - ~merge_conveyor(); - - void attach(conveyor conv); -}; - -/** - * Main interface for async operations. - */ -template class conveyor final : public conveyor_base { -public: - /** - * Construct an immediately fulfilled node - */ - conveyor(fix_void value); - - /** - * Construct an immediately failed node - */ - conveyor(error &&err); - - /** - * Construct a conveyor with a child node - */ - conveyor(own node_p); - - conveyor(conveyor &&) = default; - conveyor &operator=(conveyor &&) = default; - - /** - * This method converts values or errors from children - */ - template - [[nodiscard]] conveyor_result - then(Func &&func, ErrorFunc &&error_func = propagate_error()); - - /** - * This method adds a buffer node in the conveyor chains which acts as a - * scheduler interrupt point and collects elements up to the supplied limit. - */ - [[nodiscard]] conveyor - buffer(size_t limit = std::numeric_limits::max()); - - /** - * This method just takes ownership of any supplied types, - * which are destroyed when the chain gets destroyed. - * Useful for resource lifetime control. - */ - template - [[nodiscard]] conveyor attach(Args &&...args); - - /** @todo implement - * This method limits the total amount of passed elements - * Be careful where you place this node into the chain. - * If you meant to fork it and destroy paths you shouldn't place - * an interrupt point between the fork and this limiter - */ - [[nodiscard]] conveyor limit(size_t val = 1); - - /** - * - */ - [[nodiscard]] std::pair, merge_conveyor> merge(); - - /** - * Moves the conveyor chain into a thread local storage point which drops - * every element. Use sink() if you want to control the lifetime of a - * conveyor chain - */ - template - void detach(ErrorFunc &&err_func = propagate_error()); - /** - * Creates a local sink which drops elements, but lifetime control remains - * in your hand. - */ - template - [[nodiscard]] conveyor_sink - sink(ErrorFunc &&error_func = propagate_error()); - - /** - * If no sink() or detach() is used you have to take elements out of the - * chain yourself. - */ - error_or> take(); - - /** @todo implement - * Specifically pump elements through this chain with the provided - * wait_scope - */ - void poll(wait_scope &wait_scope); - - // helper - static conveyor to_conveyor(own node); - - // helper - static own from_conveyor(conveyor conveyor); -}; - -template conveyor_result exec_later(Func &&func); - -/* - * Join Conveyors into a single one - */ -template -conveyor> -join_conveyors(std::tuple...> &conveyors); - -template class conveyor_feeder { -public: - virtual ~conveyor_feeder() = default; - - virtual void feed(T &&data) = 0; - virtual void fail(error &&error) = 0; - - virtual size_t space() const = 0; - virtual size_t queued() const = 0; - - virtual error swap(conveyor &&conveyor) noexcept = 0; -}; - -template <> class conveyor_feeder { -public: - virtual ~conveyor_feeder() = default; - - virtual void feed(void_t &&value = void_t{}) = 0; - virtual void fail(error &&error) = 0; - - virtual size_t space() const = 0; - virtual size_t queued() const = 0; - - virtual error swap(conveyor &&conveyor) noexcept = 0; -}; - -template struct conveyor_and_feeder { - own> feeder; - conveyor conveyor; -}; - -template conveyor_and_feeder new_conveyor_and_feeder(); - -template conveyor_and_feeder one_time_conveyor_and_feeder(); - -enum class Signal : uint8_t { Terminate, User1 }; - -/** - * Class which acts as a correspondent between the running framework and outside - * events which may be signals from the operating system or just other threads. - * Default EventPorts are supplied by setupAsyncIo() in io.h - */ -class event_port { -public: - virtual ~event_port() = default; - - virtual conveyor on_signal(Signal signal) = 0; - - virtual void poll() = 0; - virtual void wait() = 0; - virtual void wait(const std::chrono::steady_clock::duration &) = 0; - virtual void wait(const std::chrono::steady_clock::time_point &) = 0; - - virtual void wake() = 0; -}; - -class sink_conveyor_node; - -class conveyor_sink_set final : public event { -private: - /* - class Helper final : public Event { - private: - void destroySinkConveyorNode(ConveyorNode& sink); - void fail(Error&& error); - - std::vector> sink_nodes; - std::queue delete_nodes; - std::function error_handler; - - public: - ConveyorSinks() = default; - ConveyorSinks(EventLoop& event_loop); - - void add(Conveyor node); - - void fire() override {} - }; - - gin::Own helper; - */ - friend class sink_conveyor_node; - - void destroy_sink_conveyor_node(conveyor_node &sink_node); - void fail(error &&err); - - std::list> sink_nodes_; - - std::queue delete_nodes_; - - std::function error_handler_; - -public: - // ConveyorSinks(); - // ConveyorSinks(EventLoop& event_loop); - conveyor_sink_set() = default; - conveyor_sink_set(event_loop &event_loop); - - void add(conveyor &&node); - - void fire() override; -}; - -/* - * EventLoop class similar to capn'proto. - * https://github.com/capnproto/capnproto - */ -class event_loop { -private: - friend class event; - event *head_ = nullptr; - event **tail_ = &head_; - event **next_insert_point_ = &head_; - event **later_insert_point_ = &head_; - - bool is_runnable_ = false; - - own event_port_ = nullptr; - - own daemon_sink_ = nullptr; - - // functions - void set_runnable(bool runnable); - - friend class wait_scope; - void enter_scope(); - void leave_scope(); - - bool turn_loop(); - bool turn(); - -public: - event_loop(); - event_loop(own &&port); - ~event_loop(); - - event_loop(event_loop &&) = default; - event_loop &operator=(event_loop &&) = default; - - bool wait(); - bool wait(const std::chrono::steady_clock::duration &); - bool wait(const std::chrono::steady_clock::time_point &); - bool poll(); - - event_port *event_port(); - - conveyor_sink_set &daemon(); -}; - -/* - * WaitScope class similar to capn'proto. - * https://github.com/capnproto/capnproto - */ -class wait_scope { -private: - event_loop &loop_; - -public: - wait_scope(event_loop &loop); - ~wait_scope(); - - void wait(); - void wait(const std::chrono::steady_clock::duration &); - void wait(const std::chrono::steady_clock::time_point &); - void poll(); -}; - -template conveyor_result yield_next(Func &&func); - -template conveyor_result yield_later(Func &&func); - -template conveyor_result yield_last(Func &&func); -} // namespace saw - -// Secret stuff -// Aka private semi hidden classes -namespace saw { - -template struct fix_void_caller { - template static Out apply(Func &func, In &&in) { - return func(std::move(in)); - } -}; - -template struct fix_void_caller { - template static Out apply(Func &func, void_t &&in) { - (void)in; - return func(); - } -}; - -template struct fix_void_caller { - template static void_t apply(Func &func, In &&in) { - func(std::move(in)); - return void_t{}; - } -}; - -template <> struct fix_void_caller { - template static void_t apply(Func &func, void_t &&in) { - (void)in; - func(); - return void_t{}; - } -}; - -template class adapt_conveyor_node; - -template -class adapt_conveyor_feeder final : public conveyor_feeder> { -private: - adapt_conveyor_node *feedee_ = nullptr; - -public: - ~adapt_conveyor_feeder(); - - void set_feedee(adapt_conveyor_node *feedee); - - void feed(T &&value) override; - void fail(error &&error) override; - - size_t space() const override; - size_t queued() const override; - - error swap(conveyor &&conv) noexcept override; -}; - -template -class adapt_conveyor_node final : public conveyor_node, - public conveyor_event_storage { -private: - adapt_conveyor_feeder *feeder_ = nullptr; - - std::queue>> storage_; - - conveyor_node_with_parent_mixin parent_node_; - -public: - adapt_conveyor_node(); - ~adapt_conveyor_node(); - - void set_feeder(adapt_conveyor_feeder *feeder); - - void feed(T &&value); - void fail(error &&error); - - // ConveyorNode - void get_result(error_or_value &err_or_val) override; - - error_or> - swap_child(own &&swapee) noexcept override; - - conveyor_storage *next_storage() noexcept override; - void notify_parent_attached(conveyor_node &) noexcept override; - - // ConveyorStorage - size_t space() const override; - size_t queued() const override; - - void child_has_fired() override; - void parent_has_fired() override; - - // Event - void fire() override; -}; - -template class one_time_conveyor_node; - -template -class one_time_conveyor_feeder final : public conveyor_feeder> { -private: - one_time_conveyor_node *feedee_ = nullptr; - -public: - ~one_time_conveyor_feeder(); - - void set_feedee(one_time_conveyor_node *feedee); - - void feed(T &&value) override; - void fail(error &&error) override; - - size_t space() const override; - size_t queued() const override; -}; - -template -class one_time_conveyor_node final : public conveyor_node, - public conveyor_storage, - public event { -private: - one_time_conveyor_feeder *feeder_ = nullptr; - - bool passed_ = false; - maybe> storage_ = std::nullopt; - -public: - ~one_time_conveyor_node(); - - void set_feeder(one_time_conveyor_feeder *feeder); - - void feed(T &&value); - void fail(error &&error); - - // ConveyorNode - void get_result(error_or_value &err_or_val) override; - - error_or> - swap_child(own &&swapee) override; - - // ConveyorStorage - size_t space() const override; - size_t queued() const override; - - void child_has_fired() override {} - void parent_has_fired() override; - - // Event - void fire() override; -}; - -/** - * This class buffers and saves incoming elements and acts as an interrupt node - * for processing calls - */ -class queue_buffer_conveyor_node_base : public conveyor_node, - public conveyor_event_storage { -protected: - conveyor_node_with_child_mixin child_mixin_; - -public: - queue_buffer_conveyor_node_base(own child_) - : conveyor_event_storage{}, child_mixin_{std::move(child_), *this} {} - virtual ~queue_buffer_conveyor_node_base() = default; - - /** - * Use mixin - */ - error_or> - swap_child(own &&swapee_) noexcept override; - - conveyor_storage *next_storage() noexcept override { - return static_cast(this); - } -}; - -template -class queue_buffer_conveyor_node final - : public queue_buffer_conveyor_node_base { -private: - std::queue> storage_; - size_t max_store_; - -public: - queue_buffer_conveyor_node(own dep, size_t max_size) - : queue_buffer_conveyor_node_base{std::move(dep)}, max_store_{ - max_size} {} - // Event - void fire() override; - // ConveyorNode - void get_result(error_or_value &eov) noexcept override; - - // ConveyorStorage - size_t space() const override; - size_t queued() const override; - - void child_has_fired() override; - void parent_has_fired() override; -}; - -class attach_conveyor_node_base : public conveyor_node { -protected: - conveyor_node_with_child_mixin child_mixin_; - -public: - attach_conveyor_node_base(own &&child_) - : child_mixin_{std::move(child_), *this} {} - - virtual ~attach_conveyor_node_base() = default; - - void get_result(error_or_value &err_or_val) noexcept override; - - /** - * Use mixin - */ - error_or> - swap_child(own &&swapee_) noexcept override; - - conveyor_storage *next_storage() noexcept override; -}; - -template -class attach_conveyor_node final : public attach_conveyor_node_base { -public: - attach_conveyor_node(own &&dep, Args &&...args) - : attach_conveyor_node_base(std::move(dep)), attached_data_{ - std::move(args...)} {} - -private: - std::tuple attached_data_; -}; - -class convert_conveyor_node_base : public conveyor_node { -public: - convert_conveyor_node_base(own &&dep); - virtual ~convert_conveyor_node_base() = default; - - void get_result(error_or_value &err_or_val) override; - - virtual void get_impl(error_or_value &err_or_val) = 0; - - error_or> - swap_child(own &&swapee) noexcept override; - - conveyor_storage *next_storage() noexcept override; - -protected: - conveyor_node_with_child_mixin child_mixin_; -}; - -template -class convert_conveyor_node final : public convert_conveyor_node_base { -private: - Func func_; - ErrorFunc error_func_; - - static_assert(std::is_same>::value, - "Should never be of type ErrorOr"); - -public: - convert_conveyor_node(own &&dep, Func &&func, - ErrorFunc &&error_func) - : convert_conveyor_node_base(std::move(dep)), func_{std::move(func)}, - error_func_{std::move(error_func)} {} - - void get_impl(error_or_value &err_or_val) noexcept override { - error_or> dep_eov; - error_or>> &eov = - err_or_val.as>>(); - if (child_mixin_.child) { - child_mixin_.child->get_result(dep_eov); - if (dep_eov.is_value()) { - try { - - eov = fix_void_caller::apply( - func_, std::move(dep_eov.get_value())); - } catch (const std::bad_alloc &) { - eov = make_error("Out of memory"); - } catch (const std::exception &) { - eov = make_error( - "Exception in chain occured. Return ErrorOr if you " - "want to handle errors which are recoverable"); - } - } else if (dep_eov.is_error()) { - eov = error_func_(std::move(dep_eov.get_error())); - } else { - eov = make_error("No value set in dependency"); - } - } else { - eov = make_error("Conveyor doesn't have child"); - } - } -}; - -class sink_conveyor_node final : public conveyor_node, - public conveyor_event_storage { -private: - conveyor_node_with_child_mixin child_mixin_; - conveyor_sink_set *conveyor_sink_; - -public: - sink_conveyor_node(own node, conveyor_sink_set &conv_sink) - : conveyor_event_storage{}, child_mixin_{std::move(node), *this}, - conveyor_sink_{&conv_sink} {} - - sink_conveyor_node(own node) - : conveyor_event_storage{}, child_mixin_{std::move(node), *this}, - conveyor_sink_{nullptr} {} - - // Event only queued if a critical error occured - void fire() override { - // Queued for destruction of children, because this acts as a sink and - // no other event should be here - child_mixin_.child = nullptr; - - if (conveyor_sink_) { - conveyor_sink_->destroy_sink_conveyor_node(*this); - conveyor_sink_ = nullptr; - } - } - - // ConveyorStorage - size_t space() const override { return 1; } - size_t queued() const override { return 0; } - - // ConveyorNode - void get_result(error_or_value &err_or_val) noexcept override { - err_or_val.as() = - make_error("In a sink node no result can be returned"); - } - - error_or> - swap_child(own &&swapee) noexcept override { - return child_mixin_.swap_child(std::move(swapee)); - } - - // ConveyorStorage - void child_has_fired() override { - if (child_mixin_.child) { - error_or dep_eov; - child_mixin_.child->get_result(dep_eov); - if (dep_eov.is_error()) { - if (dep_eov.get_error().is_critical()) { - if (!is_armed()) { - arm_last(); - } - } - if (conveyor_sink_) { - conveyor_sink_->fail(std::move(dep_eov.get_error())); - } - } - } - } - - /* - * No parent needs to be fired since we always have space - */ - void parent_has_fired() override {} - - conveyor_storage *next_storage() override { - // Should never happen though - assert(false); - return nullptr; - // return static_cast(this); - } -}; - -class immediate_conveyor_node_base : public conveyor_node, - public conveyor_event_storage { -private: -public: - immediate_conveyor_node_base(); - - virtual ~immediate_conveyor_node_base() = default; - - error_or> - swap_child(own &&swapee) noexcept override { - (void)swapee; - return make_error("Node doesn't support swapping"); - } - - conveyor_storage *next_storage() noexcept override { - return static_cast(this); - } -}; - -template -class immediate_conveyor_node final : public immediate_conveyor_node_base { -private: - error_or> value_; - uint8_t retrieved_; - -public: - immediate_conveyor_node(fix_void &&val); - immediate_conveyor_node(error &&error); - - // ConveyorStorage - size_t space() const override; - size_t queued() const override; - - void child_has_fired() override; - void parent_has_fired() override; - - // ConveyorNode - void get_result(error_or_value &err_or_val) noexcept override { - if (retrieved_ > 0) { - err_or_val.as>() = - make_error("Already taken value"); - } else { - err_or_val.as>() = std::move(value_); - } - if (queued() > 0) { - ++retrieved_; - } - } - - // Event - void fire() override; -}; - -/* - * Collects every incoming value and throws it in one lane - */ -class merge_conveyor_node_base : public conveyor_node, - public conveyor_event_storage { -public: - merge_conveyor_node_base(); - - virtual ~merge_conveyor_node_base() = default; - - conveyor_storage *next_storage() noexcept override { - return static_cast(this); - } -}; - -template -class merge_conveyor_node : public merge_conveyor_node_base { -private: - class appendage final : public conveyor_node, public conveyor_storage { - public: - own child; - merge_conveyor_node *merger; - - maybe>> error_or_value_; - - public: - appendage(own n, merge_conveyor_node &m) - : conveyor_storage{}, child{std::move(n)}, merger{&m}, - error_or_value_{std::nullopt} {} - - bool child_storage_has_element_queued() const { - if (!child) { - return false; - } - conveyor_storage *storage = child->next_storage(); - if (storage) { - return storage->queued() > 0; - } - return false; - } - - void get_appendage_result(error_or_value &eov); - - /** - * ConveyorNode - */ - error_or> - swap_child(own &&swapee_) override; - - conveyor_storage *next_storage() noexcept override { - return static_cast(this); - } - - void get_result(error_or_value &err_or_val) override; - - /** - * ConveyorStorage - */ - size_t space() const override; - - size_t queued() const override; - - void child_has_fired() override; - - void parent_has_fired() override; - - void set_parent(conveyor_storage *par) override; - }; - - friend class merge_conveyor_node_data; - friend class appendage; - - our> data_; - size_t next_appendage_ = 0; - -public: - merge_conveyor_node(our> data); - ~merge_conveyor_node(); - // ConveyorNode - error_or> - swap_child(own &&c) noexcept override; - - // Event - void get_result(error_or_value &err_or_val) noexcept override; - - void fire() override; - - // ConveyorStorage - size_t space() const override; - size_t queued() const override; - void child_has_fired() override; - void parent_has_fired() override; -}; - -template class merge_conveyor_node_data { -public: - std::vector::appendage>> appendages; - - merge_conveyor_node *merger = nullptr; - -public: - void attach(conveyor conv); - - void governing_node_destroyed(); -}; - -/* -class JoinConveyorNodeBase : public ConveyorNode, public ConveyorEventStorage { -private: - -public: -}; - -template -class JoinConveyorNode final : public JoinConveyorNodeBase { -private: - template - class Appendage : public ConveyorEventStorage { - private: - Maybe data = std::nullopt; - - public: - size_t space() const override; - size_t queued() const override; - - void fire() override; - void get_result(ErrorOrValue& eov) override; - }; - - std::tuple...> appendages; - -public: -}; - -*/ - -} // namespace saw - -#include "async.tmpl.h" diff --git a/forstio/async/async.tmpl.h b/forstio/async/async.tmpl.h deleted file mode 100644 index d081fa9..0000000 --- a/forstio/async/async.tmpl.h +++ /dev/null @@ -1,769 +0,0 @@ -#pragma once - -#include -#include - -#include -// Template inlining - -#include - -namespace saw { - -template conveyor_result execLater(Func &&func) { - conveyor conveyor{fix_void{}}; - return conveyor.then(std::move(func)); -} - -template -conveyor::conveyor(fix_void value) : conveyor_base(nullptr) { - // Is there any way to do this? - // @todo new conveyor_base constructor for Immediate values - - own>> immediate = - heap>>(std::move(value)); - - if (!immediate) { - return; - } - - node_ = std::move(immediate); -} - -template -conveyor::conveyor(error &&err) : conveyor_base(nullptr) { - own>> immediate = - heap>>(std::move(err)); - - if (!immediate) { - return; - } - - node_ = std::move(immediate); -} - -template -conveyor::conveyor(own node_p) - : conveyor_base{std::move(node_p)} {} - -template -template -conveyor_result conveyor::then(Func &&func, - ErrorFunc &&error_func) { - own conversion_node = - heap>, fix_void, - Func, ErrorFunc>>( - std::move(node_), std::move(func), std::move(error_func)); - - return conveyor>>::to_conveyor( - std::move(conversion_node)); -} - -template conveyor conveyor::buffer(size_t size) { - SAW_ASSERT(node_) { return conveyor{own{nullptr}}; } - conveyor_storage *storage = node_->next_storage(); - SAW_ASSERT(storage) { return conveyor{own{nullptr}}; } - - own>> storage_node = - heap>>(std::move(node_), size); - - conveyor_storage *storage_ptr = - static_cast(storage_node.get()); - - storage->set_parent(storage_ptr); - return conveyor{std::move(storage_node)}; -} - -template -template -conveyor conveyor::attach(Args &&...args) { - own> attach_node = - heap>(std::move(node_), - std::move(args...)); - return conveyor{std::move(attach_node)}; -} - -template -std::pair, merge_conveyor> conveyor::merge() { - our> data = - share>(); - - own> merge_node = heap>(data); - - SAW_ASSERT(node_) { - return std::make_pair(conveyor{own{nullptr}}, - merge_conveyor{}); - } - conveyor_storage *storage = node_->next_storage(); - SAW_ASSERT(storage) { - return std::make_pair(conveyor{own{nullptr}}, - merge_conveyor{}); - } - - data->attach(conveyor::to_conveyor(std::move(node_))); - - merge_conveyor node_ref{data}; - - return std::make_pair(conveyor{std::move(merge_node)}, - std::move(node_ref)); -} - -template <> -template -conveyor_sink conveyor::sink(ErrorFunc &&error_func) { - conveyor_storage *storage = node_->next_storage(); - SAW_ASSERT(storage) { return conveyor_sink{}; } - - own sink_node = - heap(std::move(node_)); - conveyor_storage *storage_ptr = - static_cast(sink_node.get()); - - storage->set_parent(storage_ptr); - - return conveyor_sink{std::move(sink_node)}; -} - -void detach_conveyor(conveyor &&conveyor); - -template -template -void conveyor::detach(ErrorFunc &&func) { - detach_conveyor(std::move(then([](T &&) {}, std::move(func)))); -} - -template <> -template -void conveyor::detach(ErrorFunc &&func) { - detach_conveyor(std::move(then([]() {}, std::move(func)))); -} - -template -conveyor conveyor::to_conveyor(own node) { - return conveyor{std::move(node)}; -} - -template -own conveyor::from_conveyor(conveyor conveyor) { - return std::move(conveyor.node_); -} - -template error_or> conveyor::take() { - SAW_ASSERT(node_) { - return error_or>{ - make_error("conveyor in invalid state")}; - } - conveyor_storage *storage = node_->next_storage(); - if (storage) { - if (storage->queued() > 0) { - error_or> result; - node_->get_result(result); - return result; - } else { - return error_or>{ - make_error("conveyor buffer has no elements")}; - } - } else { - return error_or>{ - make_error("conveyor node has no child storage")}; - } -} - -template conveyor_and_feeder new_conveyor_and_feeder() { - own>> feeder = - heap>>(); - own>> node = - heap>>(); - - feeder->set_feedee(node.get()); - node->set_feeder(feeder.get()); - - return conveyor_and_feeder{std::move(feeder), - conveyor::to_conveyor(std::move(node))}; -} - -// QueueBuffer -template void queue_buffer_conveyor_node::fire() { - if (child_mixin_.child) { - if (!storage_.empty()) { - if (storage_.front().is_error()) { - if (storage_.front().get_error().is_critical()) { - child_mixin_.child = nullptr; - } - } - } - } - - bool has_space_before_fire = space() > 0; - - if (parent_) { - parent_->child_has_fired(); - if (!storage_.empty() && parent_->space() > 0) { - arm_later(); - } - } - - if (!child_mixin_.child) { - while (!storage_.empty()) { - storage_.pop(); - } - return; - } - - conveyor_storage *ch_storage = child_mixin_.child->next_storage(); - if (ch_storage && !has_space_before_fire) { - ch_storage->parent_has_fired(); - } -} - -template -void queue_buffer_conveyor_node::get_result(error_or_value &eov) noexcept { - error_or &err_or_val = eov.as(); - err_or_val = std::move(storage_.front()); - storage_.pop(); -} - -template size_t queue_buffer_conveyor_node::space() const { - return max_store_ - storage_.size(); -} - -template size_t queue_buffer_conveyor_node::queued() const { - return storage_.size(); -} - -template void queue_buffer_conveyor_node::child_has_fired() { - if (child_mixin_.child && storage_.size() < max_store_) { - error_or eov; - child_mixin_.child->get_result(eov); - - if (eov.is_error()) { - if (eov.get_error().is_critical()) { - } - } - - storage_.push(std::move(eov)); - if (!is_armed()) { - arm_later(); - } - } -} - -template void queue_buffer_conveyor_node::parent_has_fired() { - SAW_ASSERT(parent_) { return; } - - if (parent_->space() == 0) { - return; - } - - if (queued() > 0) { - arm_later(); - } -} - -template -immediate_conveyor_node::immediate_conveyor_node(fix_void &&val) - : value_{std::move(val)}, retrieved_{0} {} - -template -immediate_conveyor_node::immediate_conveyor_node(error &&error) - : value_{std::move(error)}, retrieved_{0} {} - -template size_t immediate_conveyor_node::space() const { - return 0; -} - -template size_t immediate_conveyor_node::queued() const { - return retrieved_ > 1 ? 0 : 1; -} - -template void immediate_conveyor_node::child_has_fired() { - // Impossible case - assert(false); -} - -template void immediate_conveyor_node::parent_has_fired() { - SAW_ASSERT(parent_) { return; } - assert(parent_->space() > 0); - - if (queued() > 0) { - arm_next(); - } -} - -template void immediate_conveyor_node::fire() { - - if (parent_) { - parent_->child_has_fired(); - if (queued() > 0 && parent_->space() > 0) { - arm_last(); - } - } -} - -template -merge_conveyor::merge_conveyor(lent> d) - : data_{std::move(d)} {} - -template merge_conveyor::~merge_conveyor() {} - -template void merge_conveyor::attach(conveyor conveyor) { - auto sp = data_.lock(); - SAW_ASSERT(sp) { return; } - - sp->attach(std::move(conveyor)); -} - -template -merge_conveyor_node::merge_conveyor_node(our> d) - : data_{d} { - SAW_ASSERT(data_) { return; } - - data_->merger = this; -} - -template merge_conveyor_node::~merge_conveyor_node() {} - -template -error_or> -merge_conveyor_node::swap_child(own &&swapee_) noexcept { - (void)swapee_; - return make_error( - "merge_conveyor_node::appendage should block calls to this class"); -} - -template -void merge_conveyor_node::get_result(error_or_value &eov) noexcept { - error_or> &err_or_val = eov.as>(); - - SAW_ASSERT(data_) { return; } - - /// @todo search appendages for result - - auto &appendages = data_->appendages; - next_appendage_ = std::min(appendages.size(), next_appendage_); - - for (size_t i = next_appendage_; i < appendages.size(); ++i) { - if (appendages[i]->queued() > 0) { - err_or_val = std::move(appendages[i]->error_or_value_.value()); - appendages[i]->error_or_value_ = std::nullopt; - next_appendage_ = i + 1; - return; - } - } - for (size_t i = 0; i < next_appendage_; ++i) { - if (appendages[i]->queued() > 0) { - err_or_val = std::move(appendages[i]->error_or_value_.value()); - appendages[i]->error_or_value_ = std::nullopt; - next_appendage_ = i + 1; - return; - } - } - - err_or_val = make_error("No value in Merge appendages"); -} - -template void merge_conveyor_node::fire() { - SAW_ASSERT(queued() > 0) { return; } - - if (parent_) { - parent_->child_has_fired(); - - if (queued() > 0 && parent_->space() > 0) { - arm_later(); - } - } -} - -template size_t merge_conveyor_node::space() const { return 0; } - -template size_t merge_conveyor_node::queued() const { - SAW_ASSERT(data_) { return 0; } - - size_t queue_count = 0; - - for (auto &iter : data_->appendages) { - queue_count += iter->queued(); - } - - return queue_count; -} - -template void merge_conveyor_node::child_has_fired() { - /// This can never happen - assert(false); -} - -template void merge_conveyor_node::parent_has_fired() { - SAW_ASSERT(parent_) { return; } - if (queued() > 0) { - if (parent_->space() > 0) { - arm_later(); - } - } -} - -/** - * merge_conveyor_node::Apendage - */ - -template -error_or> -merge_conveyor_node::appendage::swap_child(own &&swapee_) { - own old_child = std::move(child); - - child = std::move(swapee_); - - // This case should never happen - SAW_ASSERT(old_child) { return make_error("No child exists"); } - - return old_child; -} - -template -void merge_conveyor_node::appendage::get_result(error_or_value &eov) { - error_or> &err_or_val = eov.as>(); - - SAW_ASSERT(queued() > 0) { - err_or_val = - make_error("No element queued in Merge appendage Node"); - return; - } - - err_or_val = std::move(error_or_value_.value()); - error_or_value_ = std::nullopt; -} - -template size_t merge_conveyor_node::appendage::space() const { - SAW_ASSERT(merger) { return 0; } - - if (error_or_value_.has_value()) { - return 0; - } - - return 1; -} - -template size_t merge_conveyor_node::appendage::queued() const { - SAW_ASSERT(merger) { return 0; } - - if (error_or_value_.has_value()) { - return 1; - } - - return 0; -} - -/// @todo delete this function. Replaced by the regular get_result -template -void merge_conveyor_node::appendage::get_appendage_result( - error_or_value &eov) { - error_or> &err_or_val = eov.as>(); - - SAW_ASSERT(queued() > 0) { - err_or_val = - make_error("No element queued in Merge appendage Node"); - return; - } - - err_or_val = std::move(error_or_value_.value()); - error_or_value_ = std::nullopt; -} - -template -void merge_conveyor_node::appendage::child_has_fired() { - SAW_ASSERT(!error_or_value_.has_value()) { return; } - error_or> eov; - child->get_result(eov); - - error_or_value_ = std::move(eov); - - if (!merger->is_armed()) { - merger->arm_later(); - } -} - -template -void merge_conveyor_node::appendage::parent_has_fired() { - conveyor_storage *child_storage = child->next_storage(); - if (child_storage) { - child_storage->parent_has_fired(); - } -} - -template -void merge_conveyor_node::appendage::set_parent(conveyor_storage *par) { - SAW_ASSERT(merger) { return; } - - SAW_ASSERT(child) { return; } - - parent_ = par; -} - -template -void merge_conveyor_node_data::attach(conveyor conv) { - auto nas = conveyor::from_conveyor(std::move(conv)); - SAW_ASSERT(nas) { return; } - conveyor_storage *storage = nas->next_storage(); - SAW_ASSERT(storage) { return; } - - auto merge_node_appendage = - heap::appendage>(std::move(nas), - *merger); - auto merge_node_appendage_ptr = merge_node_appendage.get(); - - storage->set_parent(merge_node_appendage.get()); - - SAW_ASSERT(merger) { return; } - - conveyor_storage *mrg_storage = merger->next_storage(); - SAW_ASSERT(mrg_storage) { return; } - - merge_node_appendage->set_parent(mrg_storage); - - appendages.push_back(std::move(merge_node_appendage)); - - /// @todo return this. necessary? maybe for the weird linking setup - /// maybe not - // return merge_node_appendage_ptr; -} - -template -void merge_conveyor_node_data::governing_node_destroyed() { - appendages.clear(); - merger = nullptr; -} - -template adapt_conveyor_feeder::~adapt_conveyor_feeder() { - if (feedee_) { - feedee_->set_feeder(nullptr); - feedee_ = nullptr; - } -} - -template -void adapt_conveyor_feeder::set_feedee(adapt_conveyor_node *feedee_p) { - feedee_ = feedee_p; -} - -template void adapt_conveyor_feeder::feed(T &&value) { - if (feedee_) { - feedee_->feed(std::move(value)); - } -} - -template void adapt_conveyor_feeder::fail(error &&error) { - if (feedee_) { - feedee_->fail(std::move(error)); - } -} - -template size_t adapt_conveyor_feeder::queued() const { - if (feedee_) { - return feedee_->queued(); - } - return 0; -} - -template size_t adapt_conveyor_feeder::space() const { - if (feedee_) { - return feedee_->space(); - } - return 0; -} - -template -error adapt_conveyor_feeder::swap(conveyor &&conv) noexcept { - SAW_ASSERT(feedee_) { return make_error("No feedee connected"); } - - auto node = conveyor::from_conveyor(std::move(conv)); - - feedee_->swap_child(std::move(node)); - - return no_error(); -} - -template -adapt_conveyor_node::adapt_conveyor_node() : conveyor_event_storage{} {} - -template adapt_conveyor_node::~adapt_conveyor_node() { - if (feeder_) { - feeder_->set_feedee(nullptr); - feeder_ = nullptr; - } -} - -template -error_or> -adapt_conveyor_node::swap_child(own &&swapee) noexcept { - // This should return the owning pointer of this instance - auto myself_err = parent_node_.swap_child_of_parent(std::move(swapee)); - - if (myself_err.is_error()) { - return myself_err; - } - - auto &myself = myself_err.get_value(); - - assert(myself.get() == this); - - return myself_err; -} - -template -conveyor_storage *adapt_conveyor_node::next_storage() noexcept { - return static_cast(this); -} - -template -void adapt_conveyor_node::notify_parent_attached( - conveyor_node &par) noexcept { - parent_node_.change_parent(&par); -} - -template -void adapt_conveyor_node::set_feeder(adapt_conveyor_feeder *feeder_p) { - feeder_ = feeder_p; -} - -template void adapt_conveyor_node::feed(T &&value) { - storage_.push(std::move(value)); - arm_next(); -} - -template void adapt_conveyor_node::fail(error &&error) { - storage_.push(std::move(error)); - arm_next(); -} - -template size_t adapt_conveyor_node::queued() const { - return storage_.size(); -} - -template size_t adapt_conveyor_node::space() const { - return std::numeric_limits::max() - storage_.size(); -} - -template -void adapt_conveyor_node::get_result(error_or_value &err_or_val) { - if (!storage_.empty()) { - err_or_val.as() = std::move(storage_.front()); - storage_.pop(); - } else { - err_or_val.as() = make_error( - "Signal for retrieval of storage sent even though no " - "data is present"); - } -} - -template void adapt_conveyor_node::child_has_fired() { - // Adapt node has no children - assert(false); -} - -template void adapt_conveyor_node::parent_has_fired() { - SAW_ASSERT(parent_) { return; } - - if (parent_->space() == 0) { - return; - } -} - -template void adapt_conveyor_node::fire() { - if (parent_) { - parent_->child_has_fired(); - - if (storage_.size() > 0) { - arm_later(); - } - } -} - -template one_time_conveyor_feeder::~one_time_conveyor_feeder() { - if (feedee_) { - feedee_->set_feeder(nullptr); - feedee_ = nullptr; - } -} - -template -void one_time_conveyor_feeder::set_feedee( - one_time_conveyor_node *feedee_p) { - feedee_ = feedee_p; -} - -template void one_time_conveyor_feeder::feed(T &&value) { - if (feedee_) { - feedee_->feed(std::move(value)); - } -} - -template void one_time_conveyor_feeder::fail(error &&error) { - if (feedee_) { - feedee_->fail(std::move(error)); - } -} - -template size_t one_time_conveyor_feeder::queued() const { - if (feedee_) { - return feedee_->queued(); - } - return 0; -} - -template size_t one_time_conveyor_feeder::space() const { - if (feedee_) { - return feedee_->space(); - } - return 0; -} - -template one_time_conveyor_node::~one_time_conveyor_node() { - if (feeder_) { - feeder_->set_feedee(nullptr); - feeder_ = nullptr; - } -} - -template -void one_time_conveyor_node::set_feeder( - one_time_conveyor_feeder *feeder_p) { - feeder_ = feeder_p; -} - -template void one_time_conveyor_node::feed(T &&value) { - storage_ = std::move(value); - arm_next(); -} - -template void one_time_conveyor_node::fail(error &&error) { - storage_ = std::move(error); - arm_next(); -} - -template size_t one_time_conveyor_node::queued() const { - return storage_.has_value() ? 1 : 0; -} - -template size_t one_time_conveyor_node::space() const { - return passed_ ? 0 : 1; -} - -template -void one_time_conveyor_node::get_result(error_or_value &err_or_val) { - if (storage_.has_value()) { - err_or_val.as() = std::move(storage_.value()); - storage_ = std::nullopt; - } else { - err_or_val.as() = make_error( - "Signal for retrieval of storage sent even though no " - "data is present"); - } -} - -template void one_time_conveyor_node::fire() { - if (parent_) { - parent_->child_has_fired(); - } -} - -} // namespace saw diff --git a/forstio/codec-json/.nix/derivation.nix b/forstio/codec-json/.nix/derivation.nix deleted file mode 100644 index fcc276d..0000000 --- a/forstio/codec-json/.nix/derivation.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ lib -, stdenvNoCC -, scons -, clang -, clang-tools -, version -, forstio -, gnutls -}: - -let - -in stdenvNoCC.mkDerivation { - pname = "forstio-codec-json"; - inherit version; - - src = ./..; - - enableParallelBuilding = true; - - nativeBuildInputs = [ - scons - clang - clang-tools - ]; - - buildInputs = [ - forstio.core - forstio.async - forstio.codec - ]; - - outputs = ["out" "dev"]; -} diff --git a/forstio/codec-json/SConscript b/forstio/codec-json/SConscript deleted file mode 100644 index 772ac0b..0000000 --- a/forstio/codec-json/SConscript +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/false - -import os -import os.path -import glob - - -Import('env') - -dir_path = Dir('.').abspath - -# Environment for base library -codec_json_env = env.Clone(); - -codec_json_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) -codec_json_env.headers = sorted(glob.glob(dir_path + "/*.h")) - -env.sources += codec_json_env.sources; -env.headers += codec_json_env.headers; - -## Shared lib -objects_shared = [] -codec_json_env.add_source_files(objects_shared, codec_json_env.sources, shared=True); -codec_json_env.library_shared = codec_json_env.SharedLibrary('#build/forstio-codec-json', [objects_shared]); - -## Static lib -objects_static = [] -codec_json_env.add_source_files(objects_static, codec_json_env.sources, shared=False); -codec_json_env.library_static = codec_json_env.StaticLibrary('#build/forstio-codec-json', [objects_static]); - -# Set Alias -env.Alias('library_codec_json', [codec_json_env.library_shared, codec_json_env.library_static]); - -env.targets += ['library_codec_json']; - -# Install -env.Install('$prefix/lib/', [codec_json_env.library_shared, codec_json_env.library_static]); -env.Install('$prefix/include/forstio/codec/json/', [codec_json_env.headers]); diff --git a/forstio/codec-json/SConstruct b/forstio/codec-json/SConstruct deleted file mode 100644 index edd5f57..0000000 --- a/forstio/codec-json/SConstruct +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import os.path -import glob -import re - - -if sys.version_info < (3,): - def isbasestring(s): - return isinstance(s,basestring) -else: - def isbasestring(s): - return isinstance(s, (str,bytes)) - -def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): - - if isbasestring(filetype): - dir_path = self.Dir('.').abspath - filetype = sorted(glob.glob(dir_path+"/"+filetype)) - - for path in filetype: - target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) - if shared: - target_name+='.os' - sources.append( self.SharedObject( target=target_name, source=path ) ) - else: - target_name+='.o' - sources.append( self.StaticObject( target=target_name, source=path ) ) - pass - -def isAbsolutePath(key, dirname, env): - assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) - -env_vars = Variables( - args=ARGUMENTS -) - -env_vars.Add('prefix', - help='Installation target location of build results and headers', - default='/usr/local/', - validator=isAbsolutePath -) - -env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], - CPPDEFINES=['SAW_UNIX'], - CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], - LIBS=['forstio-codec']) -env.__class__.add_source_files = add_kel_source_files -env.Tool('compilation_db'); -env.cdb = env.CompilationDatabase('compile_commands.json'); - -env.objects = []; -env.sources = []; -env.headers = []; -env.targets = []; - -Export('env') -SConscript('SConscript') - -env.Alias('cdb', env.cdb); -env.Alias('all', [env.targets]); -env.Default('all'); - -env.Alias('install', '$prefix') diff --git a/forstio/codec/.nix/derivation.nix b/forstio/codec/.nix/derivation.nix deleted file mode 100644 index c9fac2e..0000000 --- a/forstio/codec/.nix/derivation.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ lib -, stdenvNoCC -, scons -, clang -, clang-tools -, version -, forstio -}: - -let - -in stdenvNoCC.mkDerivation { - pname = "forstio-codec"; - inherit version; - - src = ./..; - - enableParallelBuilding = true; - - buildInputs = [ - forstio.core - ]; - - nativeBuildInputs = [ - scons - clang - clang-tools - ]; - - outputs = ["out" "dev"]; -} diff --git a/forstio/codec/SConscript b/forstio/codec/SConscript deleted file mode 100644 index c038d42..0000000 --- a/forstio/codec/SConscript +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/false - -import os -import os.path -import glob - - -Import('env') - -dir_path = Dir('.').abspath - -# Environment for base library -codec_env = env.Clone(); - -codec_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) -codec_env.headers = sorted(glob.glob(dir_path + "/*.h")) - -env.sources += codec_env.sources; -env.headers += codec_env.headers; - -## Shared lib -objects_shared = [] -codec_env.add_source_files(objects_shared, codec_env.sources, shared=True); -codec_env.library_shared = codec_env.SharedLibrary('#build/forstio-codec', [objects_shared]); - -## Static lib -objects_static = [] -codec_env.add_source_files(objects_static, codec_env.sources, shared=False); -codec_env.library_static = codec_env.StaticLibrary('#build/forstio-codec', [objects_static]); - -# Set Alias -env.Alias('library_codec', [codec_env.library_shared, codec_env.library_static]); - -env.targets += ['library_codec']; - -# Install -env.Install('$prefix/lib/', [codec_env.library_shared, codec_env.library_static]); -env.Install('$prefix/include/forstio/codec/', [codec_env.headers]); diff --git a/forstio/codec/SConstruct b/forstio/codec/SConstruct deleted file mode 100644 index 0d7b7c6..0000000 --- a/forstio/codec/SConstruct +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import os.path -import glob -import re - - -if sys.version_info < (3,): - def isbasestring(s): - return isinstance(s,basestring) -else: - def isbasestring(s): - return isinstance(s, (str,bytes)) - -def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): - - if isbasestring(filetype): - dir_path = self.Dir('.').abspath - filetype = sorted(glob.glob(dir_path+"/"+filetype)) - - for path in filetype: - target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) - if shared: - target_name+='.os' - sources.append( self.SharedObject( target=target_name, source=path ) ) - else: - target_name+='.o' - sources.append( self.StaticObject( target=target_name, source=path ) ) - pass - -def isAbsolutePath(key, dirname, env): - assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) - -env_vars = Variables( - args=ARGUMENTS -) - -env_vars.Add('prefix', - help='Installation target location of build results and headers', - default='/usr/local/', - validator=isAbsolutePath -) - -env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], - CPPDEFINES=['SAW_UNIX'], - CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], - LIBS=['forstio-core']) -env.__class__.add_source_files = add_kel_source_files -env.Tool('compilation_db'); -env.cdb = env.CompilationDatabase('compile_commands.json'); - -env.objects = []; -env.sources = []; -env.headers = []; -env.targets = []; - -Export('env') -SConscript('SConscript') - -env.Alias('cdb', env.cdb); -env.Alias('all', [env.targets]); -env.Default('all'); - -env.Alias('install', '$prefix') diff --git a/forstio/codec/data.h b/forstio/codec/data.h deleted file mode 100644 index 1682ae7..0000000 --- a/forstio/codec/data.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include "schema.h" - -namespace saw { -namespace encode { -struct Native {}; -} -/* - * Helper for the basic message container, so the class doesn't have to be - * specialized 10 times. - */ -template struct native_data_type; - -template <> -struct native_data_type> { - using type = int8_t; -}; - -template <> -struct native_data_type> { - using type = int16_t; -}; - -template <> -struct native_data_type> { - using type = int32_t; -}; - -template <> -struct native_data_type> { - using type = int64_t; -}; - -template <> -struct native_data_type> { - using type = uint8_t; -}; - -template <> -struct native_data_type> { - using type = uint16_t; -}; - -template <> -struct native_data_type> { - using type = uint32_t; -}; - -template <> -struct native_data_type> { - using type = uint64_t; -}; - -template <> -struct native_data_type> { - using type = float; -}; - -template -class data { -private: - static_assert(always_false, "Type not supported"); -}; - -template<> -class data { -private: - std::string value_; -public: - SAW_FORBID_COPY(data); - - data(std::string&& value__):value_{std::move(value__)}{} - - std::size_t size() const { - return value_.size(); - } - - bool operator==(const data& data){ - return value_ == data.value_; - } -}; - -template -class data, encode::Native> { -private: -}; -} diff --git a/forstio/codec/proto_kel.h b/forstio/codec/proto_kel.h deleted file mode 100644 index 3b4ebac..0000000 --- a/forstio/codec/proto_kel.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "data.h" - -#include - -namespace saw { -namespace encode { -struct ProtoKel {}; -} - -template -class data { -private: - own buffer_; -public: - data(own&& buffer__):buffer_{std::move(buffer__)}{} - - buffer& get_buffer(){ - return *buffer_; - } - - const buffer& get_buffer() const { - return *buffer_; - } -}; - -template -class codec { -private: -public: - error_or> decode(const data& encoded){ - return make_error(); - } - - error_or> encode(const data& native){ - return make_error(); - } -}; -} -} diff --git a/forstio/codec/schema.h b/forstio/codec/schema.h deleted file mode 100644 index b23aaa1..0000000 --- a/forstio/codec/schema.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include -#include - -namespace saw { -namespace schema { -// NOLINTBEGIN -template struct NamedMember {}; - -template struct Struct { - static_assert( - always_false, - "This schema template doesn't support this type of template argument"); -}; - -template -struct Struct...> {}; - -template struct Union { - static_assert( - always_false, - "This schema template doesn't support this type of template argument"); -}; - -template -struct Union...> {}; - -template struct Array {}; - -template FixedArray {}; - -template struct Tuple {}; - -struct String {}; - -struct SignedInteger {}; -struct UnsignedInteger {}; -struct FloatingPoint {}; - -template struct Primitive { - static_assert(((std::is_same_v || - std::is_same_v)&&(N == 1 || N == 2 || - N == 4 || N == 8)) || - (std::is_same_v && (N == 4 || N == 8)), - "Primitive Type is not supported"); -}; - -using Int8 = Primitive; -using Int16 = Primitive; -using Int32 = Primitive; -using Int64 = Primitive; - -using UInt8 = Primitive; -using UInt16 = Primitive; -using UInt32 = Primitive; -using UInt64 = Primitive; - -using Float32 = Primitive; -using Float64 = Primitive; - -/** - * Classes enabling Rpc calls - */ -template -struct Function {}; - -template struct Interface { - static_assert( - always_false, - "This schema template doesn't support this type of template argument"); -}; - -template -struct Interface...> {}; - -// NOLINTEND -} // namespace schema -} // namespace saw diff --git a/forstio/core/.nix/derivation.nix b/forstio/core/.nix/derivation.nix deleted file mode 100644 index adf0cb4..0000000 --- a/forstio/core/.nix/derivation.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ lib -, stdenvNoCC -, scons -, clang -, clang-tools -, version -}: - -let - -in stdenvNoCC.mkDerivation { - pname = "forstio-core"; - inherit version; - - src = ./..; - - enableParallelBuilding = true; - - nativeBuildInputs = [ - scons - clang - clang-tools - ]; - - outputs = ["out" "dev"]; -} diff --git a/forstio/core/SConscript b/forstio/core/SConscript deleted file mode 100644 index 04eb4c3..0000000 --- a/forstio/core/SConscript +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/false - -import os -import os.path -import glob - - -Import('env') - -dir_path = Dir('.').abspath - -# Environment for base library -core_env = env.Clone(); - -core_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) -core_env.headers = sorted(glob.glob(dir_path + "/*.h")) - -env.sources += core_env.sources; -env.headers += core_env.headers; - -## Shared lib -objects_shared = [] -core_env.add_source_files(objects_shared, core_env.sources, shared=True); -core_env.library_shared = core_env.SharedLibrary('#build/forstio-core', [objects_shared]); - -## Static lib -objects_static = [] -core_env.add_source_files(objects_static, core_env.sources, shared=False); -core_env.library_static = core_env.StaticLibrary('#build/forstio-core', [objects_static]); - -# Set Alias -env.Alias('library_core', [core_env.library_shared, core_env.library_static]); - -env.targets += ['library_core']; - -# Install -env.Install('$prefix/lib/', [core_env.library_shared, core_env.library_static]); -env.Install('$prefix/include/forstio/core/', [core_env.headers]); diff --git a/forstio/core/SConstruct b/forstio/core/SConstruct deleted file mode 100644 index 865d131..0000000 --- a/forstio/core/SConstruct +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import os.path -import glob -import re - - -if sys.version_info < (3,): - def isbasestring(s): - return isinstance(s,basestring) -else: - def isbasestring(s): - return isinstance(s, (str,bytes)) - -def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): - - if isbasestring(filetype): - dir_path = self.Dir('.').abspath - filetype = sorted(glob.glob(dir_path+"/"+filetype)) - - for path in filetype: - target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) - if shared: - target_name+='.os' - sources.append( self.SharedObject( target=target_name, source=path ) ) - else: - target_name+='.o' - sources.append( self.StaticObject( target=target_name, source=path ) ) - pass - -def isAbsolutePath(key, dirname, env): - assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) - -env_vars = Variables( - args=ARGUMENTS -) - -env_vars.Add('prefix', - help='Installation target location of build results and headers', - default='/usr/local/', - validator=isAbsolutePath -) - -env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], - CPPDEFINES=['SAW_UNIX'], - CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], - LIBS=[]) -env.__class__.add_source_files = add_kel_source_files -env.Tool('compilation_db'); -env.cdb = env.CompilationDatabase('compile_commands.json'); - -env.objects = []; -env.sources = []; -env.headers = []; -env.targets = []; - -Export('env') -SConscript('SConscript') - -env.Alias('cdb', env.cdb); -env.Alias('all', [env.targets]); -env.Default('all'); - -env.Alias('install', '$prefix') diff --git a/forstio/core/buffer.cpp b/forstio/core/buffer.cpp deleted file mode 100644 index ad471d7..0000000 --- a/forstio/core/buffer.cpp +++ /dev/null @@ -1,434 +0,0 @@ -#include "buffer.h" - -#include -#include -#include -#include -#include - -namespace saw { -error buffer::push(const uint8_t &value) { - size_t write_remain = write_composite_length(); - if (write_remain > 0) { - write() = value; - write_advance(1); - } else { - return make_error(); - } - return no_error(); -} - -error buffer::push(const uint8_t &buffer, size_t size) { - error error = write_require_length(size); - if (error.failed()) { - return error; - } - const uint8_t *buffer_ptr = &buffer; - while (size > 0) { - size_t segment = std::min(write_segment_length(), size); - memcpy(&write(), buffer_ptr, segment); - write_advance(segment); - size -= segment; - buffer_ptr += segment; - } - return no_error(); -} - -error buffer::pop(uint8_t &value) { - if (read_composite_length() > 0) { - value = read(); - read_advance(1); - } else { - return make_error(); - } - return no_error(); -} - -error buffer::pop(uint8_t &buffer, size_t size) { - if (read_composite_length() >= size) { - uint8_t *buffer_ptr = &buffer; - while (size > 0) { - size_t segment = std::min(read_segment_length(), size); - memcpy(buffer_ptr, &read(), segment); - read_advance(segment); - size -= segment; - buffer_ptr += segment; - } - } else { - return make_error(); - } - return no_error(); -} - -std::string buffer::to_string() const { - std::ostringstream oss; - for (size_t i = 0; i < read_composite_length(); ++i) { - oss << read(i); - } - return oss.str(); -} - -std::string buffer::to_hex() const { - std::ostringstream oss; - oss << std::hex << std::setfill('0'); - for (size_t i = 0; i < read_composite_length(); ++i) { - oss << std::setw(2) << (uint16_t)read(i); - if ((i + 1) < read_composite_length()) { - oss << ((i % 4 == 3) ? '\n' : ' '); - } - } - return oss.str(); -} - -buffer_view::buffer_view(buffer &buffer) - : buffer_{buffer}, read_offset_{0}, write_offset_{0} {} - -size_t buffer_view::read_position() const { - return read_offset_ + buffer_.read_position(); -} - -size_t buffer_view::read_composite_length() const { - assert(read_offset_ <= buffer_.read_composite_length()); - if (read_offset_ > buffer_.read_composite_length()) { - return 0; - } - - return buffer_.read_composite_length() - read_offset_; -} - -size_t buffer_view::read_segment_length(size_t offset) const { - size_t off = offset + read_offset_; - assert(off <= buffer_.read_composite_length()); - if (off > buffer_.read_composite_length()) { - return 0; - } - - return buffer_.read_segment_length(off); -} - -void buffer_view::read_advance(size_t bytes) { - size_t offset = bytes + read_offset_; - assert(offset <= buffer_.read_composite_length()); - if (offset > buffer_.read_composite_length()) { - read_offset_ += buffer_.read_composite_length(); - return; - } - - read_offset_ += bytes; -} - -uint8_t &buffer_view::read(size_t i) { - size_t pos = i + read_offset_; - - assert(pos < buffer_.read_composite_length()); - - return buffer_.read(pos); -} - -const uint8_t &buffer_view::read(size_t i) const { - size_t pos = i + read_offset_; - - assert(pos < buffer_.read_composite_length()); - - return buffer_.read(pos); -} - -size_t buffer_view::write_position() const { - return write_offset_ + buffer_.write_position(); -} - -size_t buffer_view::write_composite_length() const { - assert(write_offset_ <= buffer_.write_composite_length()); - if (write_offset_ > buffer_.write_composite_length()) { - return 0; - } - - return buffer_.write_composite_length() - write_offset_; -} - -size_t buffer_view::write_segment_length(size_t offset) const { - size_t off = offset + write_offset_; - assert(off <= buffer_.write_composite_length()); - if (off > buffer_.write_composite_length()) { - return 0; - } - - return buffer_.write_segment_length(off); -} - -void buffer_view::write_advance(size_t bytes) { - size_t offset = bytes + write_offset_; - assert(offset <= buffer_.write_composite_length()); - if (offset > buffer_.write_composite_length()) { - write_offset_ += buffer_.write_composite_length(); - return; - } - - write_offset_ += bytes; -} - -uint8_t &buffer_view::write(size_t i) { - size_t pos = i + write_offset_; - - assert(pos < buffer_.write_composite_length()); - - return buffer_.write(pos); -} - -const uint8_t &buffer_view::write(size_t i) const { - size_t pos = i + write_offset_; - - assert(pos < buffer_.write_composite_length()); - - return buffer_.write(pos); -} - -error buffer_view::write_require_length(size_t bytes) { - return buffer_.write_require_length(bytes + write_offset_); -} - -size_t buffer_view::read_offset() const { return read_offset_; } - -size_t buffer_view::write_offset() const { return write_offset_; } - -ring_buffer::ring_buffer() : read_position_{0}, write_position_{0} { - buffer_.resize(RING_BUFFER_MAX_SIZE); -} - -ring_buffer::ring_buffer(size_t size) : read_position_{0}, write_position_{0} { - buffer_.resize(size); -} - -size_t ring_buffer::read_position() const { return read_position_; } - -/* - * If write is ahead of read it is a simple distance, but if read ist ahead of - * write then there are two segments - * - */ -size_t ring_buffer::read_composite_length() const { - return write_position() < read_position() - ? buffer_.size() - (read_position() - write_position()) - : (write_reached_read_ ? buffer_.size() - : write_position() - read_position()); -} - -/* - * If write is ahead then it's the simple distance again. If read is ahead it's - * until the end of the buffer/segment - */ -size_t ring_buffer::read_segment_length(size_t offset) const { - size_t read_composite = read_composite_length(); - assert(offset <= read_composite); - offset = std::min(offset, read_composite); - size_t remaining = read_composite - offset; - - size_t read_offset = read_position() + offset; - read_offset = read_offset >= buffer_.size() ? read_offset - buffer_.size() - : read_offset; - - // case 1 write is located before read and reached read - // then offset can be used normally - // case 2 write is located at read, but read reached write - // then it is set to zero by readCompositeLength() - // case 3 write is located after read - // since std::min you can use simple subtraction - if (write_position() < read_offset) { - return buffer_.size() - read_offset; - } - - if (write_position() == read_offset) { - if (remaining > 0) { - return buffer_.size() - read_offset; - } else { - return 0; - } - } - - return write_position() - read_offset; -} - -void ring_buffer::read_advance(size_t bytes) { - size_t read_composite = read_composite_length(); - - assert(bytes <= read_composite); - bytes = std::min(bytes, read_composite); - size_t advanced = read_position_ + bytes; - read_position_ = advanced >= buffer_.size() ? advanced - buffer_.size() - : advanced; - write_reached_read_ = bytes > 0 ? false : write_reached_read_; -} - -uint8_t &ring_buffer::read(size_t i) { - assert(i < read_composite_length()); - size_t pos = read_position_ + i; - pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; - return buffer_[pos]; -} - -const uint8_t &ring_buffer::read(size_t i) const { - assert(i < read_composite_length()); - size_t pos = read_position_ + i; - pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; - return buffer_[pos]; -} - -size_t ring_buffer::write_position() const { return write_position_; } - -size_t ring_buffer::write_composite_length() const { - return read_position() > write_position() - ? (read_position() - write_position()) - : (write_reached_read_ - ? 0 - : buffer_.size() - (write_position() - read_position())); -} - -size_t ring_buffer::write_segment_length(size_t offset) const { - size_t write_composite = write_composite_length(); - assert(offset <= write_composite); - offset = std::min(offset, write_composite); - - size_t write_offset = write_position() + offset; - write_offset = write_offset >= buffer_.size() - ? write_offset - buffer_.size() - : write_offset; - - if (read_position_ > write_offset) { - return read_position_ - write_offset; - } - - if (write_reached_read_) { - return 0; - } - - return buffer_.size() - write_offset; -} - -void ring_buffer::write_advance(size_t bytes) { - assert(bytes <= write_composite_length()); - size_t advanced = write_position_ + bytes; - write_position_ = advanced >= buffer_.size() ? advanced - buffer_.size() - : advanced; - - write_reached_read_ = - (write_position_ == read_position_ && bytes > 0 ? true : false); -} - -uint8_t &ring_buffer::write(size_t i) { - assert(i < write_composite_length()); - size_t pos = write_position_ + i; - pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; - return buffer_[pos]; -} - -const uint8_t &ring_buffer::write(size_t i) const { - assert(i < write_composite_length()); - size_t pos = write_position_ + i; - pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; - return buffer_[pos]; -} -/* - Error RingBuffer::increaseSize(size_t size){ - size_t old_size = buffer.size(); - size_t new_size = old_size + size; - buffer.resize(new_size); - if(readPosition() > writePosition() || (readPosition() == - writePosition() && write_reached_read)){ size_t remaining = old_size - - writePosition(); size_t real_remaining = 0; while(remaining > 0){ size_t - segment = std::min(remaining, size); memcpy(&buffer[new_size-segment], - &buffer[old_size-segment], segment); remaining -= segment; size -= segment; - old_size -= segment; - new_size -= segment; - } - } - - return noError(); - } -*/ -error ring_buffer::write_require_length(size_t bytes) { - size_t write_remain = write_composite_length(); - if (bytes > write_remain) { - return make_error(); - } - return no_error(); -} - -array_buffer::array_buffer(size_t size) - : read_position_{0}, write_position_{0} { - buffer_.resize(size); -} - -size_t array_buffer::read_position() const { return read_position_; } - -size_t array_buffer::read_composite_length() const { - return write_position_ - read_position_; -} - -size_t array_buffer::read_segment_length(size_t offset) const { - size_t read_composite = read_composite_length(); - assert(offset <= read_composite); - - offset = std::min(read_composite, offset); - size_t read_offset = read_position_ + offset; - - return write_position_ - read_offset; -} - -void array_buffer::read_advance(size_t bytes) { - assert(bytes <= read_composite_length()); - read_position_ += bytes; -} - -uint8_t &array_buffer::read(size_t i) { - assert(i < read_composite_length()); - - return buffer_[i + read_position_]; -} - -const uint8_t &array_buffer::read(size_t i) const { - assert(i + read_position_ < buffer_.size()); - - return buffer_[i + read_position_]; -} - -size_t array_buffer::write_position() const { return write_position_; } - -size_t array_buffer::write_composite_length() const { - assert(write_position_ <= buffer_.size()); - return buffer_.size() - write_position_; -} - -size_t array_buffer::write_segment_length(size_t offset) const { - assert(write_position_ <= buffer_.size()); - size_t write_composite = write_composite_length(); - - assert(offset <= write_composite); - offset = std::min(write_composite, offset); - size_t write_offset = write_position_ + offset; - - return buffer_.size() - write_offset; -} - -void array_buffer::write_advance(size_t bytes) { - assert(bytes <= write_composite_length()); - write_position_ += bytes; -} - -uint8_t &array_buffer::write(size_t i) { - assert(i < write_composite_length()); - return buffer_[i + write_position_]; -} - -const uint8_t &array_buffer::write(size_t i) const { - assert(i < write_composite_length()); - return buffer_[i + write_position_]; -} -error array_buffer::write_require_length(size_t bytes) { - size_t write_remain = write_composite_length(); - if (bytes > write_remain) { - return make_error(); - } - return no_error(); -} - -} // namespace saw diff --git a/forstio/core/buffer.h b/forstio/core/buffer.h deleted file mode 100644 index 4485ff1..0000000 --- a/forstio/core/buffer.h +++ /dev/null @@ -1,195 +0,0 @@ -#pragma once - -#include "error.h" - -#include -#include -#include -#include -#include -#include - -namespace saw { -/* - * Access class to reduce templated BufferSegments bloat - */ -class buffer { -protected: - ~buffer() = default; - -public: - virtual size_t read_position() const = 0; - virtual size_t read_composite_length() const = 0; - virtual size_t read_segment_length(size_t offset = 0) const = 0; - virtual void read_advance(size_t bytes) = 0; - - virtual uint8_t &read(size_t i = 0) = 0; - virtual const uint8_t &read(size_t i = 0) const = 0; - - virtual size_t write_position() const = 0; - virtual size_t write_composite_length() const = 0; - virtual size_t write_segment_length(size_t offset = 0) const = 0; - virtual void write_advance(size_t bytes) = 0; - - virtual uint8_t &write(size_t i = 0) = 0; - virtual const uint8_t &write(size_t i = 0) const = 0; - - /* - * Sometime buffers need to grow with a little more control - * than with push and pop for more efficient calls. - * There is nothing you can do if read hasn't been filled, but at - * least write can be increased if it is demanded. - */ - virtual error write_require_length(size_t bytes) = 0; - - error push(const uint8_t &value); - error push(const uint8_t &buffer, size_t size); - error pop(uint8_t &value); - error pop(uint8_t &buffer, size_t size); - - /* - * Subject to change - */ - std::string to_string() const; - std::string to_hex() const; -}; - -/* - * A viewer class for buffers. - * Working on the reference buffer invalidates the buffer view - */ -class buffer_view : public buffer { -private: - buffer &buffer_; - size_t read_offset_; - size_t write_offset_; - -public: - buffer_view(buffer &); - - size_t read_position() const override; - size_t read_composite_length() const override; - size_t read_segment_length(size_t offset = 0) const override; - void read_advance(size_t bytes) override; - - uint8_t &read(size_t i = 0) override; - const uint8_t &read(size_t i = 0) const override; - - size_t write_position() const override; - size_t write_composite_length() const override; - size_t write_segment_length(size_t offset = 0) const override; - void write_advance(size_t bytes) override; - - uint8_t &write(size_t i = 0) override; - const uint8_t &write(size_t i = 0) const override; - - error write_require_length(size_t bytes) override; - - size_t read_offset() const; - size_t write_offset() const; -}; - -/* - * Buffer size meant for default allocation size of the ringbuffer since - * this class currently doesn't support proper resizing - */ -constexpr size_t RING_BUFFER_MAX_SIZE = 4096; -/* - * Buffer wrapping around if read caught up - */ -class ring_buffer final : public buffer { -private: - std::vector buffer_; - size_t read_position_; - size_t write_position_; - bool write_reached_read_ = false; - -public: - ring_buffer(); - ring_buffer(size_t size); - - inline size_t size() const { return buffer_.size(); } - - inline uint8_t &operator[](size_t i) { return buffer_[i]; } - inline const uint8_t &operator[](size_t i) const { return buffer_[i]; } - - size_t read_position() const override; - size_t read_composite_length() const override; - size_t read_segment_length(size_t offset = 0) const override; - void read_advance(size_t bytes) override; - - uint8_t &read(size_t i = 0) override; - const uint8_t &read(size_t i = 0) const override; - - size_t write_position() const override; - size_t write_composite_length() const override; - size_t write_segment_length(size_t offset = 0) const override; - void write_advance(size_t bytes) override; - - uint8_t &write(size_t i = 0) override; - const uint8_t &write(size_t i = 0) const override; - - error write_require_length(size_t bytes) override; -}; - -/* - * One time buffer - */ -class array_buffer : public buffer { -private: - std::vector buffer_; - - size_t read_position_; - size_t write_position_; - -public: - array_buffer(size_t size); - - size_t read_position() const override; - size_t read_composite_length() const override; - size_t read_segment_length(size_t offset = 0) const override; - void read_advance(size_t bytes) override; - - uint8_t &read(size_t i = 0) override; - const uint8_t &read(size_t i = 0) const override; - - size_t write_position() const override; - size_t write_composite_length() const override; - size_t write_segment_length(size_t offset = 0) const override; - void write_advance(size_t bytes) override; - - uint8_t &write(size_t i = 0) override; - const uint8_t &write(size_t i = 0) const override; - - error write_require_length(size_t bytes) override; -}; - -class chain_array_buffer : public buffer { -private: - std::deque buffer_; - - size_t read_position_; - size_t write_position_; - -public: - chain_array_buffer(); - - size_t read_position() const override; - size_t read_composite_length() const override; - size_t read_segment_length(size_t offset = 0) const override; - void read_advance(size_t bytes) override; - - uint8_t &read(size_t i = 0) override; - const uint8_t &read(size_t i = 0) const override; - - size_t write_position() const override; - size_t write_composite_length() const override; - size_t write_segment_length(size_t offset = 0) const override; - void write_advance(size_t bytes) override; - - uint8_t &write(size_t i = 0) override; - const uint8_t &write(size_t i = 0) const override; - - error write_require_length(size_t bytes) override; -}; -} // namespace saw diff --git a/forstio/core/common.h b/forstio/core/common.h deleted file mode 100644 index a06c238..0000000 --- a/forstio/core/common.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace saw { - -#define SAW_CONCAT_(x, y) x##y -#define SAW_CONCAT(x, y) SAW_CONCAT_(x, y) -#define SAW_UNIQUE_NAME(prefix) SAW_CONCAT(prefix, __LINE__) - -#define SAW_FORBID_COPY(classname) \ - classname(const classname &) = delete; \ - classname &operator=(const classname &) = delete - -#define SAW_FORBID_MOVE(classname) \ - classname(classname &&) = delete; \ - classname &operator=(classname &&) = delete - -#define SAW_DEFAULT_COPY(classname) \ - classname(const classname &) = default; \ - classname &operator=(const classname &) = default - -#define SAW_DEFAULT_MOVE(classname) \ - classname(classname &&) = default; \ - classname &operator=(classname &&) = default - -// In case of C++20 -#define SAW_ASSERT(expression) \ - assert(expression); \ - if (!(expression)) [[unlikely]] - -template using maybe = std::optional; - -template using own = std::unique_ptr; - -template using our = std::shared_ptr; - -template using lent = std::weak_ptr; - -template own heap(Args &&...args) { - return own(new T(std::forward(args)...)); -} - -template our share(Args &&...args) { - return std::make_shared(std::forward(args)...); -} - -template T instance() noexcept; - -template struct return_type_helper { - typedef decltype(instance()(instance())) Type; -}; -template struct return_type_helper { - typedef decltype(instance()()) Type; -}; - -template -using return_type = typename return_type_helper::Type; - -struct void_t {}; - -template struct void_fix { typedef T Type; }; -template <> struct void_fix { typedef void_t Type; }; -template using fix_void = typename void_fix::Type; - -template struct void_unfix { typedef T Type; }; -template <> struct void_unfix { typedef void Type; }; -template using unfix_void = typename void_unfix::Type; - -template constexpr bool always_false = false; - -} // namespace saw diff --git a/forstio/core/error.cpp b/forstio/core/error.cpp deleted file mode 100644 index 727ca95..0000000 --- a/forstio/core/error.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "error.h" - -namespace saw { -error::error(error::code code_, bool is_critical__) - : error_code_{static_cast(code_)}, is_critical_{is_critical__} {} - -error::error(error::code code_, bool is_critical__, const std::string_view &msg) - : - error_code_{static_cast(code_)} - , is_critical_{is_critical__}, error_message_{msg}{} - -error::error(error &&error) - : - error_code_{std::move(error.error_code_)} - , is_critical_{std::move(error.is_critical_)} - , error_message_{std::move(error.error_message_)}{} - -const std::string_view error::message() const { - - return std::visit( - [this](auto &&arg) -> const std::string_view { - using T = std::decay_t; - - if constexpr (std::is_same_v) { - return std::string_view{arg}; - } else if constexpr (std::is_same_v) { - return arg; - } else { - return "Error in class Error. Good luck :)"; - } - }, - error_message_); -} - -bool error::failed() const { - return this->is_error(); -} - -bool error::is_critical() const { - return is_critical_; -} - -bool error::is_recoverable() const { - return !is_critical_; -} - -error error::copy_error() const { - auto copy_error_code = error_code_; - error error{copy_error_code, is_critical_}; - - try { - error.error_message_ = error_message_; - } catch (const std::bad_alloc &) { - error.error_message_ = - std::string_view{"Error while copying Error string. Out of memory"}; - } - - return error; -} - -error::code error::get_id() const { return error_code_; } - -namespace impl { -error_registry& get_error_registry() { - static own reg = nullptr; - if(!reg){ - reg = heap(); - } - - assert(reg); - return *reg; -} -} - -error no_error(){ - return make_error(); -} - -namespace impl { -error_or error_registry::search_id(const std::string_view& desc)const{ - /** - * Search the index in the vector - */ - size_t i{}; - size_t info_max_size = std::min(infos.size(), std::numeric_limits::max()); - for(i = 0; i < info_max_size; ++i){ - if(infos.at(i).description == desc){ - break; - } - } - - if(i == info_max_size){ - return make_error(); - } - - return static_cast(i); -} - -error_or error_registry::search_or_register_id(const std::string_view& desc, bool is_critical){ - auto err_or_id = search_id(desc); - - if(err_or_id.is_value()){ - return err_or_id.get_value(); - } - - auto& err = err_or_id.get_error(); - - if(err.is_error()){ - size_t new_index = infos.size(); - if(new_index == std::numeric_limits::max()){ - return make_error("Error registry ids are exhausted"); - } - infos.emplace_back(error_info{desc, is_critical}); - return static_cast(new_index); - } - - return std::move(err); -} -} - -} // namespace saw diff --git a/forstio/core/error.h b/forstio/core/error.h deleted file mode 100644 index 3d242b9..0000000 --- a/forstio/core/error.h +++ /dev/null @@ -1,233 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -#include "common.h" - -namespace saw { -/** - * Utility class for generating errors. Has a base distinction between - * critical and recoverable errors. Additional code ids can be provided to the - * constructor if additional distinctions are necessary. - */ -class error { -public: - using code = uint32_t; -private: - code error_code_; - bool is_critical_; - std::variant error_message_; - -public: - error(error::code id, bool is_critical); - error(error::code id, bool is_critical, const std::string_view &msg); - error(error &&error); - - SAW_FORBID_COPY(error); - - error &operator=(error &&) = default; - - const std::string_view message() const; - bool failed() const; - - bool is_critical() const; - bool is_recoverable() const; - - /** - * Replaces the copy constructor. We need this since we want to explicitly copy and not implicitly - */ - error copy_error() const; - - code get_id() const; - - template - bool is_error() const; -}; - -template -class error_or; - -namespace impl { - -class error_registry { -private: - struct error_info { - error_info() = delete; - error_info(const std::string_view& d_, bool critical_):description{d_}, is_critical{critical_}{} - - std::string_view description; - bool is_critical; - }; - - std::vector infos; -public: - error_or search_id(const std::string_view& desc) const; - - error_or search_or_register_id(const std::string_view& desc, bool is_critical); -}; - -error_registry& get_error_registry(); - -template -error::code get_template_id(){ - static error::code id = std::numeric_limits::max(); - - if(id == std::numeric_limits::max()){ - auto& reg = get_error_registry(); - - auto err_or_id = reg.search_or_register_id(T::description, T::is_critical); - if(err_or_id.is_error()){ - return std::numeric_limits::max(); - } - - id = err_or_id.get_value(); - } - - return id; -} -} - -template error make_error(const std::string_view& generic){ - error::code id = impl::get_template_id(); - - return error{id, T::is_critical, generic}; -} - -template error make_error(){ - error::code id = impl::get_template_id(); - - return error{id, T::is_critical}; -} - -error make_error(error::code code, const std::string_view &generic); - - -namespace err { -struct no_error { - static constexpr std::string_view description = "No error has occured"; - static constexpr bool is_critical = false; -}; - -struct recoverable { - static constexpr std::string_view description = "No error has occured"; - static constexpr bool is_critical = false; -}; - -struct critical { - static constexpr std::string_view description = "No error has occured"; - static constexpr bool is_critical = true; -}; - -struct buffer_exhausted { - static constexpr std::string_view description = "Buffer is too small"; - static constexpr bool is_critical = false; -}; - -struct not_found { - static constexpr std::string_view description = "Not found"; - static constexpr bool is_critical = false; -}; - -struct out_of_memory { - static constexpr std::string_view description = "Out of memory"; - static constexpr bool is_critical = true; -}; - -struct invalid_state { - static constexpr std::string_view description = "Invalid state"; - static constexpr bool is_critical = true; -}; - -struct not_supported { - static constexpr std::string_view description = "Not supported"; - static constexpr bool is_critical = false; -}; - -struct not_implemented { - static constexpr std::string_view description = "Not implemented"; - static constexpr bool is_critical = true; -}; -} - -/** - * Shorthand for no error happened - */ -error no_error(); - -/** - * Exception alternative. Since I code without exceptions this class is - * essentially a kind of exception replacement. - */ -template class error_or; - -class error_or_value { -public: - virtual ~error_or_value() = default; - - template error_or> &as() { - return static_cast> &>(*this); - } - - template const error_or> &as() const { - return static_cast> &>(*this); - } -}; - -template class error_or final : public error_or_value { -private: - std::variant> value_or_error_; - - static_assert(!std::is_same_v, - "Don't use internal private types"); - -public: - error_or():value_or_error_{fix_void{}}{} - error_or(const fix_void &value) : value_or_error_{value} {} - - error_or(fix_void &&value) : value_or_error_{std::move(value)} {} - - error_or(const error &error) : value_or_error_{error} {} - error_or(error &&error) : value_or_error_{std::move(error)} {} - - bool is_value() const { - return std::holds_alternative>(value_or_error_); - } - - bool is_error() const { - return std::holds_alternative(value_or_error_); - } - - class error &get_error() { - return std::get(value_or_error_); - } - - const class error &get_error() const { - return std::get(value_or_error_); - } - - fix_void &get_value() { return std::get>(value_or_error_); } - - const fix_void &get_value() const { - return std::get>(value_or_error_); - } -}; - -template class error_or> { -private: - error_or() = delete; -}; - -template -bool error::is_error() const { - - return error_code_ == impl::get_template_id(); -} - -} // namespace saw diff --git a/forstio/core/string_literal.h b/forstio/core/string_literal.h deleted file mode 100644 index d530a54..0000000 --- a/forstio/core/string_literal.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - -namespace saw { -/** - * Helper object which creates a templated string from the provided string - * literal. It guarantees compile time uniqueness and thus allows using strings - * in template parameters. - */ -template class string_literal { -public: - constexpr string_literal(const CharT (&input)[N]) noexcept { - for (size_t i = 0; i < N; ++i) { - data[i] = input[i]; - } - } - - std::array data{}; - - constexpr std::string_view view() const noexcept { - return std::string_view{data.data()}; - } - - constexpr bool - operator==(const string_literal &) const noexcept = default; - - template - constexpr bool - operator==(const string_literal &) const noexcept { - return false; - } -}; - -template -constexpr string_literal operator""_key() { - return string_literal{Chars..., '\0'}; -} -} // namespace saw diff --git a/forstio/io-tls/.nix/derivation.nix b/forstio/io-tls/.nix/derivation.nix deleted file mode 100644 index 6c62b51..0000000 --- a/forstio/io-tls/.nix/derivation.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ lib -, stdenvNoCC -, scons -, clang -, clang-tools -, version -, forstio -, gnutls -}: - -let - -in stdenvNoCC.mkDerivation { - pname = "forstio-io-tls"; - inherit version; - - src = ./..; - - enableParallelBuilding = true; - - nativeBuildInputs = [ - scons - clang - clang-tools - ]; - - buildInputs = [ - forstio.core - forstio.async - forstio.io - gnutls - ]; - - outputs = ["out" "dev"]; -} diff --git a/forstio/io-tls/SConscript b/forstio/io-tls/SConscript deleted file mode 100644 index 4f88f37..0000000 --- a/forstio/io-tls/SConscript +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/false - -import os -import os.path -import glob - - -Import('env') - -dir_path = Dir('.').abspath - -# Environment for base library -io_tls_env = env.Clone(); - -io_tls_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) -io_tls_env.headers = sorted(glob.glob(dir_path + "/*.h")) - -env.sources += io_tls_env.sources; -env.headers += io_tls_env.headers; - -## Shared lib -objects_shared = [] -io_tls_env.add_source_files(objects_shared, io_tls_env.sources, shared=True); -io_tls_env.library_shared = io_tls_env.SharedLibrary('#build/forstio-io-tls', [objects_shared]); - -## Static lib -objects_static = [] -io_tls_env.add_source_files(objects_static, io_tls_env.sources, shared=False); -io_tls_env.library_static = io_tls_env.StaticLibrary('#build/forstio-io-tls', [objects_static]); - -# Set Alias -env.Alias('library_io_tls', [io_tls_env.library_shared, io_tls_env.library_static]); - -env.targets += ['library_io_tls']; - -# Install -env.Install('$prefix/lib/', [io_tls_env.library_shared, io_tls_env.library_static]); -env.Install('$prefix/include/forstio/io/tls/', [io_tls_env.headers]); diff --git a/forstio/io-tls/SConstruct b/forstio/io-tls/SConstruct deleted file mode 100644 index fbd8657..0000000 --- a/forstio/io-tls/SConstruct +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import os.path -import glob -import re - - -if sys.version_info < (3,): - def isbasestring(s): - return isinstance(s,basestring) -else: - def isbasestring(s): - return isinstance(s, (str,bytes)) - -def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): - - if isbasestring(filetype): - dir_path = self.Dir('.').abspath - filetype = sorted(glob.glob(dir_path+"/"+filetype)) - - for path in filetype: - target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) - if shared: - target_name+='.os' - sources.append( self.SharedObject( target=target_name, source=path ) ) - else: - target_name+='.o' - sources.append( self.StaticObject( target=target_name, source=path ) ) - pass - -def isAbsolutePath(key, dirname, env): - assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) - -env_vars = Variables( - args=ARGUMENTS -) - -env_vars.Add('prefix', - help='Installation target location of build results and headers', - default='/usr/local/', - validator=isAbsolutePath -) - -env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], - CPPDEFINES=['SAW_UNIX'], - CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], - LIBS=['gnutls','forstio-io']) -env.__class__.add_source_files = add_kel_source_files -env.Tool('compilation_db'); -env.cdb = env.CompilationDatabase('compile_commands.json'); - -env.objects = []; -env.sources = []; -env.headers = []; -env.targets = []; - -Export('env') -SConscript('SConscript') - -env.Alias('cdb', env.cdb); -env.Alias('all', [env.targets]); -env.Default('all'); - -env.Alias('install', '$prefix') diff --git a/forstio/io-tls/tls.cpp b/forstio/io-tls/tls.cpp deleted file mode 100644 index 9fa143c..0000000 --- a/forstio/io-tls/tls.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include "tls.h" - -#include -#include - -#include - -#include - -#include - -namespace saw { - -class tls::impl { -public: - gnutls_certificate_credentials_t xcred; - -public: - impl() { - gnutls_global_init(); - gnutls_certificate_allocate_credentials(&xcred); - gnutls_certificate_set_x509_system_trust(xcred); - } - - ~impl() { - gnutls_certificate_free_credentials(xcred); - gnutls_global_deinit(); - } -}; - -static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data, - size_t size); -static ssize_t forst_tls_pull_func(gnutls_transport_ptr_t p, void *data, size_t size); - -tls::tls() : impl_{heap()} {} - -tls::~tls() {} - -tls::impl &tls::get_impl() { return *impl_; } - -class tls_io_stream final : public io_stream { -private: - own internal; - gnutls_session_t session_handle; - -public: - tls_io_stream(own internal_) : internal{std::move(internal_)} {} - - ~tls_io_stream() { gnutls_bye(session_handle, GNUTLS_SHUT_RDWR); } - - error_or read(void *buffer, size_t length) override { - ssize_t size = gnutls_record_recv(session_handle, buffer, length); - if (size < 0) { - if(gnutls_error_is_fatal(size) == 0){ - return make_error("Recoverable error on read in gnutls. TODO better error msg handling"); - // Leaving proper message handling done in previous error framework - //return recoverable_error([size](){return std::string{"Read recoverable Error "}+std::string{gnutls_strerror(size)};}, "Error read r"); - }else{ - return make_error("Fatal error on read in gnutls. TODO better error msg handling"); - } - }else if(size == 0){ - return make_error(); - } - - return static_cast(length); - } - - conveyor read_ready() override { return internal->read_ready(); } - - conveyor on_read_disconnected() override { - return internal->on_read_disconnected(); - } - - error_or write(const void *buffer, size_t length) override { - ssize_t size = gnutls_record_send(session_handle, buffer, length); - if(size < 0){ - if(gnutls_error_is_fatal(size) == 0){ - return make_error("Recoverable error on write in gnutls. TODO better error msg handling"); - }else{ - return make_error("Fatal error on write in gnutls. TODO better error msg handling"); - } - } - - return static_cast(size); - } - - conveyor write_ready() override { return internal->write_ready(); } - - gnutls_session_t &session() { return session_handle; } -}; - -tls_server::tls_server(own srv) : internal{std::move(srv)} {} - -conveyor> tls_server::accept() { - SAW_ASSERT(internal) { return conveyor>{fix_void>{nullptr}}; } - return internal->accept().then([](own stream) -> own { - /// @todo handshake - - - return heap(std::move(stream)); - }); -} - -namespace { -/* -* Small helper for setting up the nonblocking connection handshake -*/ -struct tls_client_stream_helper { -public: - own>> feeder; - conveyor_sink connection_sink; - conveyor_sink stream_reader; - conveyor_sink stream_writer; - - own stream = nullptr; -public: - tls_client_stream_helper(own>> f): - feeder{std::move(f)} - {} - - void setupTurn(){ - SAW_ASSERT(stream){ - return; - } - - stream_reader = stream->read_ready().then([this](){ - turn(); - }).sink(); - - stream_writer = stream->write_ready().then([this](){ - turn(); - }).sink(); - } - - void turn(){ - if(stream){ - // Guarantee that the receiving end is already setup - SAW_ASSERT(feeder){ - return; - } - - auto &session = stream->session(); - - int ret; - do { - ret = gnutls_handshake(session); - } while ( (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) && gnutls_error_is_fatal(ret) == 0); - - if(gnutls_error_is_fatal(ret)){ - feeder->fail(make_error("Couldn't create Tls connection")); - stream = nullptr; - }else if(ret == GNUTLS_E_SUCCESS){ - feeder->feed(std::move(stream)); - } - } - } -}; -} - -own tls_network::listen(network_address& address) { - return heap(internal.listen(address)); -} - -conveyor> tls_network::connect(network_address& address) { - // Helper setups - auto caf = new_conveyor_and_feeder>(); - own helper = heap(std::move(caf.feeder)); - tls_client_stream_helper* hlp_ptr = helper.get(); - - // Conveyor entangled structure - auto prim_conv = internal.connect(address).then([this, hlp_ptr, addr = address.address()]( - own stream) -> error_or { - io_stream* inner_stream = stream.get(); - auto tls_stream = heap(std::move(stream)); - - auto &session = tls_stream->session(); - - gnutls_init(&session, GNUTLS_CLIENT); - - gnutls_server_name_set(session, GNUTLS_NAME_DNS, addr.c_str(), - addr.size()); - - gnutls_set_default_priority(session); - gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, - tls_.get_impl().xcred); - gnutls_session_set_verify_cert(session, addr.c_str(), 0); - - gnutls_transport_set_ptr(session, reinterpret_cast(inner_stream)); - gnutls_transport_set_push_function(session, forst_tls_push_func); - gnutls_transport_set_pull_function(session, forst_tls_pull_func); - - // gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - - hlp_ptr->stream = std::move(tls_stream); - hlp_ptr->setupTurn(); - hlp_ptr->turn(); - - return void_t{}; - }); - - helper->connection_sink = prim_conv.sink(); - - return caf.conveyor.attach(std::move(helper)); -} - -own tls_network::datagram(network_address& address){ - ///@unimplemented - return nullptr; -} - -static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data, - size_t size) { - io_stream *stream = reinterpret_cast(p); - if (!stream) { - return -1; - } - - error_or length = stream->write(data, size); - if (length.is_error() || !length.is_value()) { - return -1; - } - - return static_cast(length.get_value()); -} - -static ssize_t forst_tls_pull_func(gnutls_transport_ptr_t p, void *data, size_t size) { - io_stream *stream = reinterpret_cast(p); - if (!stream) { - return -1; - } - - error_or length = stream->read(data, size); - if (length.is_error() || !length.is_value()) { - return -1; - } - - return static_cast(length.get_value()); -} - -tls_network::tls_network(tls& tls_, network &network) : tls_{tls_},internal{network} {} - -conveyor> tls_network::resolve_address(const std::string &addr, - uint16_t port) { - /// @todo tls server name needed. Check validity. Won't matter later on, because gnutls should fail anyway. But - /// it's better to find the error source sooner rather than later - return internal.resolve_address(addr, port); -} - -std::optional> setup_tls_network(network &network) { - return std::nullopt; -} -} // namespace saw diff --git a/forstio/io-tls/tls.h b/forstio/io-tls/tls.h deleted file mode 100644 index 74b39ff..0000000 --- a/forstio/io-tls/tls.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace saw { -class tls; - -class tls_server final : public server { -private: - own internal; - -public: - tls_server(own srv); - - conveyor> accept() override; -}; - -class tls_network final : public network { -private: - tls& tls_; - network &internal; -public: - tls_network(tls& tls_, network &network_); - - conveyor> resolve_address(const std::string &addr, uint16_t port = 0) override; - - own listen(network_address& address) override; - - conveyor> connect(network_address& address) override; - - own datagram(network_address& address) override; -}; - -/** -* tls context class. -* Provides tls network class which ensures the usage of tls encrypted connections -*/ -class tls { -private: - class impl; - own impl_; -public: - tls(); - ~tls(); - - struct version { - struct tls_1_0{}; - struct tls_1_1{}; - struct tls_1_2{}; - }; - - struct options { - public: - version version; - }; - - impl &get_impl(); -private: - options options_; -}; - -std::optional> setup_tls_network(network &network); - -} // namespace saw diff --git a/forstio/io/.nix/derivation.nix b/forstio/io/.nix/derivation.nix deleted file mode 100644 index 0d213d3..0000000 --- a/forstio/io/.nix/derivation.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ lib -, stdenvNoCC -, scons -, clang -, clang-tools -, version -, forstio -}: - -let - -in stdenvNoCC.mkDerivation { - pname = "forstio-io"; - inherit version; - - src = ./..; - - enableParallelBuilding = true; - - nativeBuildInputs = [ - scons - clang - clang-tools - ]; - - buildInputs = [ - forstio.core - forstio.async - ]; - - outputs = ["out" "dev"]; -} diff --git a/forstio/io/SConscript b/forstio/io/SConscript deleted file mode 100644 index 62ad58a..0000000 --- a/forstio/io/SConscript +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/false - -import os -import os.path -import glob - - -Import('env') - -dir_path = Dir('.').abspath - -# Environment for base library -io_env = env.Clone(); - -io_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) -io_env.headers = sorted(glob.glob(dir_path + "/*.h")) - -env.sources += io_env.sources; -env.headers += io_env.headers; - -## Shared lib -objects_shared = [] -io_env.add_source_files(objects_shared, io_env.sources, shared=True); -io_env.library_shared = io_env.SharedLibrary('#build/forstio-io', [objects_shared]); - -## Static lib -objects_static = [] -io_env.add_source_files(objects_static, io_env.sources, shared=False); -io_env.library_static = io_env.StaticLibrary('#build/forstio-io', [objects_static]); - -# Set Alias -env.Alias('library_io', [io_env.library_shared, io_env.library_static]); - -env.targets += ['library_io']; - -# Install -env.Install('$prefix/lib/', [io_env.library_shared, io_env.library_static]); -env.Install('$prefix/include/forstio/io/', [io_env.headers]); diff --git a/forstio/io/SConstruct b/forstio/io/SConstruct deleted file mode 100644 index 4cccf82..0000000 --- a/forstio/io/SConstruct +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import os.path -import glob -import re - - -if sys.version_info < (3,): - def isbasestring(s): - return isinstance(s,basestring) -else: - def isbasestring(s): - return isinstance(s, (str,bytes)) - -def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): - - if isbasestring(filetype): - dir_path = self.Dir('.').abspath - filetype = sorted(glob.glob(dir_path+"/"+filetype)) - - for path in filetype: - target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) - if shared: - target_name+='.os' - sources.append( self.SharedObject( target=target_name, source=path ) ) - else: - target_name+='.o' - sources.append( self.StaticObject( target=target_name, source=path ) ) - pass - -def isAbsolutePath(key, dirname, env): - assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) - -env_vars = Variables( - args=ARGUMENTS -) - -env_vars.Add('prefix', - help='Installation target location of build results and headers', - default='/usr/local/', - validator=isAbsolutePath -) - -env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], - CPPDEFINES=['SAW_UNIX'], - CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], - LIBS=['forstio-async']) -env.__class__.add_source_files = add_kel_source_files -env.Tool('compilation_db'); -env.cdb = env.CompilationDatabase('compile_commands.json'); - -env.objects = []; -env.sources = []; -env.headers = []; -env.targets = []; - -Export('env') -SConscript('SConscript') - -env.Alias('cdb', env.cdb); -env.Alias('all', [env.targets]); -env.Default('all'); - -env.Alias('install', '$prefix') diff --git a/forstio/io/io.cpp b/forstio/io/io.cpp deleted file mode 100644 index f0705d2..0000000 --- a/forstio/io/io.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "io.h" - -#include - -namespace saw { - -async_io_stream::async_io_stream(own str) - : stream_{std::move(str)}, - read_ready_{stream_->read_ready() - .then([this]() { read_stepper_.read_step(*stream_); }) - .sink()}, - write_ready_{stream_->write_ready() - .then([this]() { write_stepper_.write_step(*stream_); }) - .sink()}, - read_disconnected_{stream_->on_read_disconnected() - .then([this]() { - if (read_stepper_.on_read_disconnect) { - read_stepper_.on_read_disconnect->feed(); - } - }) - .sink()} {} - -void async_io_stream::read(void *buffer, size_t min_length, size_t max_length) { - SAW_ASSERT(buffer && max_length >= min_length && min_length > 0) { return; } - - SAW_ASSERT(!read_stepper_.read_task.has_value()) { return; } - - read_stepper_.read_task = read_task_and_step_helper::read_io_task{ - buffer, min_length, max_length, 0}; - read_stepper_.read_step(*stream_); -} - -conveyor async_io_stream::read_done() { - auto caf = new_conveyor_and_feeder(); - read_stepper_.read_done = std::move(caf.feeder); - return std::move(caf.conveyor); -} - -conveyor async_io_stream::on_read_disconnected() { - auto caf = new_conveyor_and_feeder(); - read_stepper_.on_read_disconnect = std::move(caf.feeder); - return std::move(caf.conveyor); -} - -void async_io_stream::write(const void *buffer, size_t length) { - SAW_ASSERT(buffer && length > 0) { return; } - - SAW_ASSERT(!write_stepper_.write_task.has_value()) { return; } - - write_stepper_.write_task = - write_task_and_step_helper::write_io_task{buffer, length, 0}; - write_stepper_.write_step(*stream_); -} - -conveyor async_io_stream::write_done() { - auto caf = new_conveyor_and_feeder(); - write_stepper_.write_done = std::move(caf.feeder); - return std::move(caf.conveyor); -} - -string_network_address::string_network_address(const std::string &address, - uint16_t port) - : address_value_{address}, port_value_{port} {} - -const std::string &string_network_address::address() const { - return address_value_; -} - -uint16_t string_network_address::port() const { return port_value_; } -} // namespace saw diff --git a/forstio/io/io.h b/forstio/io/io.h deleted file mode 100644 index bcc59fd..0000000 --- a/forstio/io/io.h +++ /dev/null @@ -1,214 +0,0 @@ -#pragma once - -#include -#include -#include "io_helpers.h" - -#include -#include - -namespace saw { -/** - * Set of error common in io - */ -namespace err { -struct disconnected { - static constexpr std::string_view description = "Disconnected"; - static constexpr bool is_critical = true; -}; -} -/* - * Input stream - */ -class input_stream { -public: - virtual ~input_stream() = default; - - virtual error_or read(void *buffer, size_t length) = 0; - - virtual conveyor read_ready() = 0; - - virtual conveyor on_read_disconnected() = 0; -}; - -/* - * Output stream - */ -class output_stream { -public: - virtual ~output_stream() = default; - - virtual error_or write(const void *buffer, size_t length) = 0; - - virtual conveyor write_ready() = 0; -}; - -/* - * Io stream - */ -class io_stream : public input_stream, public output_stream { -public: - virtual ~io_stream() = default; -}; - -class async_input_stream { -public: - virtual ~async_input_stream() = default; - - virtual void read(void *buffer, size_t min_length, size_t max_length) = 0; - - virtual conveyor read_done() = 0; - virtual conveyor on_read_disconnected() = 0; -}; - -class async_output_stream { -public: - virtual ~async_output_stream() = default; - - virtual void write(const void *buffer, size_t length) = 0; - - virtual conveyor write_done() = 0; -}; - -class async_io_stream final : public async_input_stream, - public async_output_stream { -private: - own stream_; - - conveyor_sink read_ready_; - conveyor_sink write_ready_; - conveyor_sink read_disconnected_; - - read_task_and_step_helper read_stepper_; - write_task_and_step_helper write_stepper_; - -public: - async_io_stream(own str); - - SAW_FORBID_COPY(async_io_stream); - SAW_FORBID_MOVE(async_io_stream); - - void read(void *buffer, size_t length, size_t max_length) override; - - conveyor read_done() override; - - conveyor on_read_disconnected() override; - - void write(const void *buffer, size_t length) override; - - conveyor write_done() override; -}; - -class server { -public: - virtual ~server() = default; - - virtual conveyor> accept() = 0; -}; - -class network_address; -/** - * Datagram class. Bound to a local address it is able to receive inbound - * datagram messages and send them as well as long as an address is provided as - * well - */ -class datagram { -public: - virtual ~datagram() = default; - - virtual error_or read(void *buffer, size_t length) = 0; - virtual conveyor read_ready() = 0; - - virtual error_or write(const void *buffer, size_t length, - network_address &dest) = 0; - virtual conveyor write_ready() = 0; -}; - -class os_network_address; -class string_network_address; - -class network_address { -public: - using child_variant = - std::variant; - - virtual ~network_address() = default; - - virtual network_address::child_variant representation() = 0; - - virtual const std::string &address() const = 0; - virtual uint16_t port() const = 0; -}; - -class os_network_address : public network_address { -public: - virtual ~os_network_address() = default; - - network_address::child_variant representation() override { return this; } -}; - -class string_network_address final : public network_address { -private: - std::string address_value_; - uint16_t port_value_; - -public: - string_network_address(const std::string &address, uint16_t port); - - const std::string &address() const override; - uint16_t port() const override; - - network_address::child_variant representation() override { return this; } -}; - -class network { -public: - virtual ~network() = default; - - /** - * Resolve the provided string and uint16 to the preferred storage method - */ - virtual conveyor> - resolve_address(const std::string &addr, uint16_t port_hint = 0) = 0; - - /** - * Parse the provided string and uint16 to the preferred storage method - * Since no dns request is made here, no async conveyors have to be used. - */ - /// @todo implement - // virtual Own parseAddress(const std::string& addr, - // uint16_t port_hint = 0) = 0; - - /** - * Set up a listener on this address - */ - virtual own listen(network_address &bind_addr) = 0; - - /** - * Connect to a remote address - */ - virtual conveyor> connect(network_address &address) = 0; - - /** - * Bind a datagram socket at this address. - */ - virtual own datagram(network_address &address) = 0; -}; - -class io_provider { -public: - virtual ~io_provider() = default; - - virtual own wrap_input_fd(int fd) = 0; - - virtual network &network() = 0; -}; - -struct async_io_context { - own io; - event_loop &event_loop; - event_port &event_port; -}; - -error_or setup_async_io(); -} // namespace saw diff --git a/forstio/io/io_helpers.cpp b/forstio/io/io_helpers.cpp deleted file mode 100644 index c2cf2be..0000000 --- a/forstio/io/io_helpers.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "io_helpers.h" - -#include "io.h" - -#include - -namespace saw { -void read_task_and_step_helper::read_step(input_stream &reader) { - while (read_task.has_value()) { - read_io_task &task = *read_task; - - error_or n_err = reader.read(task.buffer, task.max_length); - if (n_err.is_error()) { - const error &error = n_err.get_error(); - if (error.is_critical()) { - if (read_done) { - read_done->fail(error.copy_error()); - } - read_task = std::nullopt; - } - - break; - } else if (n_err.is_value()) { - size_t n = n_err.get_value(); - if (static_cast(n) >= task.min_length && - static_cast(n) <= task.max_length) { - if (read_done) { - read_done->feed(n + task.already_read); - } - read_task = std::nullopt; - } else { - task.buffer = static_cast(task.buffer) + n; - task.min_length -= static_cast(n); - task.max_length -= static_cast(n); - task.already_read += n; - } - - } else { - if (read_done) { - read_done->fail(make_error("Read failed")); - } - read_task = std::nullopt; - } - } -} - -void write_task_and_step_helper::write_step(output_stream &writer) { - while (write_task.has_value()) { - write_io_task &task = *write_task; - - error_or n_err = writer.write(task.buffer, task.length); - - if (n_err.is_value()) { - - size_t n = n_err.get_value(); - assert(n <= task.length); - if (n == task.length) { - if (write_done) { - write_done->feed(n + task.already_written); - } - write_task = std::nullopt; - } else { - task.buffer = static_cast(task.buffer) + n; - task.length -= n; - task.already_written += n; - } - } else if (n_err.is_error()) { - const error &error = n_err.get_error(); - if (error.is_critical()) { - if (write_done) { - write_done->fail(error.copy_error()); - } - write_task = std::nullopt; - } - break; - } else { - if (write_done) { - write_done->fail(make_error("Write failed")); - } - write_task = std::nullopt; - } - } -} - -} // namespace saw diff --git a/forstio/io/io_helpers.h b/forstio/io/io_helpers.h deleted file mode 100644 index 94e37f4..0000000 --- a/forstio/io/io_helpers.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace saw { -/* - * Helper classes for the specific driver implementations - */ - -/* - * Since I don't want to repeat these implementations for tls on unix systems - * and gnutls doesn't let me write or read into buffers I have to have this kind - * of strange abstraction. This may also be reusable for windows/macOS though. - */ -class input_stream; - -class read_task_and_step_helper { -public: - struct read_io_task { - void *buffer; - size_t min_length; - size_t max_length; - size_t already_read = 0; - }; - std::optional read_task; - own> read_done = nullptr; - - own> on_read_disconnect = nullptr; - -public: - void read_step(input_stream &reader); -}; - -class output_stream; - -class write_task_and_step_helper { -public: - struct write_io_task { - const void *buffer; - size_t length; - size_t already_written = 0; - }; - std::optional write_task; - own> write_done = nullptr; - -public: - void write_step(output_stream &writer); -}; -} // namespace saw diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 0000000..8da5a3d --- /dev/null +++ b/src/SConscript @@ -0,0 +1,8 @@ +#!/bin/false + +Import('env') + +# Export to other libs +Export('env'); +SConscript('core/SConscript'); +SConscript('async/SConscript'); diff --git a/src/async/.nix/derivation.nix b/src/async/.nix/derivation.nix new file mode 100644 index 0000000..8ceac08 --- /dev/null +++ b/src/async/.nix/derivation.nix @@ -0,0 +1,31 @@ +{ lib +, stdenvNoCC +, scons +, clang +, clang-tools +, version +, forstio +}: + +let + +in stdenvNoCC.mkDerivation { + pname = "forstio-async"; + inherit version; + + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang + clang-tools + ]; + + buildInputs = [ + forstio.core + ]; + + outputs = ["out" "dev"]; +} diff --git a/src/async/SConscript b/src/async/SConscript new file mode 100644 index 0000000..69f8950 --- /dev/null +++ b/src/async/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +async_env = env.Clone(); + +async_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +async_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += async_env.sources; +env.headers += async_env.headers; + +## Shared lib +objects_shared = [] +async_env.add_source_files(objects_shared, async_env.sources, shared=True); +async_env.library_shared = async_env.SharedLibrary('#build/forstio-async', [objects_shared]); + +## Static lib +objects_static = [] +async_env.add_source_files(objects_static, async_env.sources, shared=False); +async_env.library_static = async_env.StaticLibrary('#build/forstio-async', [objects_static]); + +# Set Alias +env.Alias('library_async', [async_env.library_shared, async_env.library_static]); + +env.targets += ['library_async']; + +# Install +env.Install('$prefix/lib/', [async_env.library_shared, async_env.library_static]); +env.Install('$prefix/include/forstio/async/', [async_env.headers]); diff --git a/src/async/SConstruct b/src/async/SConstruct new file mode 100644 index 0000000..0d7b7c6 --- /dev/null +++ b/src/async/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=['forstio-core']) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/src/async/async.cpp b/src/async/async.cpp new file mode 100644 index 0000000..c53ffa6 --- /dev/null +++ b/src/async/async.cpp @@ -0,0 +1,419 @@ +#include "async.h" +#include +#include + +#include +#include + +namespace saw { +namespace { +thread_local event_loop *local_loop = nullptr; + +event_loop ¤t_event_loop() { + event_loop *loop = local_loop; + assert(loop); + return *loop; +} +} // namespace + +conveyor_node::conveyor_node() {} + +conveyor_node_with_child_mixin::conveyor_node_with_child_mixin( + own &&child_, conveyor_node &owner) + : child{std::move(child_)} { + assert(child); + + child->notify_parent_attached(owner); +} + +error_or> +conveyor_node_with_child_mixin::swap_child(own &&swapee) { + SAW_ASSERT(child) { + return make_error("Child should exist if this function is called"); + } + own old_child = std::move(child); + + /** + * We need the parent of the old_child's next storage + */ + conveyor_storage *old_storage = old_child->next_storage(); + conveyor_storage *old_storage_parent = + old_storage ? old_storage->get_parent() : nullptr; + + /** + * Swap in the new child + */ + if (swapee) { + child = std::move(swapee); + + /** + * Then we need to set the new child's storage parent since the next + * storage has a nullptr set And if the old_storage_parent is a nullptr, + * then it doesn't matter. So we don't check for it + */ + conveyor_storage *swapee_storage = child->next_storage(); + if (swapee_storage) { + swapee_storage->set_parent(old_storage_parent); + } + } + + return old_child; +} + +conveyor_storage::conveyor_storage() {} + +conveyor_storage::~conveyor_storage() {} + +conveyor_storage *conveyor_storage::get_parent() const { return parent_; } + +void conveyor_event_storage::set_parent(conveyor_storage *p) { + /* + * parent check isn't needed, but is used + * for the assert, because the storage should + * be armed if there was an element present + * and a valid parent + */ + if (/*!parent && */ p && !is_armed() && queued() > 0) { + assert(!parent_); + if (p->space() > 0) { + arm_later(); + } + } + + parent_ = p; +} + +conveyor_event_storage::conveyor_event_storage() : conveyor_storage{} {} + +conveyor_base::conveyor_base(own &&node_p) + : node_{std::move(node_p)} {} + +error propagate_error::operator()(const error &error) const { + return error.copy_error(); +} + +error propagate_error::operator()(error &&err) { return std::move(err); } + +event::event() : event(current_event_loop()) {} + +event::event(event_loop &loop) : loop_{loop} {} + +event::~event() { disarm(); } + +void event::arm_next() { + assert(&loop_ == local_loop); + if (prev_ == nullptr) { + // Push the next_insert_point back by one + // and inserts itself before that + next_ = *loop_.next_insert_point_; + prev_ = loop_.next_insert_point_; + *prev_ = this; + if (next_) { + next_->prev_ = &next_; + } + + // Set the new insertion ptr location to next + loop_.next_insert_point_ = &next_; + + // Pushes back the later insert point if it was pointing at the + // previous event + if (loop_.later_insert_point_ == prev_) { + loop_.later_insert_point_ = &next_; + } + + // If tail_ points at the same location then + // we are at the end and have to update tail_ then. + // Technically should be possible by checking if + // next is a `nullptr` + if (loop_.tail_ == prev_) { + loop_.tail_ = &next_; + } + + loop_.set_runnable(true); + } +} + +void event::arm_later() { + assert(&loop_ == local_loop); + + if (prev_ == nullptr) { + next_ = *loop_.later_insert_point_; + prev_ = loop_.later_insert_point_; + *prev_ = this; + if (next_) { + next_->prev_ = &next_; + } + + loop_.later_insert_point_ = &next_; + if (loop_.tail_ == prev_) { + loop_.tail_ = &next_; + } + + loop_.set_runnable(true); + } +} + +void event::arm_last() { + assert(&loop_ == local_loop); + + if (prev_ == nullptr) { + next_ = *loop_.later_insert_point_; + prev_ = loop_.later_insert_point_; + *prev_ = this; + if (next_) { + next_->prev_ = &next_; + } + + if (loop_.tail_ == prev_) { + loop_.tail_ = &next_; + } + + loop_.set_runnable(true); + } +} + +void event::disarm() { + if (prev_ != nullptr) { + if (loop_.tail_ == &next_) { + loop_.tail_ = prev_; + } + + if (loop_.next_insert_point_ == &next_) { + loop_.next_insert_point_ = prev_; + } + + *prev_ = next_; + if (next_) { + next_->prev_ = prev_; + } + + prev_ = nullptr; + next_ = nullptr; + } +} + +bool event::is_armed() const { return prev_ != nullptr; } + +conveyor_sink::conveyor_sink() : node_{nullptr} {} + +conveyor_sink::conveyor_sink(own &&node_p) + : node_{std::move(node_p)} {} + +void event_loop::set_runnable(bool runnable) { is_runnable_ = runnable; } + +event_loop::event_loop() {} + +event_loop::event_loop(own &&ep) + : event_port_{std::move(ep)} {} + +event_loop::~event_loop() { assert(local_loop != this); } + +void event_loop::enter_scope() { + assert(!local_loop); + local_loop = this; +} + +void event_loop::leave_scope() { + assert(local_loop == this); + local_loop = nullptr; +} + +bool event_loop::turn_loop() { + size_t turn_step = 0; + while (head_ && turn_step < 65536) { + if (!turn()) { + return false; + } + ++turn_step; + } + return true; +} + +bool event_loop::turn() { + event *event = head_; + + if (!event) { + return false; + } + + head_ = event->next_; + if (head_) { + head_->prev_ = &head_; + } + + next_insert_point_ = &head_; + if (later_insert_point_ == &event->next_) { + later_insert_point_ = &head_; + } + if (tail_ == &event->next_) { + tail_ = &head_; + } + + event->next_ = nullptr; + event->prev_ = nullptr; + + next_insert_point_ = &head_; + + event->fire(); + + return true; +} + +bool event_loop::wait(const std::chrono::steady_clock::duration &duration) { + if (event_port_) { + event_port_->wait(duration); + } + + return turn_loop(); +} + +bool event_loop::wait(const std::chrono::steady_clock::time_point &time_point) { + if (event_port_) { + event_port_->wait(time_point); + } + + return turn_loop(); +} + +bool event_loop::wait() { + if (event_port_) { + event_port_->wait(); + } + + return turn_loop(); +} + +bool event_loop::poll() { + if (event_port_) { + event_port_->poll(); + } + + return turn_loop(); +} + +event_port *event_loop::event_port() { return event_port_.get(); } + +conveyor_sink_set &event_loop::daemon() { + if (!daemon_sink_) { + daemon_sink_ = heap(); + } + return *daemon_sink_; +} + +wait_scope::wait_scope(event_loop &loop) : loop_{loop} { loop_.enter_scope(); } + +wait_scope::~wait_scope() { loop_.leave_scope(); } + +void wait_scope::wait() { loop_.wait(); } + +void wait_scope::wait(const std::chrono::steady_clock::duration &duration) { + loop_.wait(duration); +} + +void wait_scope::wait(const std::chrono::steady_clock::time_point &time_point) { + loop_.wait(time_point); +} + +void wait_scope::poll() { loop_.poll(); } + +error_or> +convert_conveyor_node_base::swap_child(own &&swapee) noexcept { + return child_mixin_.swap_child(std::move(swapee)); +} + +conveyor_storage *convert_conveyor_node_base::next_storage() noexcept { + if (!child_mixin_.child) { + return nullptr; + } + return child_mixin_.child->next_storage(); +} + +immediate_conveyor_node_base::immediate_conveyor_node_base() + : conveyor_event_storage{} {} + +merge_conveyor_node_base::merge_conveyor_node_base() + : conveyor_event_storage{} {} + +error_or> queue_buffer_conveyor_node_base::swap_child( + own &&swapee_) noexcept { + return child_mixin_.swap_child(std::move(swapee_)); +} + +void conveyor_sink_set::destroy_sink_conveyor_node(conveyor_node &node) { + if (!is_armed()) { + arm_last(); + } + + delete_nodes_.push(&node); +} + +void conveyor_sink_set::fail(error &&error) { + /// @todo call error_handler +} + +conveyor_sink_set::conveyor_sink_set(event_loop &event_loop) + : event{event_loop} {} + +void conveyor_sink_set::add(conveyor &&sink) { + auto nas = conveyor::from_conveyor(std::move(sink)); + SAW_ASSERT(nas) { return; } + conveyor_storage *storage = nas->next_storage(); + + own sink_node = nullptr; + try { + sink_node = heap(std::move(nas), *this); + } catch (std::bad_alloc &) { + return; + } + if (storage) { + storage->set_parent(sink_node.get()); + } + + sink_nodes_.emplace_back(std::move(sink_node)); +} + +void conveyor_sink_set::fire() { + while (!delete_nodes_.empty()) { + conveyor_node *node = delete_nodes_.front(); + /*auto erased = */ std::remove_if(sink_nodes_.begin(), + sink_nodes_.end(), + [node](own &element) { + return node == element.get(); + }); + delete_nodes_.pop(); + } +} + +convert_conveyor_node_base::convert_conveyor_node_base(own &&dep) + : child_mixin_{std::move(dep), *this} {} + +void convert_conveyor_node_base::get_result(error_or_value &err_or_val) { + get_impl(err_or_val); +} + +void attach_conveyor_node_base::get_result( + error_or_value &err_or_val) noexcept { + if (child_mixin_.child) { + child_mixin_.child->get_result(err_or_val); + } +} + +error_or> +attach_conveyor_node_base::swap_child(own &&swapee_) noexcept { + return child_mixin_.swap_child(std::move(swapee_)); +} + +conveyor_storage *attach_conveyor_node_base::next_storage() noexcept { + if (!child_mixin_.child) { + return nullptr; + } + + return child_mixin_.child->next_storage(); +} + +void detach_conveyor(conveyor &&conveyor) { + event_loop &loop = current_event_loop(); + conveyor_sink_set &sink = loop.daemon(); + sink.add(std::move(conveyor)); +} +} // namespace saw diff --git a/src/async/async.h b/src/async/async.h new file mode 100644 index 0000000..4cfed60 --- /dev/null +++ b/src/async/async.h @@ -0,0 +1,1023 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace saw { +class conveyor_storage; +class conveyor_node { +public: + conveyor_node(); + virtual ~conveyor_node() = default; + + /** + * Internal method to retrieve results from children + */ + virtual void get_result(error_or_value &err_or_val) = 0; + + /** + * Swap out child with another one + */ + virtual error_or> + swap_child(own &&swapee_) = 0; + + /** + * Retrieve the next storage node + */ + virtual conveyor_storage *next_storage() = 0; + + /** + * Notify that a new parent was attached + * Only relevant for the feeding nodes + */ + virtual void notify_parent_attached(conveyor_node &){}; +}; + +class conveyor_node_with_child_mixin final { +public: + own child = nullptr; + + conveyor_node_with_child_mixin(own &&child_, + conveyor_node &owner_); + ~conveyor_node_with_child_mixin() = default; + + /** + * Swap out children and return the child ptr, since the caller is the child + * itself. Stack needs to be cleared before the child is destroyed, so the + * swapped out node is returned as well. + */ + error_or> swap_child(own &&swapee); +}; + +class conveyor_node_with_parent_mixin final { +public: + conveyor_node *parent = nullptr; + + error_or> + swap_child_of_parent(own &&swapee) { + SAW_ASSERT(parent) { + return make_error( + "Can't swap child, because parent doesn't exist"); + } + + return parent->swap_child(std::move(swapee)); + } + void change_parent(conveyor_node *p) { parent = p; } +}; + +class event_loop; +class wait_scope; +/* + * Event class similar to capn'proto. + * https://github.com/capnproto/capnproto + */ +class event { +private: + event_loop &loop_; + event **prev_ = nullptr; + event *next_ = nullptr; + + friend class event_loop; + +public: + event(); + event(event_loop &loop); + virtual ~event(); + + virtual void fire() = 0; + + void arm_next(); + void arm_later(); + void arm_last(); + void disarm(); + + bool is_armed() const; +}; + +class conveyor_storage { +protected: + conveyor_storage *parent_ = nullptr; + +public: + conveyor_storage(); + virtual ~conveyor_storage(); + + virtual size_t space() const = 0; + virtual size_t queued() const = 0; + virtual void child_has_fired() = 0; + virtual void parent_has_fired() = 0; + + virtual void set_parent(conveyor_storage *parent) = 0; + conveyor_storage *get_parent() const; +}; + +class conveyor_event_storage : public conveyor_storage, public event { +public: + conveyor_event_storage(); + virtual ~conveyor_event_storage() = default; + + void set_parent(conveyor_storage *parent) override; +}; + +class conveyor_base { +protected: + own node_; + +public: + conveyor_base(own &&node_p); + virtual ~conveyor_base() = default; + + conveyor_base(conveyor_base &&) = default; + conveyor_base &operator=(conveyor_base &&) = default; + + void get(error_or_value &err_or_val); +}; + +template class conveyor; + +template conveyor chained_conveyor_type(T *); + +// template Conveyor chainedConveyorType(Conveyor *); + +template T remove_error_or_type(T *); + +template T remove_error_or_type(error_or *); + +template +using remove_error_or = decltype(remove_error_or_type((T *)nullptr)); + +template +using chained_conveyors = decltype(chained_conveyor_type((T *)nullptr)); + +template +using conveyor_result = + chained_conveyors>>; + +struct propagate_error { +public: + error operator()(const error &err) const; + error operator()(error &&err); +}; + +class conveyor_sink { +private: + own node_; + +public: + conveyor_sink(); + conveyor_sink(own &&node); + + conveyor_sink(conveyor_sink &&) = default; + conveyor_sink &operator=(conveyor_sink &&) = default; +}; + +template class merge_conveyor_node_data; + +template class merge_conveyor { +private: + lent> data_; + +public: + merge_conveyor() = default; + merge_conveyor(lent> d); + ~merge_conveyor(); + + void attach(conveyor conv); +}; + +/** + * Main interface for async operations. + */ +template class conveyor final : public conveyor_base { +public: + /** + * Construct an immediately fulfilled node + */ + conveyor(fix_void value); + + /** + * Construct an immediately failed node + */ + conveyor(error &&err); + + /** + * Construct a conveyor with a child node + */ + conveyor(own node_p); + + conveyor(conveyor &&) = default; + conveyor &operator=(conveyor &&) = default; + + /** + * This method converts values or errors from children + */ + template + [[nodiscard]] conveyor_result + then(Func &&func, ErrorFunc &&error_func = propagate_error()); + + /** + * This method adds a buffer node in the conveyor chains which acts as a + * scheduler interrupt point and collects elements up to the supplied limit. + */ + [[nodiscard]] conveyor + buffer(size_t limit = std::numeric_limits::max()); + + /** + * This method just takes ownership of any supplied types, + * which are destroyed when the chain gets destroyed. + * Useful for resource lifetime control. + */ + template + [[nodiscard]] conveyor attach(Args &&...args); + + /** @todo implement + * This method limits the total amount of passed elements + * Be careful where you place this node into the chain. + * If you meant to fork it and destroy paths you shouldn't place + * an interrupt point between the fork and this limiter + */ + [[nodiscard]] conveyor limit(size_t val = 1); + + /** + * + */ + [[nodiscard]] std::pair, merge_conveyor> merge(); + + /** + * Moves the conveyor chain into a thread local storage point which drops + * every element. Use sink() if you want to control the lifetime of a + * conveyor chain + */ + template + void detach(ErrorFunc &&err_func = propagate_error()); + /** + * Creates a local sink which drops elements, but lifetime control remains + * in your hand. + */ + template + [[nodiscard]] conveyor_sink + sink(ErrorFunc &&error_func = propagate_error()); + + /** + * If no sink() or detach() is used you have to take elements out of the + * chain yourself. + */ + error_or> take(); + + /** @todo implement + * Specifically pump elements through this chain with the provided + * wait_scope + */ + void poll(wait_scope &wait_scope); + + // helper + static conveyor to_conveyor(own node); + + // helper + static own from_conveyor(conveyor conveyor); +}; + +template conveyor_result exec_later(Func &&func); + +/* + * Join Conveyors into a single one + */ +template +conveyor> +join_conveyors(std::tuple...> &conveyors); + +template class conveyor_feeder { +public: + virtual ~conveyor_feeder() = default; + + virtual void feed(T &&data) = 0; + virtual void fail(error &&error) = 0; + + virtual size_t space() const = 0; + virtual size_t queued() const = 0; + + virtual error swap(conveyor &&conveyor) noexcept = 0; +}; + +template <> class conveyor_feeder { +public: + virtual ~conveyor_feeder() = default; + + virtual void feed(void_t &&value = void_t{}) = 0; + virtual void fail(error &&error) = 0; + + virtual size_t space() const = 0; + virtual size_t queued() const = 0; + + virtual error swap(conveyor &&conveyor) noexcept = 0; +}; + +template struct conveyor_and_feeder { + own> feeder; + conveyor conveyor; +}; + +template conveyor_and_feeder new_conveyor_and_feeder(); + +template conveyor_and_feeder one_time_conveyor_and_feeder(); + +enum class Signal : uint8_t { Terminate, User1 }; + +/** + * Class which acts as a correspondent between the running framework and outside + * events which may be signals from the operating system or just other threads. + * Default EventPorts are supplied by setupAsyncIo() in io.h + */ +class event_port { +public: + virtual ~event_port() = default; + + virtual conveyor on_signal(Signal signal) = 0; + + virtual void poll() = 0; + virtual void wait() = 0; + virtual void wait(const std::chrono::steady_clock::duration &) = 0; + virtual void wait(const std::chrono::steady_clock::time_point &) = 0; + + virtual void wake() = 0; +}; + +class sink_conveyor_node; + +class conveyor_sink_set final : public event { +private: + /* + class Helper final : public Event { + private: + void destroySinkConveyorNode(ConveyorNode& sink); + void fail(Error&& error); + + std::vector> sink_nodes; + std::queue delete_nodes; + std::function error_handler; + + public: + ConveyorSinks() = default; + ConveyorSinks(EventLoop& event_loop); + + void add(Conveyor node); + + void fire() override {} + }; + + gin::Own helper; + */ + friend class sink_conveyor_node; + + void destroy_sink_conveyor_node(conveyor_node &sink_node); + void fail(error &&err); + + std::list> sink_nodes_; + + std::queue delete_nodes_; + + std::function error_handler_; + +public: + // ConveyorSinks(); + // ConveyorSinks(EventLoop& event_loop); + conveyor_sink_set() = default; + conveyor_sink_set(event_loop &event_loop); + + void add(conveyor &&node); + + void fire() override; +}; + +/* + * EventLoop class similar to capn'proto. + * https://github.com/capnproto/capnproto + */ +class event_loop { +private: + friend class event; + event *head_ = nullptr; + event **tail_ = &head_; + event **next_insert_point_ = &head_; + event **later_insert_point_ = &head_; + + bool is_runnable_ = false; + + own event_port_ = nullptr; + + own daemon_sink_ = nullptr; + + // functions + void set_runnable(bool runnable); + + friend class wait_scope; + void enter_scope(); + void leave_scope(); + + bool turn_loop(); + bool turn(); + +public: + event_loop(); + event_loop(own &&port); + ~event_loop(); + + event_loop(event_loop &&) = default; + event_loop &operator=(event_loop &&) = default; + + bool wait(); + bool wait(const std::chrono::steady_clock::duration &); + bool wait(const std::chrono::steady_clock::time_point &); + bool poll(); + + event_port *event_port(); + + conveyor_sink_set &daemon(); +}; + +/* + * WaitScope class similar to capn'proto. + * https://github.com/capnproto/capnproto + */ +class wait_scope { +private: + event_loop &loop_; + +public: + wait_scope(event_loop &loop); + ~wait_scope(); + + void wait(); + void wait(const std::chrono::steady_clock::duration &); + void wait(const std::chrono::steady_clock::time_point &); + void poll(); +}; + +template conveyor_result yield_next(Func &&func); + +template conveyor_result yield_later(Func &&func); + +template conveyor_result yield_last(Func &&func); +} // namespace saw + +// Secret stuff +// Aka private semi hidden classes +namespace saw { + +template struct fix_void_caller { + template static Out apply(Func &func, In &&in) { + return func(std::move(in)); + } +}; + +template struct fix_void_caller { + template static Out apply(Func &func, void_t &&in) { + (void)in; + return func(); + } +}; + +template struct fix_void_caller { + template static void_t apply(Func &func, In &&in) { + func(std::move(in)); + return void_t{}; + } +}; + +template <> struct fix_void_caller { + template static void_t apply(Func &func, void_t &&in) { + (void)in; + func(); + return void_t{}; + } +}; + +template class adapt_conveyor_node; + +template +class adapt_conveyor_feeder final : public conveyor_feeder> { +private: + adapt_conveyor_node *feedee_ = nullptr; + +public: + ~adapt_conveyor_feeder(); + + void set_feedee(adapt_conveyor_node *feedee); + + void feed(T &&value) override; + void fail(error &&error) override; + + size_t space() const override; + size_t queued() const override; + + error swap(conveyor &&conv) noexcept override; +}; + +template +class adapt_conveyor_node final : public conveyor_node, + public conveyor_event_storage { +private: + adapt_conveyor_feeder *feeder_ = nullptr; + + std::queue>> storage_; + + conveyor_node_with_parent_mixin parent_node_; + +public: + adapt_conveyor_node(); + ~adapt_conveyor_node(); + + void set_feeder(adapt_conveyor_feeder *feeder); + + void feed(T &&value); + void fail(error &&error); + + // ConveyorNode + void get_result(error_or_value &err_or_val) override; + + error_or> + swap_child(own &&swapee) noexcept override; + + conveyor_storage *next_storage() noexcept override; + void notify_parent_attached(conveyor_node &) noexcept override; + + // ConveyorStorage + size_t space() const override; + size_t queued() const override; + + void child_has_fired() override; + void parent_has_fired() override; + + // Event + void fire() override; +}; + +template class one_time_conveyor_node; + +template +class one_time_conveyor_feeder final : public conveyor_feeder> { +private: + one_time_conveyor_node *feedee_ = nullptr; + +public: + ~one_time_conveyor_feeder(); + + void set_feedee(one_time_conveyor_node *feedee); + + void feed(T &&value) override; + void fail(error &&error) override; + + size_t space() const override; + size_t queued() const override; +}; + +template +class one_time_conveyor_node final : public conveyor_node, + public conveyor_storage, + public event { +private: + one_time_conveyor_feeder *feeder_ = nullptr; + + bool passed_ = false; + maybe> storage_ = std::nullopt; + +public: + ~one_time_conveyor_node(); + + void set_feeder(one_time_conveyor_feeder *feeder); + + void feed(T &&value); + void fail(error &&error); + + // ConveyorNode + void get_result(error_or_value &err_or_val) override; + + error_or> + swap_child(own &&swapee) override; + + // ConveyorStorage + size_t space() const override; + size_t queued() const override; + + void child_has_fired() override {} + void parent_has_fired() override; + + // Event + void fire() override; +}; + +/** + * This class buffers and saves incoming elements and acts as an interrupt node + * for processing calls + */ +class queue_buffer_conveyor_node_base : public conveyor_node, + public conveyor_event_storage { +protected: + conveyor_node_with_child_mixin child_mixin_; + +public: + queue_buffer_conveyor_node_base(own child_) + : conveyor_event_storage{}, child_mixin_{std::move(child_), *this} {} + virtual ~queue_buffer_conveyor_node_base() = default; + + /** + * Use mixin + */ + error_or> + swap_child(own &&swapee_) noexcept override; + + conveyor_storage *next_storage() noexcept override { + return static_cast(this); + } +}; + +template +class queue_buffer_conveyor_node final + : public queue_buffer_conveyor_node_base { +private: + std::queue> storage_; + size_t max_store_; + +public: + queue_buffer_conveyor_node(own dep, size_t max_size) + : queue_buffer_conveyor_node_base{std::move(dep)}, max_store_{ + max_size} {} + // Event + void fire() override; + // ConveyorNode + void get_result(error_or_value &eov) noexcept override; + + // ConveyorStorage + size_t space() const override; + size_t queued() const override; + + void child_has_fired() override; + void parent_has_fired() override; +}; + +class attach_conveyor_node_base : public conveyor_node { +protected: + conveyor_node_with_child_mixin child_mixin_; + +public: + attach_conveyor_node_base(own &&child_) + : child_mixin_{std::move(child_), *this} {} + + virtual ~attach_conveyor_node_base() = default; + + void get_result(error_or_value &err_or_val) noexcept override; + + /** + * Use mixin + */ + error_or> + swap_child(own &&swapee_) noexcept override; + + conveyor_storage *next_storage() noexcept override; +}; + +template +class attach_conveyor_node final : public attach_conveyor_node_base { +public: + attach_conveyor_node(own &&dep, Args &&...args) + : attach_conveyor_node_base(std::move(dep)), attached_data_{ + std::move(args...)} {} + +private: + std::tuple attached_data_; +}; + +class convert_conveyor_node_base : public conveyor_node { +public: + convert_conveyor_node_base(own &&dep); + virtual ~convert_conveyor_node_base() = default; + + void get_result(error_or_value &err_or_val) override; + + virtual void get_impl(error_or_value &err_or_val) = 0; + + error_or> + swap_child(own &&swapee) noexcept override; + + conveyor_storage *next_storage() noexcept override; + +protected: + conveyor_node_with_child_mixin child_mixin_; +}; + +template +class convert_conveyor_node final : public convert_conveyor_node_base { +private: + Func func_; + ErrorFunc error_func_; + + static_assert(std::is_same>::value, + "Should never be of type ErrorOr"); + +public: + convert_conveyor_node(own &&dep, Func &&func, + ErrorFunc &&error_func) + : convert_conveyor_node_base(std::move(dep)), func_{std::move(func)}, + error_func_{std::move(error_func)} {} + + void get_impl(error_or_value &err_or_val) noexcept override { + error_or> dep_eov; + error_or>> &eov = + err_or_val.as>>(); + if (child_mixin_.child) { + child_mixin_.child->get_result(dep_eov); + if (dep_eov.is_value()) { + try { + + eov = fix_void_caller::apply( + func_, std::move(dep_eov.get_value())); + } catch (const std::bad_alloc &) { + eov = make_error("Out of memory"); + } catch (const std::exception &) { + eov = make_error( + "Exception in chain occured. Return ErrorOr if you " + "want to handle errors which are recoverable"); + } + } else if (dep_eov.is_error()) { + eov = error_func_(std::move(dep_eov.get_error())); + } else { + eov = make_error("No value set in dependency"); + } + } else { + eov = make_error("Conveyor doesn't have child"); + } + } +}; + +class sink_conveyor_node final : public conveyor_node, + public conveyor_event_storage { +private: + conveyor_node_with_child_mixin child_mixin_; + conveyor_sink_set *conveyor_sink_; + +public: + sink_conveyor_node(own node, conveyor_sink_set &conv_sink) + : conveyor_event_storage{}, child_mixin_{std::move(node), *this}, + conveyor_sink_{&conv_sink} {} + + sink_conveyor_node(own node) + : conveyor_event_storage{}, child_mixin_{std::move(node), *this}, + conveyor_sink_{nullptr} {} + + // Event only queued if a critical error occured + void fire() override { + // Queued for destruction of children, because this acts as a sink and + // no other event should be here + child_mixin_.child = nullptr; + + if (conveyor_sink_) { + conveyor_sink_->destroy_sink_conveyor_node(*this); + conveyor_sink_ = nullptr; + } + } + + // ConveyorStorage + size_t space() const override { return 1; } + size_t queued() const override { return 0; } + + // ConveyorNode + void get_result(error_or_value &err_or_val) noexcept override { + err_or_val.as() = + make_error("In a sink node no result can be returned"); + } + + error_or> + swap_child(own &&swapee) noexcept override { + return child_mixin_.swap_child(std::move(swapee)); + } + + // ConveyorStorage + void child_has_fired() override { + if (child_mixin_.child) { + error_or dep_eov; + child_mixin_.child->get_result(dep_eov); + if (dep_eov.is_error()) { + if (dep_eov.get_error().is_critical()) { + if (!is_armed()) { + arm_last(); + } + } + if (conveyor_sink_) { + conveyor_sink_->fail(std::move(dep_eov.get_error())); + } + } + } + } + + /* + * No parent needs to be fired since we always have space + */ + void parent_has_fired() override {} + + conveyor_storage *next_storage() override { + // Should never happen though + assert(false); + return nullptr; + // return static_cast(this); + } +}; + +class immediate_conveyor_node_base : public conveyor_node, + public conveyor_event_storage { +private: +public: + immediate_conveyor_node_base(); + + virtual ~immediate_conveyor_node_base() = default; + + error_or> + swap_child(own &&swapee) noexcept override { + (void)swapee; + return make_error("Node doesn't support swapping"); + } + + conveyor_storage *next_storage() noexcept override { + return static_cast(this); + } +}; + +template +class immediate_conveyor_node final : public immediate_conveyor_node_base { +private: + error_or> value_; + uint8_t retrieved_; + +public: + immediate_conveyor_node(fix_void &&val); + immediate_conveyor_node(error &&error); + + // ConveyorStorage + size_t space() const override; + size_t queued() const override; + + void child_has_fired() override; + void parent_has_fired() override; + + // ConveyorNode + void get_result(error_or_value &err_or_val) noexcept override { + if (retrieved_ > 0) { + err_or_val.as>() = + make_error("Already taken value"); + } else { + err_or_val.as>() = std::move(value_); + } + if (queued() > 0) { + ++retrieved_; + } + } + + // Event + void fire() override; +}; + +/* + * Collects every incoming value and throws it in one lane + */ +class merge_conveyor_node_base : public conveyor_node, + public conveyor_event_storage { +public: + merge_conveyor_node_base(); + + virtual ~merge_conveyor_node_base() = default; + + conveyor_storage *next_storage() noexcept override { + return static_cast(this); + } +}; + +template +class merge_conveyor_node : public merge_conveyor_node_base { +private: + class appendage final : public conveyor_node, public conveyor_storage { + public: + own child; + merge_conveyor_node *merger; + + maybe>> error_or_value_; + + public: + appendage(own n, merge_conveyor_node &m) + : conveyor_storage{}, child{std::move(n)}, merger{&m}, + error_or_value_{std::nullopt} {} + + bool child_storage_has_element_queued() const { + if (!child) { + return false; + } + conveyor_storage *storage = child->next_storage(); + if (storage) { + return storage->queued() > 0; + } + return false; + } + + void get_appendage_result(error_or_value &eov); + + /** + * ConveyorNode + */ + error_or> + swap_child(own &&swapee_) override; + + conveyor_storage *next_storage() noexcept override { + return static_cast(this); + } + + void get_result(error_or_value &err_or_val) override; + + /** + * ConveyorStorage + */ + size_t space() const override; + + size_t queued() const override; + + void child_has_fired() override; + + void parent_has_fired() override; + + void set_parent(conveyor_storage *par) override; + }; + + friend class merge_conveyor_node_data; + friend class appendage; + + our> data_; + size_t next_appendage_ = 0; + +public: + merge_conveyor_node(our> data); + ~merge_conveyor_node(); + // ConveyorNode + error_or> + swap_child(own &&c) noexcept override; + + // Event + void get_result(error_or_value &err_or_val) noexcept override; + + void fire() override; + + // ConveyorStorage + size_t space() const override; + size_t queued() const override; + void child_has_fired() override; + void parent_has_fired() override; +}; + +template class merge_conveyor_node_data { +public: + std::vector::appendage>> appendages; + + merge_conveyor_node *merger = nullptr; + +public: + void attach(conveyor conv); + + void governing_node_destroyed(); +}; + +/* +class JoinConveyorNodeBase : public ConveyorNode, public ConveyorEventStorage { +private: + +public: +}; + +template +class JoinConveyorNode final : public JoinConveyorNodeBase { +private: + template + class Appendage : public ConveyorEventStorage { + private: + Maybe data = std::nullopt; + + public: + size_t space() const override; + size_t queued() const override; + + void fire() override; + void get_result(ErrorOrValue& eov) override; + }; + + std::tuple...> appendages; + +public: +}; + +*/ + +} // namespace saw + +#include "async.tmpl.h" diff --git a/src/async/async.tmpl.h b/src/async/async.tmpl.h new file mode 100644 index 0000000..d081fa9 --- /dev/null +++ b/src/async/async.tmpl.h @@ -0,0 +1,769 @@ +#pragma once + +#include +#include + +#include +// Template inlining + +#include + +namespace saw { + +template conveyor_result execLater(Func &&func) { + conveyor conveyor{fix_void{}}; + return conveyor.then(std::move(func)); +} + +template +conveyor::conveyor(fix_void value) : conveyor_base(nullptr) { + // Is there any way to do this? + // @todo new conveyor_base constructor for Immediate values + + own>> immediate = + heap>>(std::move(value)); + + if (!immediate) { + return; + } + + node_ = std::move(immediate); +} + +template +conveyor::conveyor(error &&err) : conveyor_base(nullptr) { + own>> immediate = + heap>>(std::move(err)); + + if (!immediate) { + return; + } + + node_ = std::move(immediate); +} + +template +conveyor::conveyor(own node_p) + : conveyor_base{std::move(node_p)} {} + +template +template +conveyor_result conveyor::then(Func &&func, + ErrorFunc &&error_func) { + own conversion_node = + heap>, fix_void, + Func, ErrorFunc>>( + std::move(node_), std::move(func), std::move(error_func)); + + return conveyor>>::to_conveyor( + std::move(conversion_node)); +} + +template conveyor conveyor::buffer(size_t size) { + SAW_ASSERT(node_) { return conveyor{own{nullptr}}; } + conveyor_storage *storage = node_->next_storage(); + SAW_ASSERT(storage) { return conveyor{own{nullptr}}; } + + own>> storage_node = + heap>>(std::move(node_), size); + + conveyor_storage *storage_ptr = + static_cast(storage_node.get()); + + storage->set_parent(storage_ptr); + return conveyor{std::move(storage_node)}; +} + +template +template +conveyor conveyor::attach(Args &&...args) { + own> attach_node = + heap>(std::move(node_), + std::move(args...)); + return conveyor{std::move(attach_node)}; +} + +template +std::pair, merge_conveyor> conveyor::merge() { + our> data = + share>(); + + own> merge_node = heap>(data); + + SAW_ASSERT(node_) { + return std::make_pair(conveyor{own{nullptr}}, + merge_conveyor{}); + } + conveyor_storage *storage = node_->next_storage(); + SAW_ASSERT(storage) { + return std::make_pair(conveyor{own{nullptr}}, + merge_conveyor{}); + } + + data->attach(conveyor::to_conveyor(std::move(node_))); + + merge_conveyor node_ref{data}; + + return std::make_pair(conveyor{std::move(merge_node)}, + std::move(node_ref)); +} + +template <> +template +conveyor_sink conveyor::sink(ErrorFunc &&error_func) { + conveyor_storage *storage = node_->next_storage(); + SAW_ASSERT(storage) { return conveyor_sink{}; } + + own sink_node = + heap(std::move(node_)); + conveyor_storage *storage_ptr = + static_cast(sink_node.get()); + + storage->set_parent(storage_ptr); + + return conveyor_sink{std::move(sink_node)}; +} + +void detach_conveyor(conveyor &&conveyor); + +template +template +void conveyor::detach(ErrorFunc &&func) { + detach_conveyor(std::move(then([](T &&) {}, std::move(func)))); +} + +template <> +template +void conveyor::detach(ErrorFunc &&func) { + detach_conveyor(std::move(then([]() {}, std::move(func)))); +} + +template +conveyor conveyor::to_conveyor(own node) { + return conveyor{std::move(node)}; +} + +template +own conveyor::from_conveyor(conveyor conveyor) { + return std::move(conveyor.node_); +} + +template error_or> conveyor::take() { + SAW_ASSERT(node_) { + return error_or>{ + make_error("conveyor in invalid state")}; + } + conveyor_storage *storage = node_->next_storage(); + if (storage) { + if (storage->queued() > 0) { + error_or> result; + node_->get_result(result); + return result; + } else { + return error_or>{ + make_error("conveyor buffer has no elements")}; + } + } else { + return error_or>{ + make_error("conveyor node has no child storage")}; + } +} + +template conveyor_and_feeder new_conveyor_and_feeder() { + own>> feeder = + heap>>(); + own>> node = + heap>>(); + + feeder->set_feedee(node.get()); + node->set_feeder(feeder.get()); + + return conveyor_and_feeder{std::move(feeder), + conveyor::to_conveyor(std::move(node))}; +} + +// QueueBuffer +template void queue_buffer_conveyor_node::fire() { + if (child_mixin_.child) { + if (!storage_.empty()) { + if (storage_.front().is_error()) { + if (storage_.front().get_error().is_critical()) { + child_mixin_.child = nullptr; + } + } + } + } + + bool has_space_before_fire = space() > 0; + + if (parent_) { + parent_->child_has_fired(); + if (!storage_.empty() && parent_->space() > 0) { + arm_later(); + } + } + + if (!child_mixin_.child) { + while (!storage_.empty()) { + storage_.pop(); + } + return; + } + + conveyor_storage *ch_storage = child_mixin_.child->next_storage(); + if (ch_storage && !has_space_before_fire) { + ch_storage->parent_has_fired(); + } +} + +template +void queue_buffer_conveyor_node::get_result(error_or_value &eov) noexcept { + error_or &err_or_val = eov.as(); + err_or_val = std::move(storage_.front()); + storage_.pop(); +} + +template size_t queue_buffer_conveyor_node::space() const { + return max_store_ - storage_.size(); +} + +template size_t queue_buffer_conveyor_node::queued() const { + return storage_.size(); +} + +template void queue_buffer_conveyor_node::child_has_fired() { + if (child_mixin_.child && storage_.size() < max_store_) { + error_or eov; + child_mixin_.child->get_result(eov); + + if (eov.is_error()) { + if (eov.get_error().is_critical()) { + } + } + + storage_.push(std::move(eov)); + if (!is_armed()) { + arm_later(); + } + } +} + +template void queue_buffer_conveyor_node::parent_has_fired() { + SAW_ASSERT(parent_) { return; } + + if (parent_->space() == 0) { + return; + } + + if (queued() > 0) { + arm_later(); + } +} + +template +immediate_conveyor_node::immediate_conveyor_node(fix_void &&val) + : value_{std::move(val)}, retrieved_{0} {} + +template +immediate_conveyor_node::immediate_conveyor_node(error &&error) + : value_{std::move(error)}, retrieved_{0} {} + +template size_t immediate_conveyor_node::space() const { + return 0; +} + +template size_t immediate_conveyor_node::queued() const { + return retrieved_ > 1 ? 0 : 1; +} + +template void immediate_conveyor_node::child_has_fired() { + // Impossible case + assert(false); +} + +template void immediate_conveyor_node::parent_has_fired() { + SAW_ASSERT(parent_) { return; } + assert(parent_->space() > 0); + + if (queued() > 0) { + arm_next(); + } +} + +template void immediate_conveyor_node::fire() { + + if (parent_) { + parent_->child_has_fired(); + if (queued() > 0 && parent_->space() > 0) { + arm_last(); + } + } +} + +template +merge_conveyor::merge_conveyor(lent> d) + : data_{std::move(d)} {} + +template merge_conveyor::~merge_conveyor() {} + +template void merge_conveyor::attach(conveyor conveyor) { + auto sp = data_.lock(); + SAW_ASSERT(sp) { return; } + + sp->attach(std::move(conveyor)); +} + +template +merge_conveyor_node::merge_conveyor_node(our> d) + : data_{d} { + SAW_ASSERT(data_) { return; } + + data_->merger = this; +} + +template merge_conveyor_node::~merge_conveyor_node() {} + +template +error_or> +merge_conveyor_node::swap_child(own &&swapee_) noexcept { + (void)swapee_; + return make_error( + "merge_conveyor_node::appendage should block calls to this class"); +} + +template +void merge_conveyor_node::get_result(error_or_value &eov) noexcept { + error_or> &err_or_val = eov.as>(); + + SAW_ASSERT(data_) { return; } + + /// @todo search appendages for result + + auto &appendages = data_->appendages; + next_appendage_ = std::min(appendages.size(), next_appendage_); + + for (size_t i = next_appendage_; i < appendages.size(); ++i) { + if (appendages[i]->queued() > 0) { + err_or_val = std::move(appendages[i]->error_or_value_.value()); + appendages[i]->error_or_value_ = std::nullopt; + next_appendage_ = i + 1; + return; + } + } + for (size_t i = 0; i < next_appendage_; ++i) { + if (appendages[i]->queued() > 0) { + err_or_val = std::move(appendages[i]->error_or_value_.value()); + appendages[i]->error_or_value_ = std::nullopt; + next_appendage_ = i + 1; + return; + } + } + + err_or_val = make_error("No value in Merge appendages"); +} + +template void merge_conveyor_node::fire() { + SAW_ASSERT(queued() > 0) { return; } + + if (parent_) { + parent_->child_has_fired(); + + if (queued() > 0 && parent_->space() > 0) { + arm_later(); + } + } +} + +template size_t merge_conveyor_node::space() const { return 0; } + +template size_t merge_conveyor_node::queued() const { + SAW_ASSERT(data_) { return 0; } + + size_t queue_count = 0; + + for (auto &iter : data_->appendages) { + queue_count += iter->queued(); + } + + return queue_count; +} + +template void merge_conveyor_node::child_has_fired() { + /// This can never happen + assert(false); +} + +template void merge_conveyor_node::parent_has_fired() { + SAW_ASSERT(parent_) { return; } + if (queued() > 0) { + if (parent_->space() > 0) { + arm_later(); + } + } +} + +/** + * merge_conveyor_node::Apendage + */ + +template +error_or> +merge_conveyor_node::appendage::swap_child(own &&swapee_) { + own old_child = std::move(child); + + child = std::move(swapee_); + + // This case should never happen + SAW_ASSERT(old_child) { return make_error("No child exists"); } + + return old_child; +} + +template +void merge_conveyor_node::appendage::get_result(error_or_value &eov) { + error_or> &err_or_val = eov.as>(); + + SAW_ASSERT(queued() > 0) { + err_or_val = + make_error("No element queued in Merge appendage Node"); + return; + } + + err_or_val = std::move(error_or_value_.value()); + error_or_value_ = std::nullopt; +} + +template size_t merge_conveyor_node::appendage::space() const { + SAW_ASSERT(merger) { return 0; } + + if (error_or_value_.has_value()) { + return 0; + } + + return 1; +} + +template size_t merge_conveyor_node::appendage::queued() const { + SAW_ASSERT(merger) { return 0; } + + if (error_or_value_.has_value()) { + return 1; + } + + return 0; +} + +/// @todo delete this function. Replaced by the regular get_result +template +void merge_conveyor_node::appendage::get_appendage_result( + error_or_value &eov) { + error_or> &err_or_val = eov.as>(); + + SAW_ASSERT(queued() > 0) { + err_or_val = + make_error("No element queued in Merge appendage Node"); + return; + } + + err_or_val = std::move(error_or_value_.value()); + error_or_value_ = std::nullopt; +} + +template +void merge_conveyor_node::appendage::child_has_fired() { + SAW_ASSERT(!error_or_value_.has_value()) { return; } + error_or> eov; + child->get_result(eov); + + error_or_value_ = std::move(eov); + + if (!merger->is_armed()) { + merger->arm_later(); + } +} + +template +void merge_conveyor_node::appendage::parent_has_fired() { + conveyor_storage *child_storage = child->next_storage(); + if (child_storage) { + child_storage->parent_has_fired(); + } +} + +template +void merge_conveyor_node::appendage::set_parent(conveyor_storage *par) { + SAW_ASSERT(merger) { return; } + + SAW_ASSERT(child) { return; } + + parent_ = par; +} + +template +void merge_conveyor_node_data::attach(conveyor conv) { + auto nas = conveyor::from_conveyor(std::move(conv)); + SAW_ASSERT(nas) { return; } + conveyor_storage *storage = nas->next_storage(); + SAW_ASSERT(storage) { return; } + + auto merge_node_appendage = + heap::appendage>(std::move(nas), + *merger); + auto merge_node_appendage_ptr = merge_node_appendage.get(); + + storage->set_parent(merge_node_appendage.get()); + + SAW_ASSERT(merger) { return; } + + conveyor_storage *mrg_storage = merger->next_storage(); + SAW_ASSERT(mrg_storage) { return; } + + merge_node_appendage->set_parent(mrg_storage); + + appendages.push_back(std::move(merge_node_appendage)); + + /// @todo return this. necessary? maybe for the weird linking setup + /// maybe not + // return merge_node_appendage_ptr; +} + +template +void merge_conveyor_node_data::governing_node_destroyed() { + appendages.clear(); + merger = nullptr; +} + +template adapt_conveyor_feeder::~adapt_conveyor_feeder() { + if (feedee_) { + feedee_->set_feeder(nullptr); + feedee_ = nullptr; + } +} + +template +void adapt_conveyor_feeder::set_feedee(adapt_conveyor_node *feedee_p) { + feedee_ = feedee_p; +} + +template void adapt_conveyor_feeder::feed(T &&value) { + if (feedee_) { + feedee_->feed(std::move(value)); + } +} + +template void adapt_conveyor_feeder::fail(error &&error) { + if (feedee_) { + feedee_->fail(std::move(error)); + } +} + +template size_t adapt_conveyor_feeder::queued() const { + if (feedee_) { + return feedee_->queued(); + } + return 0; +} + +template size_t adapt_conveyor_feeder::space() const { + if (feedee_) { + return feedee_->space(); + } + return 0; +} + +template +error adapt_conveyor_feeder::swap(conveyor &&conv) noexcept { + SAW_ASSERT(feedee_) { return make_error("No feedee connected"); } + + auto node = conveyor::from_conveyor(std::move(conv)); + + feedee_->swap_child(std::move(node)); + + return no_error(); +} + +template +adapt_conveyor_node::adapt_conveyor_node() : conveyor_event_storage{} {} + +template adapt_conveyor_node::~adapt_conveyor_node() { + if (feeder_) { + feeder_->set_feedee(nullptr); + feeder_ = nullptr; + } +} + +template +error_or> +adapt_conveyor_node::swap_child(own &&swapee) noexcept { + // This should return the owning pointer of this instance + auto myself_err = parent_node_.swap_child_of_parent(std::move(swapee)); + + if (myself_err.is_error()) { + return myself_err; + } + + auto &myself = myself_err.get_value(); + + assert(myself.get() == this); + + return myself_err; +} + +template +conveyor_storage *adapt_conveyor_node::next_storage() noexcept { + return static_cast(this); +} + +template +void adapt_conveyor_node::notify_parent_attached( + conveyor_node &par) noexcept { + parent_node_.change_parent(&par); +} + +template +void adapt_conveyor_node::set_feeder(adapt_conveyor_feeder *feeder_p) { + feeder_ = feeder_p; +} + +template void adapt_conveyor_node::feed(T &&value) { + storage_.push(std::move(value)); + arm_next(); +} + +template void adapt_conveyor_node::fail(error &&error) { + storage_.push(std::move(error)); + arm_next(); +} + +template size_t adapt_conveyor_node::queued() const { + return storage_.size(); +} + +template size_t adapt_conveyor_node::space() const { + return std::numeric_limits::max() - storage_.size(); +} + +template +void adapt_conveyor_node::get_result(error_or_value &err_or_val) { + if (!storage_.empty()) { + err_or_val.as() = std::move(storage_.front()); + storage_.pop(); + } else { + err_or_val.as() = make_error( + "Signal for retrieval of storage sent even though no " + "data is present"); + } +} + +template void adapt_conveyor_node::child_has_fired() { + // Adapt node has no children + assert(false); +} + +template void adapt_conveyor_node::parent_has_fired() { + SAW_ASSERT(parent_) { return; } + + if (parent_->space() == 0) { + return; + } +} + +template void adapt_conveyor_node::fire() { + if (parent_) { + parent_->child_has_fired(); + + if (storage_.size() > 0) { + arm_later(); + } + } +} + +template one_time_conveyor_feeder::~one_time_conveyor_feeder() { + if (feedee_) { + feedee_->set_feeder(nullptr); + feedee_ = nullptr; + } +} + +template +void one_time_conveyor_feeder::set_feedee( + one_time_conveyor_node *feedee_p) { + feedee_ = feedee_p; +} + +template void one_time_conveyor_feeder::feed(T &&value) { + if (feedee_) { + feedee_->feed(std::move(value)); + } +} + +template void one_time_conveyor_feeder::fail(error &&error) { + if (feedee_) { + feedee_->fail(std::move(error)); + } +} + +template size_t one_time_conveyor_feeder::queued() const { + if (feedee_) { + return feedee_->queued(); + } + return 0; +} + +template size_t one_time_conveyor_feeder::space() const { + if (feedee_) { + return feedee_->space(); + } + return 0; +} + +template one_time_conveyor_node::~one_time_conveyor_node() { + if (feeder_) { + feeder_->set_feedee(nullptr); + feeder_ = nullptr; + } +} + +template +void one_time_conveyor_node::set_feeder( + one_time_conveyor_feeder *feeder_p) { + feeder_ = feeder_p; +} + +template void one_time_conveyor_node::feed(T &&value) { + storage_ = std::move(value); + arm_next(); +} + +template void one_time_conveyor_node::fail(error &&error) { + storage_ = std::move(error); + arm_next(); +} + +template size_t one_time_conveyor_node::queued() const { + return storage_.has_value() ? 1 : 0; +} + +template size_t one_time_conveyor_node::space() const { + return passed_ ? 0 : 1; +} + +template +void one_time_conveyor_node::get_result(error_or_value &err_or_val) { + if (storage_.has_value()) { + err_or_val.as() = std::move(storage_.value()); + storage_ = std::nullopt; + } else { + err_or_val.as() = make_error( + "Signal for retrieval of storage sent even though no " + "data is present"); + } +} + +template void one_time_conveyor_node::fire() { + if (parent_) { + parent_->child_has_fired(); + } +} + +} // namespace saw diff --git a/src/codec-json/.nix/derivation.nix b/src/codec-json/.nix/derivation.nix new file mode 100644 index 0000000..fcc276d --- /dev/null +++ b/src/codec-json/.nix/derivation.nix @@ -0,0 +1,34 @@ +{ lib +, stdenvNoCC +, scons +, clang +, clang-tools +, version +, forstio +, gnutls +}: + +let + +in stdenvNoCC.mkDerivation { + pname = "forstio-codec-json"; + inherit version; + + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang + clang-tools + ]; + + buildInputs = [ + forstio.core + forstio.async + forstio.codec + ]; + + outputs = ["out" "dev"]; +} diff --git a/src/codec-json/SConscript b/src/codec-json/SConscript new file mode 100644 index 0000000..772ac0b --- /dev/null +++ b/src/codec-json/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +codec_json_env = env.Clone(); + +codec_json_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +codec_json_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += codec_json_env.sources; +env.headers += codec_json_env.headers; + +## Shared lib +objects_shared = [] +codec_json_env.add_source_files(objects_shared, codec_json_env.sources, shared=True); +codec_json_env.library_shared = codec_json_env.SharedLibrary('#build/forstio-codec-json', [objects_shared]); + +## Static lib +objects_static = [] +codec_json_env.add_source_files(objects_static, codec_json_env.sources, shared=False); +codec_json_env.library_static = codec_json_env.StaticLibrary('#build/forstio-codec-json', [objects_static]); + +# Set Alias +env.Alias('library_codec_json', [codec_json_env.library_shared, codec_json_env.library_static]); + +env.targets += ['library_codec_json']; + +# Install +env.Install('$prefix/lib/', [codec_json_env.library_shared, codec_json_env.library_static]); +env.Install('$prefix/include/forstio/codec/json/', [codec_json_env.headers]); diff --git a/src/codec-json/SConstruct b/src/codec-json/SConstruct new file mode 100644 index 0000000..edd5f57 --- /dev/null +++ b/src/codec-json/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=['forstio-codec']) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/src/codec-json/json.h b/src/codec-json/json.h new file mode 100644 index 0000000..2c5b83e --- /dev/null +++ b/src/codec-json/json.h @@ -0,0 +1,12 @@ +#pragma once + +namespace saw { +namespace encoded { +struct Json {}; +} + +template +class codec { + +}; +} diff --git a/src/codec/.nix/derivation.nix b/src/codec/.nix/derivation.nix new file mode 100644 index 0000000..c9fac2e --- /dev/null +++ b/src/codec/.nix/derivation.nix @@ -0,0 +1,31 @@ +{ lib +, stdenvNoCC +, scons +, clang +, clang-tools +, version +, forstio +}: + +let + +in stdenvNoCC.mkDerivation { + pname = "forstio-codec"; + inherit version; + + src = ./..; + + enableParallelBuilding = true; + + buildInputs = [ + forstio.core + ]; + + nativeBuildInputs = [ + scons + clang + clang-tools + ]; + + outputs = ["out" "dev"]; +} diff --git a/src/codec/SConscript b/src/codec/SConscript new file mode 100644 index 0000000..c038d42 --- /dev/null +++ b/src/codec/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +codec_env = env.Clone(); + +codec_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +codec_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += codec_env.sources; +env.headers += codec_env.headers; + +## Shared lib +objects_shared = [] +codec_env.add_source_files(objects_shared, codec_env.sources, shared=True); +codec_env.library_shared = codec_env.SharedLibrary('#build/forstio-codec', [objects_shared]); + +## Static lib +objects_static = [] +codec_env.add_source_files(objects_static, codec_env.sources, shared=False); +codec_env.library_static = codec_env.StaticLibrary('#build/forstio-codec', [objects_static]); + +# Set Alias +env.Alias('library_codec', [codec_env.library_shared, codec_env.library_static]); + +env.targets += ['library_codec']; + +# Install +env.Install('$prefix/lib/', [codec_env.library_shared, codec_env.library_static]); +env.Install('$prefix/include/forstio/codec/', [codec_env.headers]); diff --git a/src/codec/SConstruct b/src/codec/SConstruct new file mode 100644 index 0000000..0d7b7c6 --- /dev/null +++ b/src/codec/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=['forstio-core']) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/src/codec/data.h b/src/codec/data.h new file mode 100644 index 0000000..1682ae7 --- /dev/null +++ b/src/codec/data.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include "schema.h" + +namespace saw { +namespace encode { +struct Native {}; +} +/* + * Helper for the basic message container, so the class doesn't have to be + * specialized 10 times. + */ +template struct native_data_type; + +template <> +struct native_data_type> { + using type = int8_t; +}; + +template <> +struct native_data_type> { + using type = int16_t; +}; + +template <> +struct native_data_type> { + using type = int32_t; +}; + +template <> +struct native_data_type> { + using type = int64_t; +}; + +template <> +struct native_data_type> { + using type = uint8_t; +}; + +template <> +struct native_data_type> { + using type = uint16_t; +}; + +template <> +struct native_data_type> { + using type = uint32_t; +}; + +template <> +struct native_data_type> { + using type = uint64_t; +}; + +template <> +struct native_data_type> { + using type = float; +}; + +template +class data { +private: + static_assert(always_false, "Type not supported"); +}; + +template<> +class data { +private: + std::string value_; +public: + SAW_FORBID_COPY(data); + + data(std::string&& value__):value_{std::move(value__)}{} + + std::size_t size() const { + return value_.size(); + } + + bool operator==(const data& data){ + return value_ == data.value_; + } +}; + +template +class data, encode::Native> { +private: +}; +} diff --git a/src/codec/proto_kel.h b/src/codec/proto_kel.h new file mode 100644 index 0000000..3b4ebac --- /dev/null +++ b/src/codec/proto_kel.h @@ -0,0 +1,41 @@ +#pragma once + +#include "data.h" + +#include + +namespace saw { +namespace encode { +struct ProtoKel {}; +} + +template +class data { +private: + own buffer_; +public: + data(own&& buffer__):buffer_{std::move(buffer__)}{} + + buffer& get_buffer(){ + return *buffer_; + } + + const buffer& get_buffer() const { + return *buffer_; + } +}; + +template +class codec { +private: +public: + error_or> decode(const data& encoded){ + return make_error(); + } + + error_or> encode(const data& native){ + return make_error(); + } +}; +} +} diff --git a/src/codec/schema.h b/src/codec/schema.h new file mode 100644 index 0000000..b23aaa1 --- /dev/null +++ b/src/codec/schema.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +namespace saw { +namespace schema { +// NOLINTBEGIN +template struct NamedMember {}; + +template struct Struct { + static_assert( + always_false, + "This schema template doesn't support this type of template argument"); +}; + +template +struct Struct...> {}; + +template struct Union { + static_assert( + always_false, + "This schema template doesn't support this type of template argument"); +}; + +template +struct Union...> {}; + +template struct Array {}; + +template FixedArray {}; + +template struct Tuple {}; + +struct String {}; + +struct SignedInteger {}; +struct UnsignedInteger {}; +struct FloatingPoint {}; + +template struct Primitive { + static_assert(((std::is_same_v || + std::is_same_v)&&(N == 1 || N == 2 || + N == 4 || N == 8)) || + (std::is_same_v && (N == 4 || N == 8)), + "Primitive Type is not supported"); +}; + +using Int8 = Primitive; +using Int16 = Primitive; +using Int32 = Primitive; +using Int64 = Primitive; + +using UInt8 = Primitive; +using UInt16 = Primitive; +using UInt32 = Primitive; +using UInt64 = Primitive; + +using Float32 = Primitive; +using Float64 = Primitive; + +/** + * Classes enabling Rpc calls + */ +template +struct Function {}; + +template struct Interface { + static_assert( + always_false, + "This schema template doesn't support this type of template argument"); +}; + +template +struct Interface...> {}; + +// NOLINTEND +} // namespace schema +} // namespace saw diff --git a/src/core/.nix/derivation.nix b/src/core/.nix/derivation.nix new file mode 100644 index 0000000..adf0cb4 --- /dev/null +++ b/src/core/.nix/derivation.nix @@ -0,0 +1,26 @@ +{ lib +, stdenvNoCC +, scons +, clang +, clang-tools +, version +}: + +let + +in stdenvNoCC.mkDerivation { + pname = "forstio-core"; + inherit version; + + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang + clang-tools + ]; + + outputs = ["out" "dev"]; +} diff --git a/src/core/SConscript b/src/core/SConscript new file mode 100644 index 0000000..04eb4c3 --- /dev/null +++ b/src/core/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +core_env = env.Clone(); + +core_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +core_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += core_env.sources; +env.headers += core_env.headers; + +## Shared lib +objects_shared = [] +core_env.add_source_files(objects_shared, core_env.sources, shared=True); +core_env.library_shared = core_env.SharedLibrary('#build/forstio-core', [objects_shared]); + +## Static lib +objects_static = [] +core_env.add_source_files(objects_static, core_env.sources, shared=False); +core_env.library_static = core_env.StaticLibrary('#build/forstio-core', [objects_static]); + +# Set Alias +env.Alias('library_core', [core_env.library_shared, core_env.library_static]); + +env.targets += ['library_core']; + +# Install +env.Install('$prefix/lib/', [core_env.library_shared, core_env.library_static]); +env.Install('$prefix/include/forstio/core/', [core_env.headers]); diff --git a/src/core/SConstruct b/src/core/SConstruct new file mode 100644 index 0000000..865d131 --- /dev/null +++ b/src/core/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=[]) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/src/core/buffer.cpp b/src/core/buffer.cpp new file mode 100644 index 0000000..ad471d7 --- /dev/null +++ b/src/core/buffer.cpp @@ -0,0 +1,434 @@ +#include "buffer.h" + +#include +#include +#include +#include +#include + +namespace saw { +error buffer::push(const uint8_t &value) { + size_t write_remain = write_composite_length(); + if (write_remain > 0) { + write() = value; + write_advance(1); + } else { + return make_error(); + } + return no_error(); +} + +error buffer::push(const uint8_t &buffer, size_t size) { + error error = write_require_length(size); + if (error.failed()) { + return error; + } + const uint8_t *buffer_ptr = &buffer; + while (size > 0) { + size_t segment = std::min(write_segment_length(), size); + memcpy(&write(), buffer_ptr, segment); + write_advance(segment); + size -= segment; + buffer_ptr += segment; + } + return no_error(); +} + +error buffer::pop(uint8_t &value) { + if (read_composite_length() > 0) { + value = read(); + read_advance(1); + } else { + return make_error(); + } + return no_error(); +} + +error buffer::pop(uint8_t &buffer, size_t size) { + if (read_composite_length() >= size) { + uint8_t *buffer_ptr = &buffer; + while (size > 0) { + size_t segment = std::min(read_segment_length(), size); + memcpy(buffer_ptr, &read(), segment); + read_advance(segment); + size -= segment; + buffer_ptr += segment; + } + } else { + return make_error(); + } + return no_error(); +} + +std::string buffer::to_string() const { + std::ostringstream oss; + for (size_t i = 0; i < read_composite_length(); ++i) { + oss << read(i); + } + return oss.str(); +} + +std::string buffer::to_hex() const { + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for (size_t i = 0; i < read_composite_length(); ++i) { + oss << std::setw(2) << (uint16_t)read(i); + if ((i + 1) < read_composite_length()) { + oss << ((i % 4 == 3) ? '\n' : ' '); + } + } + return oss.str(); +} + +buffer_view::buffer_view(buffer &buffer) + : buffer_{buffer}, read_offset_{0}, write_offset_{0} {} + +size_t buffer_view::read_position() const { + return read_offset_ + buffer_.read_position(); +} + +size_t buffer_view::read_composite_length() const { + assert(read_offset_ <= buffer_.read_composite_length()); + if (read_offset_ > buffer_.read_composite_length()) { + return 0; + } + + return buffer_.read_composite_length() - read_offset_; +} + +size_t buffer_view::read_segment_length(size_t offset) const { + size_t off = offset + read_offset_; + assert(off <= buffer_.read_composite_length()); + if (off > buffer_.read_composite_length()) { + return 0; + } + + return buffer_.read_segment_length(off); +} + +void buffer_view::read_advance(size_t bytes) { + size_t offset = bytes + read_offset_; + assert(offset <= buffer_.read_composite_length()); + if (offset > buffer_.read_composite_length()) { + read_offset_ += buffer_.read_composite_length(); + return; + } + + read_offset_ += bytes; +} + +uint8_t &buffer_view::read(size_t i) { + size_t pos = i + read_offset_; + + assert(pos < buffer_.read_composite_length()); + + return buffer_.read(pos); +} + +const uint8_t &buffer_view::read(size_t i) const { + size_t pos = i + read_offset_; + + assert(pos < buffer_.read_composite_length()); + + return buffer_.read(pos); +} + +size_t buffer_view::write_position() const { + return write_offset_ + buffer_.write_position(); +} + +size_t buffer_view::write_composite_length() const { + assert(write_offset_ <= buffer_.write_composite_length()); + if (write_offset_ > buffer_.write_composite_length()) { + return 0; + } + + return buffer_.write_composite_length() - write_offset_; +} + +size_t buffer_view::write_segment_length(size_t offset) const { + size_t off = offset + write_offset_; + assert(off <= buffer_.write_composite_length()); + if (off > buffer_.write_composite_length()) { + return 0; + } + + return buffer_.write_segment_length(off); +} + +void buffer_view::write_advance(size_t bytes) { + size_t offset = bytes + write_offset_; + assert(offset <= buffer_.write_composite_length()); + if (offset > buffer_.write_composite_length()) { + write_offset_ += buffer_.write_composite_length(); + return; + } + + write_offset_ += bytes; +} + +uint8_t &buffer_view::write(size_t i) { + size_t pos = i + write_offset_; + + assert(pos < buffer_.write_composite_length()); + + return buffer_.write(pos); +} + +const uint8_t &buffer_view::write(size_t i) const { + size_t pos = i + write_offset_; + + assert(pos < buffer_.write_composite_length()); + + return buffer_.write(pos); +} + +error buffer_view::write_require_length(size_t bytes) { + return buffer_.write_require_length(bytes + write_offset_); +} + +size_t buffer_view::read_offset() const { return read_offset_; } + +size_t buffer_view::write_offset() const { return write_offset_; } + +ring_buffer::ring_buffer() : read_position_{0}, write_position_{0} { + buffer_.resize(RING_BUFFER_MAX_SIZE); +} + +ring_buffer::ring_buffer(size_t size) : read_position_{0}, write_position_{0} { + buffer_.resize(size); +} + +size_t ring_buffer::read_position() const { return read_position_; } + +/* + * If write is ahead of read it is a simple distance, but if read ist ahead of + * write then there are two segments + * + */ +size_t ring_buffer::read_composite_length() const { + return write_position() < read_position() + ? buffer_.size() - (read_position() - write_position()) + : (write_reached_read_ ? buffer_.size() + : write_position() - read_position()); +} + +/* + * If write is ahead then it's the simple distance again. If read is ahead it's + * until the end of the buffer/segment + */ +size_t ring_buffer::read_segment_length(size_t offset) const { + size_t read_composite = read_composite_length(); + assert(offset <= read_composite); + offset = std::min(offset, read_composite); + size_t remaining = read_composite - offset; + + size_t read_offset = read_position() + offset; + read_offset = read_offset >= buffer_.size() ? read_offset - buffer_.size() + : read_offset; + + // case 1 write is located before read and reached read + // then offset can be used normally + // case 2 write is located at read, but read reached write + // then it is set to zero by readCompositeLength() + // case 3 write is located after read + // since std::min you can use simple subtraction + if (write_position() < read_offset) { + return buffer_.size() - read_offset; + } + + if (write_position() == read_offset) { + if (remaining > 0) { + return buffer_.size() - read_offset; + } else { + return 0; + } + } + + return write_position() - read_offset; +} + +void ring_buffer::read_advance(size_t bytes) { + size_t read_composite = read_composite_length(); + + assert(bytes <= read_composite); + bytes = std::min(bytes, read_composite); + size_t advanced = read_position_ + bytes; + read_position_ = advanced >= buffer_.size() ? advanced - buffer_.size() + : advanced; + write_reached_read_ = bytes > 0 ? false : write_reached_read_; +} + +uint8_t &ring_buffer::read(size_t i) { + assert(i < read_composite_length()); + size_t pos = read_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} + +const uint8_t &ring_buffer::read(size_t i) const { + assert(i < read_composite_length()); + size_t pos = read_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} + +size_t ring_buffer::write_position() const { return write_position_; } + +size_t ring_buffer::write_composite_length() const { + return read_position() > write_position() + ? (read_position() - write_position()) + : (write_reached_read_ + ? 0 + : buffer_.size() - (write_position() - read_position())); +} + +size_t ring_buffer::write_segment_length(size_t offset) const { + size_t write_composite = write_composite_length(); + assert(offset <= write_composite); + offset = std::min(offset, write_composite); + + size_t write_offset = write_position() + offset; + write_offset = write_offset >= buffer_.size() + ? write_offset - buffer_.size() + : write_offset; + + if (read_position_ > write_offset) { + return read_position_ - write_offset; + } + + if (write_reached_read_) { + return 0; + } + + return buffer_.size() - write_offset; +} + +void ring_buffer::write_advance(size_t bytes) { + assert(bytes <= write_composite_length()); + size_t advanced = write_position_ + bytes; + write_position_ = advanced >= buffer_.size() ? advanced - buffer_.size() + : advanced; + + write_reached_read_ = + (write_position_ == read_position_ && bytes > 0 ? true : false); +} + +uint8_t &ring_buffer::write(size_t i) { + assert(i < write_composite_length()); + size_t pos = write_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} + +const uint8_t &ring_buffer::write(size_t i) const { + assert(i < write_composite_length()); + size_t pos = write_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} +/* + Error RingBuffer::increaseSize(size_t size){ + size_t old_size = buffer.size(); + size_t new_size = old_size + size; + buffer.resize(new_size); + if(readPosition() > writePosition() || (readPosition() == + writePosition() && write_reached_read)){ size_t remaining = old_size - + writePosition(); size_t real_remaining = 0; while(remaining > 0){ size_t + segment = std::min(remaining, size); memcpy(&buffer[new_size-segment], + &buffer[old_size-segment], segment); remaining -= segment; size -= segment; + old_size -= segment; + new_size -= segment; + } + } + + return noError(); + } +*/ +error ring_buffer::write_require_length(size_t bytes) { + size_t write_remain = write_composite_length(); + if (bytes > write_remain) { + return make_error(); + } + return no_error(); +} + +array_buffer::array_buffer(size_t size) + : read_position_{0}, write_position_{0} { + buffer_.resize(size); +} + +size_t array_buffer::read_position() const { return read_position_; } + +size_t array_buffer::read_composite_length() const { + return write_position_ - read_position_; +} + +size_t array_buffer::read_segment_length(size_t offset) const { + size_t read_composite = read_composite_length(); + assert(offset <= read_composite); + + offset = std::min(read_composite, offset); + size_t read_offset = read_position_ + offset; + + return write_position_ - read_offset; +} + +void array_buffer::read_advance(size_t bytes) { + assert(bytes <= read_composite_length()); + read_position_ += bytes; +} + +uint8_t &array_buffer::read(size_t i) { + assert(i < read_composite_length()); + + return buffer_[i + read_position_]; +} + +const uint8_t &array_buffer::read(size_t i) const { + assert(i + read_position_ < buffer_.size()); + + return buffer_[i + read_position_]; +} + +size_t array_buffer::write_position() const { return write_position_; } + +size_t array_buffer::write_composite_length() const { + assert(write_position_ <= buffer_.size()); + return buffer_.size() - write_position_; +} + +size_t array_buffer::write_segment_length(size_t offset) const { + assert(write_position_ <= buffer_.size()); + size_t write_composite = write_composite_length(); + + assert(offset <= write_composite); + offset = std::min(write_composite, offset); + size_t write_offset = write_position_ + offset; + + return buffer_.size() - write_offset; +} + +void array_buffer::write_advance(size_t bytes) { + assert(bytes <= write_composite_length()); + write_position_ += bytes; +} + +uint8_t &array_buffer::write(size_t i) { + assert(i < write_composite_length()); + return buffer_[i + write_position_]; +} + +const uint8_t &array_buffer::write(size_t i) const { + assert(i < write_composite_length()); + return buffer_[i + write_position_]; +} +error array_buffer::write_require_length(size_t bytes) { + size_t write_remain = write_composite_length(); + if (bytes > write_remain) { + return make_error(); + } + return no_error(); +} + +} // namespace saw diff --git a/src/core/buffer.h b/src/core/buffer.h new file mode 100644 index 0000000..4485ff1 --- /dev/null +++ b/src/core/buffer.h @@ -0,0 +1,195 @@ +#pragma once + +#include "error.h" + +#include +#include +#include +#include +#include +#include + +namespace saw { +/* + * Access class to reduce templated BufferSegments bloat + */ +class buffer { +protected: + ~buffer() = default; + +public: + virtual size_t read_position() const = 0; + virtual size_t read_composite_length() const = 0; + virtual size_t read_segment_length(size_t offset = 0) const = 0; + virtual void read_advance(size_t bytes) = 0; + + virtual uint8_t &read(size_t i = 0) = 0; + virtual const uint8_t &read(size_t i = 0) const = 0; + + virtual size_t write_position() const = 0; + virtual size_t write_composite_length() const = 0; + virtual size_t write_segment_length(size_t offset = 0) const = 0; + virtual void write_advance(size_t bytes) = 0; + + virtual uint8_t &write(size_t i = 0) = 0; + virtual const uint8_t &write(size_t i = 0) const = 0; + + /* + * Sometime buffers need to grow with a little more control + * than with push and pop for more efficient calls. + * There is nothing you can do if read hasn't been filled, but at + * least write can be increased if it is demanded. + */ + virtual error write_require_length(size_t bytes) = 0; + + error push(const uint8_t &value); + error push(const uint8_t &buffer, size_t size); + error pop(uint8_t &value); + error pop(uint8_t &buffer, size_t size); + + /* + * Subject to change + */ + std::string to_string() const; + std::string to_hex() const; +}; + +/* + * A viewer class for buffers. + * Working on the reference buffer invalidates the buffer view + */ +class buffer_view : public buffer { +private: + buffer &buffer_; + size_t read_offset_; + size_t write_offset_; + +public: + buffer_view(buffer &); + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; + + size_t read_offset() const; + size_t write_offset() const; +}; + +/* + * Buffer size meant for default allocation size of the ringbuffer since + * this class currently doesn't support proper resizing + */ +constexpr size_t RING_BUFFER_MAX_SIZE = 4096; +/* + * Buffer wrapping around if read caught up + */ +class ring_buffer final : public buffer { +private: + std::vector buffer_; + size_t read_position_; + size_t write_position_; + bool write_reached_read_ = false; + +public: + ring_buffer(); + ring_buffer(size_t size); + + inline size_t size() const { return buffer_.size(); } + + inline uint8_t &operator[](size_t i) { return buffer_[i]; } + inline const uint8_t &operator[](size_t i) const { return buffer_[i]; } + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; +}; + +/* + * One time buffer + */ +class array_buffer : public buffer { +private: + std::vector buffer_; + + size_t read_position_; + size_t write_position_; + +public: + array_buffer(size_t size); + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; +}; + +class chain_array_buffer : public buffer { +private: + std::deque buffer_; + + size_t read_position_; + size_t write_position_; + +public: + chain_array_buffer(); + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; +}; +} // namespace saw diff --git a/src/core/common.h b/src/core/common.h new file mode 100644 index 0000000..a06c238 --- /dev/null +++ b/src/core/common.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include + +namespace saw { + +#define SAW_CONCAT_(x, y) x##y +#define SAW_CONCAT(x, y) SAW_CONCAT_(x, y) +#define SAW_UNIQUE_NAME(prefix) SAW_CONCAT(prefix, __LINE__) + +#define SAW_FORBID_COPY(classname) \ + classname(const classname &) = delete; \ + classname &operator=(const classname &) = delete + +#define SAW_FORBID_MOVE(classname) \ + classname(classname &&) = delete; \ + classname &operator=(classname &&) = delete + +#define SAW_DEFAULT_COPY(classname) \ + classname(const classname &) = default; \ + classname &operator=(const classname &) = default + +#define SAW_DEFAULT_MOVE(classname) \ + classname(classname &&) = default; \ + classname &operator=(classname &&) = default + +// In case of C++20 +#define SAW_ASSERT(expression) \ + assert(expression); \ + if (!(expression)) [[unlikely]] + +template using maybe = std::optional; + +template using own = std::unique_ptr; + +template using our = std::shared_ptr; + +template using lent = std::weak_ptr; + +template own heap(Args &&...args) { + return own(new T(std::forward(args)...)); +} + +template our share(Args &&...args) { + return std::make_shared(std::forward(args)...); +} + +template T instance() noexcept; + +template struct return_type_helper { + typedef decltype(instance()(instance())) Type; +}; +template struct return_type_helper { + typedef decltype(instance()()) Type; +}; + +template +using return_type = typename return_type_helper::Type; + +struct void_t {}; + +template struct void_fix { typedef T Type; }; +template <> struct void_fix { typedef void_t Type; }; +template using fix_void = typename void_fix::Type; + +template struct void_unfix { typedef T Type; }; +template <> struct void_unfix { typedef void Type; }; +template using unfix_void = typename void_unfix::Type; + +template constexpr bool always_false = false; + +} // namespace saw diff --git a/src/core/error.cpp b/src/core/error.cpp new file mode 100644 index 0000000..727ca95 --- /dev/null +++ b/src/core/error.cpp @@ -0,0 +1,121 @@ +#include "error.h" + +namespace saw { +error::error(error::code code_, bool is_critical__) + : error_code_{static_cast(code_)}, is_critical_{is_critical__} {} + +error::error(error::code code_, bool is_critical__, const std::string_view &msg) + : + error_code_{static_cast(code_)} + , is_critical_{is_critical__}, error_message_{msg}{} + +error::error(error &&error) + : + error_code_{std::move(error.error_code_)} + , is_critical_{std::move(error.is_critical_)} + , error_message_{std::move(error.error_message_)}{} + +const std::string_view error::message() const { + + return std::visit( + [this](auto &&arg) -> const std::string_view { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + return std::string_view{arg}; + } else if constexpr (std::is_same_v) { + return arg; + } else { + return "Error in class Error. Good luck :)"; + } + }, + error_message_); +} + +bool error::failed() const { + return this->is_error(); +} + +bool error::is_critical() const { + return is_critical_; +} + +bool error::is_recoverable() const { + return !is_critical_; +} + +error error::copy_error() const { + auto copy_error_code = error_code_; + error error{copy_error_code, is_critical_}; + + try { + error.error_message_ = error_message_; + } catch (const std::bad_alloc &) { + error.error_message_ = + std::string_view{"Error while copying Error string. Out of memory"}; + } + + return error; +} + +error::code error::get_id() const { return error_code_; } + +namespace impl { +error_registry& get_error_registry() { + static own reg = nullptr; + if(!reg){ + reg = heap(); + } + + assert(reg); + return *reg; +} +} + +error no_error(){ + return make_error(); +} + +namespace impl { +error_or error_registry::search_id(const std::string_view& desc)const{ + /** + * Search the index in the vector + */ + size_t i{}; + size_t info_max_size = std::min(infos.size(), std::numeric_limits::max()); + for(i = 0; i < info_max_size; ++i){ + if(infos.at(i).description == desc){ + break; + } + } + + if(i == info_max_size){ + return make_error(); + } + + return static_cast(i); +} + +error_or error_registry::search_or_register_id(const std::string_view& desc, bool is_critical){ + auto err_or_id = search_id(desc); + + if(err_or_id.is_value()){ + return err_or_id.get_value(); + } + + auto& err = err_or_id.get_error(); + + if(err.is_error()){ + size_t new_index = infos.size(); + if(new_index == std::numeric_limits::max()){ + return make_error("Error registry ids are exhausted"); + } + infos.emplace_back(error_info{desc, is_critical}); + return static_cast(new_index); + } + + return std::move(err); +} +} + +} // namespace saw diff --git a/src/core/error.h b/src/core/error.h new file mode 100644 index 0000000..3d242b9 --- /dev/null +++ b/src/core/error.h @@ -0,0 +1,233 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" + +namespace saw { +/** + * Utility class for generating errors. Has a base distinction between + * critical and recoverable errors. Additional code ids can be provided to the + * constructor if additional distinctions are necessary. + */ +class error { +public: + using code = uint32_t; +private: + code error_code_; + bool is_critical_; + std::variant error_message_; + +public: + error(error::code id, bool is_critical); + error(error::code id, bool is_critical, const std::string_view &msg); + error(error &&error); + + SAW_FORBID_COPY(error); + + error &operator=(error &&) = default; + + const std::string_view message() const; + bool failed() const; + + bool is_critical() const; + bool is_recoverable() const; + + /** + * Replaces the copy constructor. We need this since we want to explicitly copy and not implicitly + */ + error copy_error() const; + + code get_id() const; + + template + bool is_error() const; +}; + +template +class error_or; + +namespace impl { + +class error_registry { +private: + struct error_info { + error_info() = delete; + error_info(const std::string_view& d_, bool critical_):description{d_}, is_critical{critical_}{} + + std::string_view description; + bool is_critical; + }; + + std::vector infos; +public: + error_or search_id(const std::string_view& desc) const; + + error_or search_or_register_id(const std::string_view& desc, bool is_critical); +}; + +error_registry& get_error_registry(); + +template +error::code get_template_id(){ + static error::code id = std::numeric_limits::max(); + + if(id == std::numeric_limits::max()){ + auto& reg = get_error_registry(); + + auto err_or_id = reg.search_or_register_id(T::description, T::is_critical); + if(err_or_id.is_error()){ + return std::numeric_limits::max(); + } + + id = err_or_id.get_value(); + } + + return id; +} +} + +template error make_error(const std::string_view& generic){ + error::code id = impl::get_template_id(); + + return error{id, T::is_critical, generic}; +} + +template error make_error(){ + error::code id = impl::get_template_id(); + + return error{id, T::is_critical}; +} + +error make_error(error::code code, const std::string_view &generic); + + +namespace err { +struct no_error { + static constexpr std::string_view description = "No error has occured"; + static constexpr bool is_critical = false; +}; + +struct recoverable { + static constexpr std::string_view description = "No error has occured"; + static constexpr bool is_critical = false; +}; + +struct critical { + static constexpr std::string_view description = "No error has occured"; + static constexpr bool is_critical = true; +}; + +struct buffer_exhausted { + static constexpr std::string_view description = "Buffer is too small"; + static constexpr bool is_critical = false; +}; + +struct not_found { + static constexpr std::string_view description = "Not found"; + static constexpr bool is_critical = false; +}; + +struct out_of_memory { + static constexpr std::string_view description = "Out of memory"; + static constexpr bool is_critical = true; +}; + +struct invalid_state { + static constexpr std::string_view description = "Invalid state"; + static constexpr bool is_critical = true; +}; + +struct not_supported { + static constexpr std::string_view description = "Not supported"; + static constexpr bool is_critical = false; +}; + +struct not_implemented { + static constexpr std::string_view description = "Not implemented"; + static constexpr bool is_critical = true; +}; +} + +/** + * Shorthand for no error happened + */ +error no_error(); + +/** + * Exception alternative. Since I code without exceptions this class is + * essentially a kind of exception replacement. + */ +template class error_or; + +class error_or_value { +public: + virtual ~error_or_value() = default; + + template error_or> &as() { + return static_cast> &>(*this); + } + + template const error_or> &as() const { + return static_cast> &>(*this); + } +}; + +template class error_or final : public error_or_value { +private: + std::variant> value_or_error_; + + static_assert(!std::is_same_v, + "Don't use internal private types"); + +public: + error_or():value_or_error_{fix_void{}}{} + error_or(const fix_void &value) : value_or_error_{value} {} + + error_or(fix_void &&value) : value_or_error_{std::move(value)} {} + + error_or(const error &error) : value_or_error_{error} {} + error_or(error &&error) : value_or_error_{std::move(error)} {} + + bool is_value() const { + return std::holds_alternative>(value_or_error_); + } + + bool is_error() const { + return std::holds_alternative(value_or_error_); + } + + class error &get_error() { + return std::get(value_or_error_); + } + + const class error &get_error() const { + return std::get(value_or_error_); + } + + fix_void &get_value() { return std::get>(value_or_error_); } + + const fix_void &get_value() const { + return std::get>(value_or_error_); + } +}; + +template class error_or> { +private: + error_or() = delete; +}; + +template +bool error::is_error() const { + + return error_code_ == impl::get_template_id(); +} + +} // namespace saw diff --git a/src/core/string_literal.h b/src/core/string_literal.h new file mode 100644 index 0000000..d530a54 --- /dev/null +++ b/src/core/string_literal.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace saw { +/** + * Helper object which creates a templated string from the provided string + * literal. It guarantees compile time uniqueness and thus allows using strings + * in template parameters. + */ +template class string_literal { +public: + constexpr string_literal(const CharT (&input)[N]) noexcept { + for (size_t i = 0; i < N; ++i) { + data[i] = input[i]; + } + } + + std::array data{}; + + constexpr std::string_view view() const noexcept { + return std::string_view{data.data()}; + } + + constexpr bool + operator==(const string_literal &) const noexcept = default; + + template + constexpr bool + operator==(const string_literal &) const noexcept { + return false; + } +}; + +template +constexpr string_literal operator""_key() { + return string_literal{Chars..., '\0'}; +} +} // namespace saw diff --git a/src/io-tls/.nix/derivation.nix b/src/io-tls/.nix/derivation.nix new file mode 100644 index 0000000..6c62b51 --- /dev/null +++ b/src/io-tls/.nix/derivation.nix @@ -0,0 +1,35 @@ +{ lib +, stdenvNoCC +, scons +, clang +, clang-tools +, version +, forstio +, gnutls +}: + +let + +in stdenvNoCC.mkDerivation { + pname = "forstio-io-tls"; + inherit version; + + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang + clang-tools + ]; + + buildInputs = [ + forstio.core + forstio.async + forstio.io + gnutls + ]; + + outputs = ["out" "dev"]; +} diff --git a/src/io-tls/SConscript b/src/io-tls/SConscript new file mode 100644 index 0000000..4f88f37 --- /dev/null +++ b/src/io-tls/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +io_tls_env = env.Clone(); + +io_tls_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +io_tls_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += io_tls_env.sources; +env.headers += io_tls_env.headers; + +## Shared lib +objects_shared = [] +io_tls_env.add_source_files(objects_shared, io_tls_env.sources, shared=True); +io_tls_env.library_shared = io_tls_env.SharedLibrary('#build/forstio-io-tls', [objects_shared]); + +## Static lib +objects_static = [] +io_tls_env.add_source_files(objects_static, io_tls_env.sources, shared=False); +io_tls_env.library_static = io_tls_env.StaticLibrary('#build/forstio-io-tls', [objects_static]); + +# Set Alias +env.Alias('library_io_tls', [io_tls_env.library_shared, io_tls_env.library_static]); + +env.targets += ['library_io_tls']; + +# Install +env.Install('$prefix/lib/', [io_tls_env.library_shared, io_tls_env.library_static]); +env.Install('$prefix/include/forstio/io/tls/', [io_tls_env.headers]); diff --git a/src/io-tls/SConstruct b/src/io-tls/SConstruct new file mode 100644 index 0000000..fbd8657 --- /dev/null +++ b/src/io-tls/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=['gnutls','forstio-io']) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/src/io-tls/tls.cpp b/src/io-tls/tls.cpp new file mode 100644 index 0000000..9fa143c --- /dev/null +++ b/src/io-tls/tls.cpp @@ -0,0 +1,252 @@ +#include "tls.h" + +#include +#include + +#include + +#include + +#include + +namespace saw { + +class tls::impl { +public: + gnutls_certificate_credentials_t xcred; + +public: + impl() { + gnutls_global_init(); + gnutls_certificate_allocate_credentials(&xcred); + gnutls_certificate_set_x509_system_trust(xcred); + } + + ~impl() { + gnutls_certificate_free_credentials(xcred); + gnutls_global_deinit(); + } +}; + +static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data, + size_t size); +static ssize_t forst_tls_pull_func(gnutls_transport_ptr_t p, void *data, size_t size); + +tls::tls() : impl_{heap()} {} + +tls::~tls() {} + +tls::impl &tls::get_impl() { return *impl_; } + +class tls_io_stream final : public io_stream { +private: + own internal; + gnutls_session_t session_handle; + +public: + tls_io_stream(own internal_) : internal{std::move(internal_)} {} + + ~tls_io_stream() { gnutls_bye(session_handle, GNUTLS_SHUT_RDWR); } + + error_or read(void *buffer, size_t length) override { + ssize_t size = gnutls_record_recv(session_handle, buffer, length); + if (size < 0) { + if(gnutls_error_is_fatal(size) == 0){ + return make_error("Recoverable error on read in gnutls. TODO better error msg handling"); + // Leaving proper message handling done in previous error framework + //return recoverable_error([size](){return std::string{"Read recoverable Error "}+std::string{gnutls_strerror(size)};}, "Error read r"); + }else{ + return make_error("Fatal error on read in gnutls. TODO better error msg handling"); + } + }else if(size == 0){ + return make_error(); + } + + return static_cast(length); + } + + conveyor read_ready() override { return internal->read_ready(); } + + conveyor on_read_disconnected() override { + return internal->on_read_disconnected(); + } + + error_or write(const void *buffer, size_t length) override { + ssize_t size = gnutls_record_send(session_handle, buffer, length); + if(size < 0){ + if(gnutls_error_is_fatal(size) == 0){ + return make_error("Recoverable error on write in gnutls. TODO better error msg handling"); + }else{ + return make_error("Fatal error on write in gnutls. TODO better error msg handling"); + } + } + + return static_cast(size); + } + + conveyor write_ready() override { return internal->write_ready(); } + + gnutls_session_t &session() { return session_handle; } +}; + +tls_server::tls_server(own srv) : internal{std::move(srv)} {} + +conveyor> tls_server::accept() { + SAW_ASSERT(internal) { return conveyor>{fix_void>{nullptr}}; } + return internal->accept().then([](own stream) -> own { + /// @todo handshake + + + return heap(std::move(stream)); + }); +} + +namespace { +/* +* Small helper for setting up the nonblocking connection handshake +*/ +struct tls_client_stream_helper { +public: + own>> feeder; + conveyor_sink connection_sink; + conveyor_sink stream_reader; + conveyor_sink stream_writer; + + own stream = nullptr; +public: + tls_client_stream_helper(own>> f): + feeder{std::move(f)} + {} + + void setupTurn(){ + SAW_ASSERT(stream){ + return; + } + + stream_reader = stream->read_ready().then([this](){ + turn(); + }).sink(); + + stream_writer = stream->write_ready().then([this](){ + turn(); + }).sink(); + } + + void turn(){ + if(stream){ + // Guarantee that the receiving end is already setup + SAW_ASSERT(feeder){ + return; + } + + auto &session = stream->session(); + + int ret; + do { + ret = gnutls_handshake(session); + } while ( (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) && gnutls_error_is_fatal(ret) == 0); + + if(gnutls_error_is_fatal(ret)){ + feeder->fail(make_error("Couldn't create Tls connection")); + stream = nullptr; + }else if(ret == GNUTLS_E_SUCCESS){ + feeder->feed(std::move(stream)); + } + } + } +}; +} + +own tls_network::listen(network_address& address) { + return heap(internal.listen(address)); +} + +conveyor> tls_network::connect(network_address& address) { + // Helper setups + auto caf = new_conveyor_and_feeder>(); + own helper = heap(std::move(caf.feeder)); + tls_client_stream_helper* hlp_ptr = helper.get(); + + // Conveyor entangled structure + auto prim_conv = internal.connect(address).then([this, hlp_ptr, addr = address.address()]( + own stream) -> error_or { + io_stream* inner_stream = stream.get(); + auto tls_stream = heap(std::move(stream)); + + auto &session = tls_stream->session(); + + gnutls_init(&session, GNUTLS_CLIENT); + + gnutls_server_name_set(session, GNUTLS_NAME_DNS, addr.c_str(), + addr.size()); + + gnutls_set_default_priority(session); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + tls_.get_impl().xcred); + gnutls_session_set_verify_cert(session, addr.c_str(), 0); + + gnutls_transport_set_ptr(session, reinterpret_cast(inner_stream)); + gnutls_transport_set_push_function(session, forst_tls_push_func); + gnutls_transport_set_pull_function(session, forst_tls_pull_func); + + // gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + hlp_ptr->stream = std::move(tls_stream); + hlp_ptr->setupTurn(); + hlp_ptr->turn(); + + return void_t{}; + }); + + helper->connection_sink = prim_conv.sink(); + + return caf.conveyor.attach(std::move(helper)); +} + +own tls_network::datagram(network_address& address){ + ///@unimplemented + return nullptr; +} + +static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data, + size_t size) { + io_stream *stream = reinterpret_cast(p); + if (!stream) { + return -1; + } + + error_or length = stream->write(data, size); + if (length.is_error() || !length.is_value()) { + return -1; + } + + return static_cast(length.get_value()); +} + +static ssize_t forst_tls_pull_func(gnutls_transport_ptr_t p, void *data, size_t size) { + io_stream *stream = reinterpret_cast(p); + if (!stream) { + return -1; + } + + error_or length = stream->read(data, size); + if (length.is_error() || !length.is_value()) { + return -1; + } + + return static_cast(length.get_value()); +} + +tls_network::tls_network(tls& tls_, network &network) : tls_{tls_},internal{network} {} + +conveyor> tls_network::resolve_address(const std::string &addr, + uint16_t port) { + /// @todo tls server name needed. Check validity. Won't matter later on, because gnutls should fail anyway. But + /// it's better to find the error source sooner rather than later + return internal.resolve_address(addr, port); +} + +std::optional> setup_tls_network(network &network) { + return std::nullopt; +} +} // namespace saw diff --git a/src/io-tls/tls.h b/src/io-tls/tls.h new file mode 100644 index 0000000..74b39ff --- /dev/null +++ b/src/io-tls/tls.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include + +#include +#include + +namespace saw { +class tls; + +class tls_server final : public server { +private: + own internal; + +public: + tls_server(own srv); + + conveyor> accept() override; +}; + +class tls_network final : public network { +private: + tls& tls_; + network &internal; +public: + tls_network(tls& tls_, network &network_); + + conveyor> resolve_address(const std::string &addr, uint16_t port = 0) override; + + own listen(network_address& address) override; + + conveyor> connect(network_address& address) override; + + own datagram(network_address& address) override; +}; + +/** +* tls context class. +* Provides tls network class which ensures the usage of tls encrypted connections +*/ +class tls { +private: + class impl; + own impl_; +public: + tls(); + ~tls(); + + struct version { + struct tls_1_0{}; + struct tls_1_1{}; + struct tls_1_2{}; + }; + + struct options { + public: + version version; + }; + + impl &get_impl(); +private: + options options_; +}; + +std::optional> setup_tls_network(network &network); + +} // namespace saw diff --git a/src/io/.nix/derivation.nix b/src/io/.nix/derivation.nix new file mode 100644 index 0000000..0d213d3 --- /dev/null +++ b/src/io/.nix/derivation.nix @@ -0,0 +1,32 @@ +{ lib +, stdenvNoCC +, scons +, clang +, clang-tools +, version +, forstio +}: + +let + +in stdenvNoCC.mkDerivation { + pname = "forstio-io"; + inherit version; + + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang + clang-tools + ]; + + buildInputs = [ + forstio.core + forstio.async + ]; + + outputs = ["out" "dev"]; +} diff --git a/src/io/SConscript b/src/io/SConscript new file mode 100644 index 0000000..62ad58a --- /dev/null +++ b/src/io/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +io_env = env.Clone(); + +io_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +io_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += io_env.sources; +env.headers += io_env.headers; + +## Shared lib +objects_shared = [] +io_env.add_source_files(objects_shared, io_env.sources, shared=True); +io_env.library_shared = io_env.SharedLibrary('#build/forstio-io', [objects_shared]); + +## Static lib +objects_static = [] +io_env.add_source_files(objects_static, io_env.sources, shared=False); +io_env.library_static = io_env.StaticLibrary('#build/forstio-io', [objects_static]); + +# Set Alias +env.Alias('library_io', [io_env.library_shared, io_env.library_static]); + +env.targets += ['library_io']; + +# Install +env.Install('$prefix/lib/', [io_env.library_shared, io_env.library_static]); +env.Install('$prefix/include/forstio/io/', [io_env.headers]); diff --git a/src/io/SConstruct b/src/io/SConstruct new file mode 100644 index 0000000..4cccf82 --- /dev/null +++ b/src/io/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=['forstio-async']) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/src/io/io.cpp b/src/io/io.cpp new file mode 100644 index 0000000..f0705d2 --- /dev/null +++ b/src/io/io.cpp @@ -0,0 +1,70 @@ +#include "io.h" + +#include + +namespace saw { + +async_io_stream::async_io_stream(own str) + : stream_{std::move(str)}, + read_ready_{stream_->read_ready() + .then([this]() { read_stepper_.read_step(*stream_); }) + .sink()}, + write_ready_{stream_->write_ready() + .then([this]() { write_stepper_.write_step(*stream_); }) + .sink()}, + read_disconnected_{stream_->on_read_disconnected() + .then([this]() { + if (read_stepper_.on_read_disconnect) { + read_stepper_.on_read_disconnect->feed(); + } + }) + .sink()} {} + +void async_io_stream::read(void *buffer, size_t min_length, size_t max_length) { + SAW_ASSERT(buffer && max_length >= min_length && min_length > 0) { return; } + + SAW_ASSERT(!read_stepper_.read_task.has_value()) { return; } + + read_stepper_.read_task = read_task_and_step_helper::read_io_task{ + buffer, min_length, max_length, 0}; + read_stepper_.read_step(*stream_); +} + +conveyor async_io_stream::read_done() { + auto caf = new_conveyor_and_feeder(); + read_stepper_.read_done = std::move(caf.feeder); + return std::move(caf.conveyor); +} + +conveyor async_io_stream::on_read_disconnected() { + auto caf = new_conveyor_and_feeder(); + read_stepper_.on_read_disconnect = std::move(caf.feeder); + return std::move(caf.conveyor); +} + +void async_io_stream::write(const void *buffer, size_t length) { + SAW_ASSERT(buffer && length > 0) { return; } + + SAW_ASSERT(!write_stepper_.write_task.has_value()) { return; } + + write_stepper_.write_task = + write_task_and_step_helper::write_io_task{buffer, length, 0}; + write_stepper_.write_step(*stream_); +} + +conveyor async_io_stream::write_done() { + auto caf = new_conveyor_and_feeder(); + write_stepper_.write_done = std::move(caf.feeder); + return std::move(caf.conveyor); +} + +string_network_address::string_network_address(const std::string &address, + uint16_t port) + : address_value_{address}, port_value_{port} {} + +const std::string &string_network_address::address() const { + return address_value_; +} + +uint16_t string_network_address::port() const { return port_value_; } +} // namespace saw diff --git a/src/io/io.h b/src/io/io.h new file mode 100644 index 0000000..bcc59fd --- /dev/null +++ b/src/io/io.h @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +#include "io_helpers.h" + +#include +#include + +namespace saw { +/** + * Set of error common in io + */ +namespace err { +struct disconnected { + static constexpr std::string_view description = "Disconnected"; + static constexpr bool is_critical = true; +}; +} +/* + * Input stream + */ +class input_stream { +public: + virtual ~input_stream() = default; + + virtual error_or read(void *buffer, size_t length) = 0; + + virtual conveyor read_ready() = 0; + + virtual conveyor on_read_disconnected() = 0; +}; + +/* + * Output stream + */ +class output_stream { +public: + virtual ~output_stream() = default; + + virtual error_or write(const void *buffer, size_t length) = 0; + + virtual conveyor write_ready() = 0; +}; + +/* + * Io stream + */ +class io_stream : public input_stream, public output_stream { +public: + virtual ~io_stream() = default; +}; + +class async_input_stream { +public: + virtual ~async_input_stream() = default; + + virtual void read(void *buffer, size_t min_length, size_t max_length) = 0; + + virtual conveyor read_done() = 0; + virtual conveyor on_read_disconnected() = 0; +}; + +class async_output_stream { +public: + virtual ~async_output_stream() = default; + + virtual void write(const void *buffer, size_t length) = 0; + + virtual conveyor write_done() = 0; +}; + +class async_io_stream final : public async_input_stream, + public async_output_stream { +private: + own stream_; + + conveyor_sink read_ready_; + conveyor_sink write_ready_; + conveyor_sink read_disconnected_; + + read_task_and_step_helper read_stepper_; + write_task_and_step_helper write_stepper_; + +public: + async_io_stream(own str); + + SAW_FORBID_COPY(async_io_stream); + SAW_FORBID_MOVE(async_io_stream); + + void read(void *buffer, size_t length, size_t max_length) override; + + conveyor read_done() override; + + conveyor on_read_disconnected() override; + + void write(const void *buffer, size_t length) override; + + conveyor write_done() override; +}; + +class server { +public: + virtual ~server() = default; + + virtual conveyor> accept() = 0; +}; + +class network_address; +/** + * Datagram class. Bound to a local address it is able to receive inbound + * datagram messages and send them as well as long as an address is provided as + * well + */ +class datagram { +public: + virtual ~datagram() = default; + + virtual error_or read(void *buffer, size_t length) = 0; + virtual conveyor read_ready() = 0; + + virtual error_or write(const void *buffer, size_t length, + network_address &dest) = 0; + virtual conveyor write_ready() = 0; +}; + +class os_network_address; +class string_network_address; + +class network_address { +public: + using child_variant = + std::variant; + + virtual ~network_address() = default; + + virtual network_address::child_variant representation() = 0; + + virtual const std::string &address() const = 0; + virtual uint16_t port() const = 0; +}; + +class os_network_address : public network_address { +public: + virtual ~os_network_address() = default; + + network_address::child_variant representation() override { return this; } +}; + +class string_network_address final : public network_address { +private: + std::string address_value_; + uint16_t port_value_; + +public: + string_network_address(const std::string &address, uint16_t port); + + const std::string &address() const override; + uint16_t port() const override; + + network_address::child_variant representation() override { return this; } +}; + +class network { +public: + virtual ~network() = default; + + /** + * Resolve the provided string and uint16 to the preferred storage method + */ + virtual conveyor> + resolve_address(const std::string &addr, uint16_t port_hint = 0) = 0; + + /** + * Parse the provided string and uint16 to the preferred storage method + * Since no dns request is made here, no async conveyors have to be used. + */ + /// @todo implement + // virtual Own parseAddress(const std::string& addr, + // uint16_t port_hint = 0) = 0; + + /** + * Set up a listener on this address + */ + virtual own listen(network_address &bind_addr) = 0; + + /** + * Connect to a remote address + */ + virtual conveyor> connect(network_address &address) = 0; + + /** + * Bind a datagram socket at this address. + */ + virtual own datagram(network_address &address) = 0; +}; + +class io_provider { +public: + virtual ~io_provider() = default; + + virtual own wrap_input_fd(int fd) = 0; + + virtual network &network() = 0; +}; + +struct async_io_context { + own io; + event_loop &event_loop; + event_port &event_port; +}; + +error_or setup_async_io(); +} // namespace saw diff --git a/src/io/io_helpers.cpp b/src/io/io_helpers.cpp new file mode 100644 index 0000000..c2cf2be --- /dev/null +++ b/src/io/io_helpers.cpp @@ -0,0 +1,85 @@ +#include "io_helpers.h" + +#include "io.h" + +#include + +namespace saw { +void read_task_and_step_helper::read_step(input_stream &reader) { + while (read_task.has_value()) { + read_io_task &task = *read_task; + + error_or n_err = reader.read(task.buffer, task.max_length); + if (n_err.is_error()) { + const error &error = n_err.get_error(); + if (error.is_critical()) { + if (read_done) { + read_done->fail(error.copy_error()); + } + read_task = std::nullopt; + } + + break; + } else if (n_err.is_value()) { + size_t n = n_err.get_value(); + if (static_cast(n) >= task.min_length && + static_cast(n) <= task.max_length) { + if (read_done) { + read_done->feed(n + task.already_read); + } + read_task = std::nullopt; + } else { + task.buffer = static_cast(task.buffer) + n; + task.min_length -= static_cast(n); + task.max_length -= static_cast(n); + task.already_read += n; + } + + } else { + if (read_done) { + read_done->fail(make_error("Read failed")); + } + read_task = std::nullopt; + } + } +} + +void write_task_and_step_helper::write_step(output_stream &writer) { + while (write_task.has_value()) { + write_io_task &task = *write_task; + + error_or n_err = writer.write(task.buffer, task.length); + + if (n_err.is_value()) { + + size_t n = n_err.get_value(); + assert(n <= task.length); + if (n == task.length) { + if (write_done) { + write_done->feed(n + task.already_written); + } + write_task = std::nullopt; + } else { + task.buffer = static_cast(task.buffer) + n; + task.length -= n; + task.already_written += n; + } + } else if (n_err.is_error()) { + const error &error = n_err.get_error(); + if (error.is_critical()) { + if (write_done) { + write_done->fail(error.copy_error()); + } + write_task = std::nullopt; + } + break; + } else { + if (write_done) { + write_done->fail(make_error("Write failed")); + } + write_task = std::nullopt; + } + } +} + +} // namespace saw diff --git a/src/io/io_helpers.h b/src/io/io_helpers.h new file mode 100644 index 0000000..94e37f4 --- /dev/null +++ b/src/io/io_helpers.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +#include +#include + +namespace saw { +/* + * Helper classes for the specific driver implementations + */ + +/* + * Since I don't want to repeat these implementations for tls on unix systems + * and gnutls doesn't let me write or read into buffers I have to have this kind + * of strange abstraction. This may also be reusable for windows/macOS though. + */ +class input_stream; + +class read_task_and_step_helper { +public: + struct read_io_task { + void *buffer; + size_t min_length; + size_t max_length; + size_t already_read = 0; + }; + std::optional read_task; + own> read_done = nullptr; + + own> on_read_disconnect = nullptr; + +public: + void read_step(input_stream &reader); +}; + +class output_stream; + +class write_task_and_step_helper { +public: + struct write_io_task { + const void *buffer; + size_t length; + size_t already_written = 0; + }; + std::optional write_task; + own> write_done = nullptr; + +public: + void write_step(output_stream &writer); +}; +} // namespace saw -- cgit v1.2.3