diff options
author | Claudius "keldu" Holeksa <mail@keldu.de> | 2023-12-04 12:18:14 +0100 |
---|---|---|
committer | Claudius "keldu" Holeksa <mail@keldu.de> | 2023-12-04 12:18:14 +0100 |
commit | a14896f9ed209dd3f9597722e5a5697bd7dbf531 (patch) | |
tree | 089ca5cbbd206d1921f8f6b53292f5bc1902ca5c /modules/codec | |
parent | 84ecdcbca9e55b1f57fbb832e12ff4fdbb86e7c9 (diff) |
meta: Renamed folder containing source
Diffstat (limited to 'modules/codec')
-rw-r--r-- | modules/codec/.nix/derivation.nix | 28 | ||||
-rw-r--r-- | modules/codec/SConscript | 38 | ||||
-rw-r--r-- | modules/codec/SConstruct | 66 | ||||
-rw-r--r-- | modules/codec/args.h | 123 | ||||
-rw-r--r-- | modules/codec/csv.h | 25 | ||||
-rw-r--r-- | modules/codec/data.h | 370 | ||||
-rw-r--r-- | modules/codec/forst.h | 32 | ||||
-rw-r--r-- | modules/codec/forst.tmpl.h | 7 | ||||
-rw-r--r-- | modules/codec/interface.h | 103 | ||||
-rw-r--r-- | modules/codec/rpc.h | 26 | ||||
-rw-r--r-- | modules/codec/schema.h | 109 | ||||
-rw-r--r-- | modules/codec/schema_hash.h | 105 | ||||
-rw-r--r-- | modules/codec/schema_stringify.h | 118 | ||||
-rw-r--r-- | modules/codec/simple.h | 389 | ||||
-rw-r--r-- | modules/codec/stream_value.h | 64 |
15 files changed, 1603 insertions, 0 deletions
diff --git a/modules/codec/.nix/derivation.nix b/modules/codec/.nix/derivation.nix new file mode 100644 index 0000000..bcf2be7 --- /dev/null +++ b/modules/codec/.nix/derivation.nix @@ -0,0 +1,28 @@ +{ lib +, stdenv +, scons +, clang-tools +, version +, forstio +}: + +let + +in stdenv.mkDerivation { + pname = "forstio-codec"; + inherit version; + src = ./..; + + enableParallelBuilding = true; + + buildInputs = [ + forstio.core + ]; + + nativeBuildInputs = [ + scons + clang-tools + ]; + + outputs = ["out" "dev"]; +} diff --git a/modules/codec/SConscript b/modules/codec/SConscript new file mode 100644 index 0000000..c038d42 --- /dev/null +++ b/modules/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/modules/codec/SConstruct b/modules/codec/SConstruct new file mode 100644 index 0000000..0d7b7c6 --- /dev/null +++ b/modules/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/modules/codec/args.h b/modules/codec/args.h new file mode 100644 index 0000000..6bb75a2 --- /dev/null +++ b/modules/codec/args.h @@ -0,0 +1,123 @@ +#pragma once + +#include "schema.h" + +namespace saw { +namespace encode { +struct Args {}; +} + +namespace schema { +template<typename InnerSchemaStruct, typename InnerSchemaTuple> +using Args = Struct< + Member<String, "program">, + Member<InnerSchemaStruct, "args">, + Member<InnerSchemaTuple, "positionals"> +>; +} + +template<typename T> +class data<T, encode::Args> { + static_assert(always_false<T>,"Not supported. Use the schema::Args alias or check how it's defined."); +}; + +template<typename... Str, string_literal... Lits, typename... Tup> +class data<schema::Args<schema::Struct<schema::Member<Str,Lits>...>, schema::Tuple<Tup...>>, encode::Args> { +private: + char** argv_; + int argc_; +public: + data(char** argv, int argc): + argv_{argv}, + argc_{argc} + {} + + size_t size() const { + if(argc_ < 0){ + return 0; + } + + static_assert(sizeof(int) <= sizeof(size_t), "size_t is smaller than int"); + + return static_cast<size_t>(argc_); + } + + std::string_view arg_view(size_t i){ + if(i < size()){ + return std::string_view{argv_[i]}; + } + return ""; + } +}; + +namespace impl { +template<typename T, typename ToDec> +struct args_decode { + static_assert(always_false<T,ToDec>, "Not supported"); +}; + +template<typename... Str, string_literal... Lits, typename... Tup, typename ToDec> +struct args_decode<schema::Args<schema::Struct<schema::Member<Str,Lits>...>,schema::Tuple<Tup...>>,ToDec> { + using Schema = schema::Args< + schema::Struct<schema::Member<Str,Lits>...>, + schema::Tuple<Tup...> + >; + + static error_or<void> decode(data<Schema, encode::Args>& from, data<Schema,ToDec>& to){ + if(from.size() == 0){ + return make_error<err::invalid_state>(); + } + + to.get<"program">().set(std::string{from.arg_view(0)}); + std::size_t tuple_pos = 0; + for(size_t i = 1; i < from.size(); ++i){ + auto view = from.arg_view(i); + if(view.starts_with("--")){ + view.remove_prefix(std::min(2u, view.size())); + ++i; + + if( i >= from.size() ){ + return make_error<err::invalid_state>(); + } + + auto value_view = from.arg_view(i); + + auto eov = decode_struct_member<0>(to.get<"args">(), view, value_view); + if(eov.is_error()){ + return eov; + } + } else { + auto eov = decode_tuple_member<0>(to.get<"positionals">(), view); + if(eov.is_error()){ + return eov; + } + ++tuple_pos; + } + } + + if(tuple_pos != sizeof...(Tup)){ + return make_error<err::invalid_state>(); + } + + return void_t{}; + } +}; +} + +template<typename Schema> +class codec<Schema, encode::Args> { +public: + template<typename ToDec> + error_or<void> decode(data<Schema, encode::Args>& from, data<Schema, ToDec>& to){ + struct name_and_value { + std::string name; + std::string value; + }; + std::string program; + std::vector<name_and_value> navs; + std::vector<std::string> positionals; + + return void_t{}; + } +}; +} diff --git a/modules/codec/csv.h b/modules/codec/csv.h new file mode 100644 index 0000000..67c2c1d --- /dev/null +++ b/modules/codec/csv.h @@ -0,0 +1,25 @@ +#pragma once + +#include "data.h" + +namespace saw { +namespace encode { +struct Csv {}; +} + +template<typename Schema> +class codec<Schema, encode::Csv> { +public: + template<typename FromEncode> + static error_or<void> encode(const data<Schema, FromEncode>& from, data<Schema,encode::Csv>& to){ + + return make_error<err::not_implemented>(); + } + + template<typename ToDecode> + static error_or<void> decode(data<Schema,encode::Csv>& from, data<Schema, FromEncode>& to){ + + return make_error<err::not_implemented>(); + } +}; +} diff --git a/modules/codec/data.h b/modules/codec/data.h new file mode 100644 index 0000000..237ef5a --- /dev/null +++ b/modules/codec/data.h @@ -0,0 +1,370 @@ +#pragma once + +#include <forstio/core/common.h> +#include <forstio/core/templates.h> + +#include <cassert> + +#include <array> +#include <concepts> +#include <variant> +#include <vector> + +#include "schema.h" + +namespace saw { +namespace encode { +struct Native {}; +} +template<typename Schema, typename Encode> +class codec; +/* + * Helper for the basic message container, so the class doesn't have to be + * specialized 10 times. + */ +template <class T> struct native_data_type; + +template <> +struct native_data_type<schema::Primitive<schema::SignedInteger, 1>> { + using type = int8_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::SignedInteger, 2>> { + using type = int16_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::SignedInteger, 4>> { + using type = int32_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::SignedInteger, 8>> { + using type = int64_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::UnsignedInteger, 1>> { + using type = uint8_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::UnsignedInteger, 2>> { + using type = uint16_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::UnsignedInteger, 4>> { + using type = uint32_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::UnsignedInteger, 8>> { + using type = uint64_t; +}; + +template <> +struct native_data_type<schema::Primitive<schema::FloatingPoint, 4>> { + using type = float; +}; + +template <> +struct native_data_type<schema::Primitive<schema::FloatingPoint, 8>> { + using type = double; +}; + +template<typename T, typename Encoding = encode::Native> +class data { +private: + static_assert(always_false<T>, "Type not supported"); +}; + +template<typename... T, string_literal... literals> +class data<schema::Union<schema::Member<T, literals>...>, encode::Native> { +private: + std::variant<data<T,encode::Native>...> value_; +public: + data() = default; + + SAW_DEFAULT_COPY(data); + SAW_DEFAULT_MOVE(data); + + template<string_literal lit> + void set(data<typename parameter_pack_type<parameter_key_pack_index<lit, literals...>::value, T...>::type, encode::Native> val){ + value_ = std::move(val); + } + + template<string_literal lit> + data<typename parameter_pack_type<parameter_key_pack_index<lit, literals...>::value, T...>::type, encode::Native>& init(){ + value_ = data<typename parameter_pack_type<parameter_key_pack_index<lit, literals...>::value, T...>::type, encode::Native>{}; + return get<lit>(); + } + + template<string_literal lit> + bool holds_alternative() const { + return (parameter_key_pack_index<lit, literals...>::value == value_.index()); + } + + template<string_literal lit> + data<typename parameter_pack_type<parameter_key_pack_index<lit, literals...>::value, T...>::type, encode::Native>& get(){ + return std::get<parameter_key_pack_index<lit, literals...>::value>(value_); + } + + template<string_literal lit> + const data<typename parameter_pack_type<parameter_key_pack_index<lit, literals...>::value, T...>::type, encode::Native>& get() const{ + return std::get<parameter_key_pack_index<lit, literals...>::value>(value_); + } +}; + +template<typename... T, string_literal... literals> +class data<schema::Struct<schema::Member<T, literals>...>, encode::Native> { +private: + std::tuple<data<T,encode::Native>...> value_; +public: + data() = default; + SAW_DEFAULT_COPY(data); + SAW_DEFAULT_MOVE(data); + + template<string_literal literal> + data< + typename parameter_pack_type< + parameter_key_pack_index< + literal, literals... + >::value + , T...>::type + , encode::Native>& get(){ + return std::get<parameter_key_pack_index<literal, literals...>::value>(value_); + } + + template<string_literal literal> + const data< + typename parameter_pack_type< + parameter_key_pack_index< + literal, literals... + >::value + , T...>::type + , encode::Native>& get() const { + return std::get<parameter_key_pack_index<literal, literals...>::value>(value_); + } + + constexpr size_t size() const { + return sizeof...(T); + } +}; + +template<typename... T> +class data<schema::Tuple<T...>, encode::Native> { +private: + std::tuple<data<T,encode::Native>...> value_; +public: + data() = default; + SAW_DEFAULT_COPY(data); + SAW_DEFAULT_MOVE(data); + + template<size_t i> + data<typename parameter_pack_type<i,T...>::type, encode::Native>& get(){ + return std::get<i>(value_); + } + + template<size_t i> + const data<typename parameter_pack_type<i,T...>::type, encode::Native>& get() const{ + return std::get<i>(value_); + } + + constexpr size_t size() const { + return sizeof...(T); + } +}; + +template<typename T, size_t Dim> +class data<schema::Array<T,Dim>, encode::Native> { + private: + std::array<std::size_t, Dim> dims_; + std::vector<data<T, encode::Native>> value_; + + std::size_t get_full_size() const { + std::size_t s = 1; + + for(std::size_t iter = 0; iter < Dim; ++iter){ + assert(dims_.at(iter) > 0); + s *= dims_.at(iter); + } + + return s; + } + public: + data() = default; + SAW_DEFAULT_COPY(data); + SAW_DEFAULT_MOVE(data); + + data(const std::array<std::size_t, Dim>& i): + dims_{i}, + value_{} + { + value_.resize(get_full_size()); + } + + template<std::integral... Dims> + data(Dims... size_): + data{{size_...}} + { + static_assert(sizeof...(Dims)==Dim, "Argument size must be equal to the Dimension"); + } + + data<T, encode::Native>& at(const std::array<std::size_t, Dim>& ind){ + return value_.at(this->get_flat_index(ind)); + } + + const data<T, encode::Native>& at(const std::array<std::size_t, Dim>& ind) const { + return value_.at(this->get_flat_index(ind)); + } + + template<std::integral... Dims> + data<T, encode::Native>& at(Dims... i){ + return value_.at(this->get_flat_index({i...})); + } + + template<std::integral... Dims> + const data<T, encode::Native>& at(Dims... i) const { + return value_.at(this->get_flat_index({i...})); + } + + std::size_t get_dim_size(std::size_t i) const { + return dims_.at(i); + } + + size_t size() const { return value_.size();} + +private: + std::size_t get_flat_index(const std::array<std::size_t, Dim>& i) const { + std::size_t s = 0; + + std::size_t stride = 1; + + for(std::size_t iter = 0; iter < Dim; ++iter){ + assert(i.at(iter) < dims_.at(iter)); + s += i.at(iter) * stride; + stride *= dims_.at(iter); + } + + return s; + } +}; + +template<typename T, std::size_t... D> +class data<schema::FixedArray<T,D...>, encode::Native> { +private: + //using inner_type = std::array<data<T, encode::Native>, multiply_helper<Dims...>::value>; + //std::unique_ptr<inner_type> value_; + std::vector<data<T, encode::Native>> value_; + +public: + data(){ + value_.resize(ct_multiply<std::size_t, D...>::value); + } + + data<T, encode::Native>& at(const std::array<std::size_t, sizeof...(D)>& ind){ + return value_.at(this->get_flat_index(ind)); + } + + const data<T, encode::Native>& at(const std::array<std::size_t, sizeof...(D)>& ind) const { + return value_.at(this->get_flat_index(ind)); + } + + template<std::integral... Dims> + data<T, encode::Native>& at(Dims... i) { + return value_.at(this->get_flat_index({i...})); + } + + template<std::integral... Dims> + const data<T, encode::Native>& at(Dims... i) const { + return value_.at(this->get_flat_index({i...})); + } + + template<std::size_t i> + std::size_t get_dim_size() const { + return parameter_pack_value<i, std::size_t, D...>::value; + } +private: + std::size_t get_flat_index(const std::array<std::size_t, sizeof...(D)>& i) const { + std::size_t s = 0; + + std::size_t stride = 1; + + constexpr static std::array<std::size_t, sizeof...(D)> dims_{D...}; + + for(std::size_t iter = 0; iter < sizeof...(D); ++iter){ + assert(i.at(iter) < dims_.at(iter)); + s += i.at(iter) * stride; + stride *= dims_.at(iter); + } + + return s; + } +}; + +template<> +class data<schema::String, encode::Native> { +private: + std::string value_; +public: + data() = default; + SAW_DEFAULT_COPY(data); + SAW_DEFAULT_MOVE(data); + + data(std::string value__):value_{std::move(value__)}{} + data(std::size_t size_){ + value_.resize(size_); + } + + std::size_t size() const { + return value_.size(); + } + + void set(std::string str){ + value_ = std::move(str); + } + + char& at(size_t i) { + return value_.at(i); + } + + const char& at(size_t i) const { + return value_.at(i); + } + + char get_at(size_t i) const{ + return value_.at(i); + } + + void set_at(size_t i, char val){ + value_.at(i) = val; + } + + bool operator==(const std::string_view& val)const{ + return value_ == val; + } +}; + +template<typename T, size_t N> +class data<schema::Primitive<T,N>, encode::Native> { +private: + typename native_data_type<schema::Primitive<T,N>>::type value_; +public: + data():value_{{}}{}; + SAW_DEFAULT_COPY(data); + SAW_DEFAULT_MOVE(data); + + data(typename native_data_type<schema::Primitive<T,N>>::type value__): + value_{std::move(value__)}{} + + void set(typename native_data_type<schema::Primitive<T,N>>::type val){ + value_ = val; + } + + typename native_data_type<schema::Primitive<T,N>>::type get() const {return value_;} +}; + + +} diff --git a/modules/codec/forst.h b/modules/codec/forst.h new file mode 100644 index 0000000..7e8fbf0 --- /dev/null +++ b/modules/codec/forst.h @@ -0,0 +1,32 @@ +#pragma once + +#include "data.h" + +namespace saw { +namespace encode { +struct KelForst {}; +} +} + +#include "forst.tmpl.hpp" + +namespace saw { +class data<schema::String, encode::KelForst> { +private: + own<buffer> buff_; +public: + data(own<buffer> buff): + buff_{std::move(buff)} + {} +}; + +template<typename... T, string_literal... Keys> +class data<schema::Struct<schema::Member<T,Keys>...>, encode::KelForst> { +private: + own<buffer> buff_; +public: + data(own<buffer> buff): + buff_{std::move(buff)} + {} +}; +} diff --git a/modules/codec/forst.tmpl.h b/modules/codec/forst.tmpl.h new file mode 100644 index 0000000..30d18ef --- /dev/null +++ b/modules/codec/forst.tmpl.h @@ -0,0 +1,7 @@ +namespace saw { +namespace impl { +struct forst_decode { + +}; +} +} diff --git a/modules/codec/interface.h b/modules/codec/interface.h new file mode 100644 index 0000000..b1422a9 --- /dev/null +++ b/modules/codec/interface.h @@ -0,0 +1,103 @@ +#pragma once + +#include <forstio/core/error.h> +#include "schema.h" +#include "data.h" + +namespace saw { +template<typename SchemaFunc, typename Encode, typename Func> +class function; + +template<typename Request, typename Response, typename Encode, typename Func> +class function<schema::Function<Request, Response>, Encode, Func> { +private: + Func func_; +public: + function(Func func): + func_{std::move(func)} + {} + + error_or<data<Response, Encode>> call(data<Request, Encode> req){ + return func_(std::move(req)); + } +}; + +template<typename T, typename Encode, typename... Funcs> +class interface; + +template<typename... Requests, typename... Responses, string_literal... Names, typename Encode, typename... Funcs> +class interface<schema::Interface<schema::Member<schema::Function<Requests, Responses>, Names>...>, Encode, Funcs...> { +private: + std::tuple<function<schema::Function<Requests, Responses>, Encode, Funcs>...> funcs_; +public: + interface(function<schema::Function<Requests, Responses>, Encode, Funcs>... funcs): + funcs_{std::move(funcs)...} + {} + + /** + * Get the function based on the string literal matching the position in the tuple + */ + template<string_literal Lit> + function< + schema::Function< + typename parameter_pack_type< + parameter_key_pack_index< + Lit, Names... + >::value + , Requests...>::type + , + typename parameter_pack_type< + parameter_key_pack_index< + Lit, Names... + >::value + , Responses...>::type + > + , + Encode + , + typename parameter_pack_type< + parameter_key_pack_index< + Lit, Names... + >::value + , Funcs...>::type + >& get(){ + return std::get<parameter_key_pack_index<Lit, Names...>::value>(funcs_); + } + + template<string_literal Lit> + error_or<data< + typename parameter_pack_type< + parameter_key_pack_index< + Lit, Names... + >::value + , Responses...>::type + , Encode>> call( + data< + typename parameter_pack_type< + parameter_key_pack_index< + Lit, Names... + >::value + , Requests...>::type + , Encode> req + ){ + return get<Lit>().call(std::move(req)); + } + +}; + +template<typename T, typename Encode> +struct function_factory { + template<typename Func> + static function<T,Encode, Func> create(Func func){ + return function<T,Encode,Func>{std::move(func)}; + } +}; + +template<typename T, typename Encode> +struct interface_factory { + template<typename... Func> + static interface<T,Encode, Func...> create(Func... func){ + return interface<T,Encode, Func...>{std::move(func)...}; + } +}; +} diff --git a/modules/codec/rpc.h b/modules/codec/rpc.h new file mode 100644 index 0000000..b21ecf8 --- /dev/null +++ b/modules/codec/rpc.h @@ -0,0 +1,26 @@ +#pragma once + +namespace saw { +template<typename T> +class remote { + static_assert(always_false<T>, "Type of remote not supported"); +}; + +template<typename T> +class rpc_client { + template<typename... T> + struct request { + private: + std::tuple<id<T>> ids_; + public: + error_or<data<T>> wait(); + }; + + template<typename... T> + request<T...> request_data(id<T>... data); +}; + +template<typename T> +class rpc_server { +}; +} diff --git a/modules/codec/schema.h b/modules/codec/schema.h new file mode 100644 index 0000000..a8494fe --- /dev/null +++ b/modules/codec/schema.h @@ -0,0 +1,109 @@ +#pragma once + +#include <forstio/core/common.h> +#include <forstio/core/string_literal.h> + +namespace saw { +namespace schema { +// NOLINTBEGIN +template <typename T, string_literal Literal> struct Member {}; + +template <typename... T> struct Struct { + static_assert( + always_false<T...>, + "This schema template doesn't support this type of template argument"); +}; + +template <typename... V, string_literal... K> +struct Struct<Member<V, K>...> {}; + +template <typename... T> struct Union { + static_assert( + always_false<T...>, + "This schema template doesn't support this type of template argument"); +}; + +template <typename... V, string_literal... K> +struct Union<Member<V, K>...> {}; + +template <typename T, size_t Dim = 1> struct Array {}; + +template <class T> struct is_array { + constexpr static bool value = false; +}; + +template <class T, size_t Dim> struct is_array<schema::Array<T,Dim>> { + constexpr static bool value = true; +}; + +template<typename T, size_t... S> struct FixedArray {}; + +template <typename... T> struct Tuple {}; + +/** + * This acts as a separator of different encodings being mashed together + * For example we can transport any base64 encodings in JSON + * + * using WrappedExample = schema::Tuple< + * schema::Wrapper<schema::String, encode::Base64> + * >; + * + * data<WrappedExample, encode::Json> ex_data; + */ +template <typename T, typename Enc> +class Wrapper {}; + +struct String {}; + +struct SignedInteger {}; +struct UnsignedInteger {}; +struct FloatingPoint {}; + +template <class T, size_t N> struct Primitive { + static_assert(((std::is_same_v<T, SignedInteger> || + std::is_same_v<T, UnsignedInteger>)&&(N == 1 || N == 2 || + N == 4 || N == 8)) || + (std::is_same_v<T, FloatingPoint> && (N == 4 || N == 8)), + "Primitive Type is not supported"); +}; + +using Int8 = Primitive<SignedInteger, 1>; +using Int16 = Primitive<SignedInteger, 2>; +using Int32 = Primitive<SignedInteger, 4>; +using Int64 = Primitive<SignedInteger, 8>; + +using UInt8 = Primitive<UnsignedInteger, 1>; +using UInt16 = Primitive<UnsignedInteger, 2>; +using UInt32 = Primitive<UnsignedInteger, 4>; +using UInt64 = Primitive<UnsignedInteger, 8>; + +using Float32 = Primitive<FloatingPoint, 4>; +using Float64 = Primitive<FloatingPoint, 8>; + +/** + * Classes allowing to distinguish Ints from VarInts + */ +template<typename T, std::size_t MaxLen> +struct VariableLengthPrimitive {}; + +using VarInt = VariableLengthPrimitive<SignedInteger, 5>; +using VarLong = VariableLengthPrimitive<SignedInteger, 10>; + +/** + * Classes enabling Rpc calls + */ +template <class Request, class Response> +struct Function {}; + +template <class... T> struct Interface { + static_assert( + always_false<T...>, + "This schema template doesn't support this type of template argument"); +}; + +template <class... Requests, class... Responses, string_literal... Names> +struct Interface<Member<Function<Requests, Responses>,Names>...> {}; + +// NOLINTEND +} // namespace schema +} // namespace saw diff --git a/modules/codec/schema_hash.h b/modules/codec/schema_hash.h new file mode 100644 index 0000000..5690166 --- /dev/null +++ b/modules/codec/schema_hash.h @@ -0,0 +1,105 @@ +#pragma once + +#include "schema.h" + +namespace saw { +struct schema_hash_combine { + static constexpr uint64_t apply(uint64_t seed, uint64_t v){ + return (seed ^ v) * 1099511628211u; + + + return seed ^( std::hash<uint64_t>{}(v) + 0x9e3779b9 + (seed<<6) + (seed >> 2)); + } +}; + +template<typename Schema> +struct schema_hash { + static_assert(always_false<Schema>, "Not schema_hashable"); +}; + +template<> +struct schema_hash<schema::Primitive<schema::SignedInteger,1>> { + static constexpr uint64_t base_value = 0u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::SignedInteger,2>> { + static constexpr uint64_t base_value = 1u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::SignedInteger,4>> { + static constexpr uint64_t base_value = 2u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::SignedInteger,8>> { + static constexpr uint64_t base_value = 3u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::UnsignedInteger,1>> { + static constexpr uint64_t base_value = 4u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::UnsignedInteger,2>> { + static constexpr uint64_t base_value = 5u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::UnsignedInteger,4>> { + static constexpr uint64_t base_value = 6u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::UnsignedInteger,8>> { + static constexpr uint64_t base_value = 7u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::FloatingPoint,4>> { + static constexpr uint64_t base_value = 8u; +}; + +template<> +struct schema_hash<schema::Primitive<schema::FloatingPoint,8>> { + static constexpr uint64_t base_value = 9u; +}; + +template<typename... T> +struct schema_hash<schema::Tuple<T...>> { + static constexpr uint64_t base_value = 10u; +}; + +template<typename... T, string_literal Literal> +struct schema_hash<schema::Struct<schema::Member<T,Literal>...>> { + static constexpr uint64_t base_value = 11u; +}; + +template<typename... T, string_literal Literal> +struct schema_hash<schema::Union<schema::Member<T,Literal>...>> { + static constexpr uint64_t base_value = 12u; +}; + +template<typename T, size_t Dim> +struct schema_hash<schema::Array<T,Dim>> { + static constexpr uint64_t base_value = 13u; +}; + +template<> +struct schema_hash<schema::String> { + static constexpr uint64_t base_value = 14u; +}; + +template<typename T, typename N> +struct schema_hash<schema::Wrapper<T,N>> { + static constexpr uint64_t base_value = 15u; +}; + +template<typename T, size_t... Dims> +struct schema_hash<schema::FixedArray<T,Dims...>> { + static constexpr uint64_t base_value = 16u; +}; + +} diff --git a/modules/codec/schema_stringify.h b/modules/codec/schema_stringify.h new file mode 100644 index 0000000..a82081a --- /dev/null +++ b/modules/codec/schema_stringify.h @@ -0,0 +1,118 @@ +#pragma once + +#include "schema.h" + +#include <sstream> +#include <type_traits> + +namespace saw { +template<typename Schema> +struct schema_stringify { + static_assert(always_false<Schema>, "Not supported"); +}; + +template<typename T, size_t Dim> +struct schema_stringify<schema::Array<T,Dim>> { + static void apply(std::stringstream& iss) { + iss << "saw::schema::Array<"; + schema_stringify<T>::apply(iss); + iss << ","; + iss << ct_convert_to_digits<Dim, 10>::literal.view(); + iss << ">"; + } +}; + +template<typename T, size_t N> +struct schema_stringify<schema::Primitive<T,N>> { + static void apply(std::stringstream& iss) { + iss << "saw::schema::Primitive<"; + schema_stringify<T>::apply(iss); + iss << ","; + iss << ct_convert_to_digits<N,10>::literal.view(); + iss << ">"; + } +}; + +template<> +struct schema_stringify<schema::SignedInteger> { + static void apply(std::stringstream& iss) { + iss << "saw:schema::SignedInteger"; + } +}; + +template<> +struct schema_stringify<schema::UnsignedInteger> { + static void apply(std::stringstream& iss) { + iss << "saw:schema::UnsignedInteger"; + } +}; + +template<> +struct schema_stringify<schema::FloatingPoint> { + static void apply(std::stringstream& iss) { + iss << "saw:schema::FloatingPoint"; + } +}; + +template<typename... T> +struct schema_stringify_member { + static void apply(std::stringstream& iss) { + (void)iss; + } +}; + +template<typename T0, string_literal Name, typename... TL> +struct schema_stringify_member<schema::Member<T0,Name>, TL...> { + + static void apply(std::stringstream& iss) { + iss << "saw::schema::Member<"; + schema_stringify<T0>::apply(iss); + iss << ",\""; + iss << Name.view(); + iss << "\">"; + if constexpr ( sizeof...(TL) > 0){ + iss << ","; + schema_stringify_member<TL...>::apply(iss); + } + } +}; + +template<typename... T, string_literal... Lits> +struct schema_stringify<schema::Struct<schema::Member<T,Lits>...>> { + static void apply(std::stringstream& iss) { + iss << "saw::schema::Struct<"; + schema_stringify_member<schema::Member<T,Lits>...>::apply(iss); + iss << ">"; + } +}; + +template<typename... T, string_literal... Lits> +struct schema_stringify<schema::Union<schema::Member<T,Lits>...>> { + static void apply(std::stringstream& iss) { + iss << "saw::schema::Union<"; + schema_stringify_member<schema::Member<T,Lits>...>::apply(iss); + iss << ">"; + } +}; + +template<typename Req, typename Resp> +struct schema_stringify<schema::Function<Req, Resp>> { + static void apply(std::stringstream& iss){ + iss << "saw::schema::Function<"; + schema_stringify<Req>::apply(iss); + iss << ","; + schema_stringify<Resp>::apply(iss); + iss << ">"; + } +}; + +template<typename... T, string_literal... Lits> +struct schema_stringify<schema::Interface<schema::Member<T,Lits>...>>{ + static void apply(std::stringstream& iss){ + iss << "saw::schema::Interface<"; + schema_stringify_member<schema::Member<T,Lits>...>::apply(iss); + iss << ">"; + } +}; + +} diff --git a/modules/codec/simple.h b/modules/codec/simple.h new file mode 100644 index 0000000..8760754 --- /dev/null +++ b/modules/codec/simple.h @@ -0,0 +1,389 @@ +#pragma once + +#include "data.h" +#include "stream_value.h" + +#include <forstio/core/buffer.h> +#include <forstio/core/error.h> + +namespace saw { +namespace encode { +struct KelSimple {}; +} + +template<typename T> +class data<T, encode::KelSimple> { +private: + ring_buffer buffer_; +public: + data() = default; + + buffer& get_buffer(){ + return buffer_; + } +}; + +namespace impl { +template<typename Schema, typename FromEnc> +class kelsimple_encode { + static_assert(always_false<Schema, FromEnc>, "This schema type is not being handled by the kelsimple encoding."); +}; + +template<typename T, size_t N, typename FromEnc> +struct kelsimple_encode<schema::Primitive<T,N>, FromEnc> { + static error_or<void> encode(const data<schema::Primitive<T,N>, FromEnc>& from, buffer& to){ + auto eov = stream_value<schema::Primitive<T,N>>::encode(from.get(), to); + return eov; + } +}; + +template<typename T, size_t Dim, typename FromEnc> +struct kelsimple_encode<schema::Array<T,Dim>, FromEnc> { + template<std::size_t Level> + static error_or<void> encode_level(const data<schema::Array<T,Dim>, FromEnc>& from, buffer& to, std::array<std::size_t, Dim>& index){ + if constexpr (Dim == Level){ + return kelsimple_encode<T,FromEnc>::encode(from.at(index), to); + } else { + const std::size_t dim_size = from.get_dim_size(Level); + for(index[Level] = 0; (index.at(Level) < dim_size); ++index[Level]){ + auto eov = encode_level<Level+1>(from, to, index); + if(eov.is_error()){ + return eov; + } + } + } + return void_t{}; + } + + static error_or<void> encode(const data<schema::Array<T,Dim>, FromEnc>& from, buffer& to){ + { + for(uint64_t i = 0; i < Dim; ++i){ + auto eov = stream_value<schema::UInt64>::encode(from.get_dim_size(i), to); + if(eov.is_error()){ + return eov; + } + } + } + { + std::array<std::size_t, Dim> index; + std::fill(index.begin(), index.end(), 0); + + return encode_level<0>(from, to, index); + } + return void_t{}; + } +}; + +template<typename... T, string_literal... Lits, typename FromEnc> +struct kelsimple_encode<schema::Struct<schema::Member<T,Lits>...>,FromEnc> { + template<std::size_t i> + static error_or<void> encode_member(const data<schema::Struct<schema::Member<T,Lits>...>, FromEnc>& from, buffer& to){ + using Type = typename parameter_pack_type<i,T...>::type; + constexpr string_literal Literal = parameter_key_pack_type<i, Lits...>::literal; + { + auto eov = kelsimple_encode<Type, FromEnc>::encode(from.template get<Literal>(), to); + if(eov.is_error()){ + return eov; + } + } + if constexpr ((i+1) < sizeof...(T)){ + auto eov = encode_member<i+1>(from, to); + if(eov.is_error()){ + return eov; + } + } + return void_t{}; + } + + static error_or<void> encode(const data<schema::Struct<schema::Member<T,Lits>...>, FromEnc>& from, buffer& to){ + return encode_member<0>(from, to); + } +}; + +template<typename... T, string_literal... Lits, typename FromEnc> +struct kelsimple_encode<schema::Union<schema::Member<T,Lits>...>,FromEnc> { + template<std::size_t i> + static error_or<void> encode_member(const data<schema::Union<schema::Member<T,Lits>...>, FromEnc>& from, buffer& to){ + using Type = typename parameter_pack_type<i,T...>::type; + constexpr string_literal Literal = parameter_key_pack_type<i, Lits...>::literal; + if (from.template holds_alternative<Literal>()) { + { + auto eov = stream_value<schema::UInt64>::encode(static_cast<uint64_t>(i), to); + if(eov.is_error()){ + return eov; + } + } + { + auto eov = kelsimple_encode<Type, FromEnc>::encode(from.template get<Literal>(), to); + if(eov.is_error()){ + return eov; + } + } + } + + if constexpr ( (i+1) < sizeof...(T) ){ + auto eov = encode_member<i+1>(from, to); + if(eov.is_error()){ + return eov; + } + } + return void_t{}; + } + + static error_or<void> encode(const data<schema::Union<schema::Member<T,Lits>...>, FromEnc>& from, buffer& to){ + return encode_member<0>(from, to); + } +}; + +template<typename... T, typename FromEnc> +struct kelsimple_encode<schema::Tuple<T...>, FromEnc> { + template<std::size_t i> + static error_or<void> encode_member(const data<schema::Tuple<T...>, FromEnc>& from, buffer& to){ + using Type = typename parameter_pack_type<i,T...>::type; + { + auto eov = kelsimple_encode<Type, FromEnc>::encode(from.template get<i>(), to); + } + if constexpr ((i+1) < sizeof...(T)){ + auto eov = encode_member<i+1>(from, to); + if(eov.is_error()){ + return eov; + } + } + return void_t{}; + } + + static error_or<void> encode(const data<schema::Tuple<T...>, FromEnc>& from, buffer& to){ + return encode_member<0>(from, to); + } +}; + +template<typename FromEnc> +struct kelsimple_encode<schema::String, FromEnc> { + static error_or<void> encode(const data<schema::String, FromEnc>& from, buffer& to){ + const auto str_size = from.size(); + typename native_data_type<schema::UInt64>::type str_len = static_cast<uint64_t>(str_size); + { + auto eov = stream_value<schema::UInt64>::encode(str_len, to); + if(eov.is_error()){ + return eov; + } + } + + for(std::size_t i = 0; i < str_size; ++i){ + auto eov = stream_value<schema::Int8>::encode(from.at(i), to); + if(eov.is_error()){ + return eov; + } + } + + return void_t{}; + } +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template<typename Schema, typename FromEnc> +class kelsimple_decode { + static_assert(always_false<Schema, FromEnc>, "This schema type is not being handled by the kelsimple encoding."); +}; + +template<typename T, size_t N, typename FromEnc> +struct kelsimple_decode<schema::Primitive<T,N>, FromEnc> { + static error_or<void> decode(buffer& from, data<schema::Primitive<T,N>, FromEnc>& to){ + typename native_data_type<schema::Primitive<T,N>>::type val{}; + auto eov = stream_value<schema::Primitive<T,N>>::decode(val, from); + if (eov.is_value()) { + to.set(val); + } + return eov; + } + +}; + +template<typename T, size_t Dim, typename FromEnc> +struct kelsimple_decode<schema::Array<T,Dim>, FromEnc> { + template<std::size_t Level> + static error_or<void> decode_level(buffer& from, data<schema::Array<T,Dim>, FromEnc>& to, std::array<std::size_t, Dim>& index){ + if constexpr (Level == Dim){ + return kelsimple_decode<T, FromEnc>::decode(from, to.at(index)); + }else{ + const std::size_t dim_size = to.get_dim_size(Level); + for(index[Level] = 0; index[Level] < dim_size; ++index[Level]){ + auto eov = decode_level<Level+1>(from, to, index); + if(eov.is_error()){ + return eov; + } + } + } + return void_t{}; + } + + static error_or<void> decode(buffer& from, data<schema::Array<T,Dim>, FromEnc>& to){ + { + std::array<std::size_t, Dim> dims{}; + for(std::size_t i = 0; i < Dim; ++i){ + uint64_t val{}; + auto eov = stream_value<schema::UInt64>::decode(val, from); + if(eov.is_error()){ + return eov; + } + dims.at(i) = static_cast<std::size_t>(val); + } + to = data<schema::Array<T,Dim>,FromEnc>{dims}; + } + { + std::array<std::size_t, Dim> index{}; + return decode_level<0>(from, to, index); + } + return void_t{}; + } +}; +template<typename... T, string_literal... Lits, typename FromEnc> +struct kelsimple_decode<schema::Struct<schema::Member<T,Lits>...>,FromEnc> { + template<std::size_t i> + static error_or<void> decode_member(buffer& from, data<schema::Struct<schema::Member<T,Lits>...>, FromEnc>& to){ + using Type = typename parameter_pack_type<i,T...>::type; + constexpr string_literal Literal = parameter_key_pack_type<i, Lits...>::literal; + { + auto eov = kelsimple_decode<Type, FromEnc>::decode(from, to.template get<Literal>()); + if(eov.is_error()){ + return eov; + } + } + if constexpr ((i+1) < sizeof...(T)){ + auto eov = decode_member<i+1>(from, to); + if(eov.is_error()){ + return eov; + } + } + return void_t{}; + + } + static error_or<void> decode(buffer& from, data<schema::Struct<schema::Member<T,Lits>...>, FromEnc>& to){ + return decode_member<0>(from, to); + } + +}; + +template<typename... T, string_literal... Lits, typename FromEnc> +struct kelsimple_decode<schema::Union<schema::Member<T,Lits>...>,FromEnc> { + template<uint64_t i> + static error_or<void> decode_member(buffer& from, data<schema::Union<schema::Member<T,Lits>...>, FromEnc>& to, uint64_t val){ + using Type = typename parameter_pack_type<i,T...>::type; + constexpr string_literal Literal = parameter_key_pack_type<i, Lits...>::literal; + + if( i == val ){ + to.template set<Literal>(data<Type, FromEnc>{}); + auto eov = kelsimple_decode<Type, FromEnc>::decode(from, to.template get<Literal>()); + if(eov.is_error()){ + return eov; + } + return void_t{}; + } + + if constexpr ((i+1) < sizeof...(T)){ + auto eov = decode_member<i+1>(from, to, val); + if(eov.is_error()){ + return eov; + } + } + return void_t{}; + + } + static error_or<void> decode(buffer& from, data<schema::Union<schema::Member<T,Lits>...>, FromEnc>& to){ + uint64_t val{}; + auto eov = stream_value<schema::UInt64>::decode(val, from); + if(eov.is_error()){ + return eov; + } + if ( val >= sizeof...(T) ){ + return make_error<err::invalid_state>(); + } + return decode_member<0>(from, to, val); + } + +}; + +template<typename... T, typename FromEnc> +struct kelsimple_decode<schema::Tuple<T...>,FromEnc> { + template<std::size_t i> + static error_or<void> decode_member(buffer& from, data<schema::Tuple<T...>, FromEnc>& to){ + using Type = typename parameter_pack_type<i,T...>::type; + { + auto eov = kelsimple_decode<Type, FromEnc>::decode(from, to.template get<i>()); + } + if constexpr ((i+1) < sizeof...(T)){ + auto eov = decode_member<i+1>(from, to); + if(eov.is_error()){ + return eov; + } + } + return void_t{}; + + } + static error_or<void> decode(buffer& from, data<schema::Tuple<T...>, FromEnc>& to){ + return decode_member<0>(from, to); + } + +}; +template<typename FromEnc> +struct kelsimple_decode<schema::String, FromEnc> { + static error_or<void> decode(buffer& from, data<schema::String, FromEnc>& to){ + { + uint64_t val{}; + auto eov = stream_value<schema::UInt64>::decode(val, from); + if(eov.is_error()){ + return eov; + } + to = data<schema::String,FromEnc>{val}; + } + const std::size_t str_size = to.size(); + for(std::size_t i = 0; i < str_size; ++i){ + int8_t val{}; + auto eov = stream_value<schema::Int8>::decode(val, from); + if(eov.is_error()){ + return eov; + } + to.set_at(i, val); + } + return void_t{}; + } +}; + +} + +template<typename Schema> +class codec<Schema, encode::KelSimple> { +public: + struct config { + size_t depth = 16; + size_t length = 1024; + }; +private: + config cfg_; +public: + codec() = default; + + SAW_FORBID_COPY(codec); + SAW_DEFAULT_MOVE(codec); + + template<typename FromEnc> + error_or<void> encode(const data<Schema, FromEnc>& from_enc, data<Schema, encode::KelSimple>& to_enc){ + buffer_view buff_v{to_enc.get_buffer()}; + + auto eov = impl::kelsimple_encode<Schema, FromEnc>::encode(from_enc, buff_v); + + to_enc.get_buffer().write_advance(buff_v.write_offset()); + + return eov; + } + + template<typename ToDec> + error_or<void> decode(data<Schema, encode::KelSimple>& from_dec, data<Schema, ToDec>& to){ + buffer_view buff_v{from_dec.get_buffer()}; + + auto eov = impl::kelsimple_decode<Schema,ToDec>::decode(buff_v, to); + + return eov; + } +}; +} diff --git a/modules/codec/stream_value.h b/modules/codec/stream_value.h new file mode 100644 index 0000000..09203cb --- /dev/null +++ b/modules/codec/stream_value.h @@ -0,0 +1,64 @@ +#pragma once + +#include "schema.h" + +#include <forstio/core/buffer.h> +#include <forstio/core/error.h> + +#include <cstdint> +#include <cstring> + +namespace saw { +/** + * Helper class to encode/decode any primtive type into/from litte endian. + * The shift class does this by shifting bytes. This type of procedure is + * platform independent. So it does not matter if the memory layout is + * little endian or big endian + */ +template<typename T> class shift_stream_value { + static_assert(always_false<T>, "Shift Stream Value only supports Primitives"); +}; + +template <typename T, size_t N> class shift_stream_value<schema::Primitive<T,N>> { +public: + inline static error_or<void> decode(typename native_data_type<schema::Primitive<T,N>>::type &val, buffer &buff) { + if (buff.read_composite_length() < N) { + return make_error<err::buffer_exhausted>(); + } + + typename native_data_type<schema::Primitive<schema::UnsignedInteger,N>>::type raw = 0; + + for (size_t i = 0; i < N; ++i) { + raw |= (static_cast<typename native_data_type<schema::Primitive<T,N>>::type>(buff.read(i)) << (i * 8)); + } + + memcpy(&val, &raw, N); + buff.read_advance(N); + + return void_t{}; + } + + inline static error_or<void> encode(const typename native_data_type<schema::Primitive<T,N>>::type &val, buffer &buff) { + error err = buff.write_require_length(N); + if (err.failed()) { + return err; + } + + typename native_data_type<schema::Primitive<schema::UnsignedInteger,N>>::type raw{}; + memcpy(&raw, &val, N); + + for (size_t i = 0; i < N; ++i) { + buff.write(i) = raw >> (i * 8); + } + + buff.write_advance(N); + + return void_t{}; + } + + inline static size_t size() { return N; } +}; + +template <typename T> using stream_value = shift_stream_value<T>; + +} // namespace saw |