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-json | |
parent | 84ecdcbca9e55b1f57fbb832e12ff4fdbb86e7c9 (diff) |
meta: Renamed folder containing source
Diffstat (limited to 'modules/codec-json')
-rw-r--r-- | modules/codec-json/.nix/derivation.nix | 30 | ||||
-rw-r--r-- | modules/codec-json/SConscript | 38 | ||||
-rw-r--r-- | modules/codec-json/SConstruct | 66 | ||||
-rw-r--r-- | modules/codec-json/json.h | 120 | ||||
-rw-r--r-- | modules/codec-json/json.tmpl.h | 734 |
5 files changed, 988 insertions, 0 deletions
diff --git a/modules/codec-json/.nix/derivation.nix b/modules/codec-json/.nix/derivation.nix new file mode 100644 index 0000000..3738e43 --- /dev/null +++ b/modules/codec-json/.nix/derivation.nix @@ -0,0 +1,30 @@ +{ lib +, stdenv +, scons +, clang-tools +, version +, forstio +}: + +let + +in stdenv.mkDerivation { + pname = "forstio-codec-json"; + inherit version; + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang-tools + ]; + + buildInputs = [ + forstio.core + forstio.async + forstio.codec + ]; + + outputs = ["out" "dev"]; +} diff --git a/modules/codec-json/SConscript b/modules/codec-json/SConscript new file mode 100644 index 0000000..772ac0b --- /dev/null +++ b/modules/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/modules/codec-json/SConstruct b/modules/codec-json/SConstruct new file mode 100644 index 0000000..edd5f57 --- /dev/null +++ b/modules/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/modules/codec-json/json.h b/modules/codec-json/json.h new file mode 100644 index 0000000..56cadc0 --- /dev/null +++ b/modules/codec-json/json.h @@ -0,0 +1,120 @@ +#pragma once + +#include <forstio/core/buffer.h> +#include <forstio/core/common.h> +#include <forstio/codec/data.h> + +#include <algorithm> + +namespace saw { +namespace encode { +struct Json {}; +} + +template<typename Schema> +class data<Schema, encode::Json> { +private: + ring_buffer buffer_; +public: + data():buffer_{}{} + + data(std::size_t ring_size_):buffer_{ring_size_}{} + + data(ring_buffer buff_): + buffer_{std::move(buff_)} + {} + + data(const std::string_view& view__): + buffer_{view__.size()} + { + auto ptr = reinterpret_cast<const uint8_t*>(view__.data()); + if(!ptr){ + return; + } + buffer_.push(*ptr, view__.size()); + } + + buffer& get_buffer(){ + return buffer_; + } + + const buffer& get_buffer() const { + return buffer_; + } + + error push(uint8_t val){ + return buffer_.push(val); + } + + std::size_t get_size() const { + return buffer_.read_composite_length(); + } + + uint8_t& at(std::size_t i){ + return buffer_.read(i); + } + + const uint8_t& at(std::size_t i) const { + return buffer_.read(i); + } +}; +} + +#include "json.tmpl.h" + +namespace saw { + +/** + * Codec class for json + */ +template<typename Schema> +class codec<Schema, encode::Json> { +public: + struct config { + size_t depth = 16; + size_t length = 1024; + }; +private: + config cfg_; +public: + /** + * Default constructor + */ + codec(){} + + /** + * Constructor + */ + codec(config cfg__):cfg_{std::move(cfg__)}{} + + SAW_FORBID_COPY(codec); + SAW_DEFAULT_MOVE(codec); + + template <typename FromEncoding> + error_or<void> encode(const data<Schema, FromEncoding>& from_encode, data<Schema, encode::Json>& to_encode){ + // To Be encoded + buffer_view buff_v{to_encode.get_buffer()}; + auto eov = impl::json_encode<Schema, Schema, FromEncoding>::encode(from_encode, buff_v); + if(eov.is_error()){ + return std::move(eov.get_error()); + } + to_encode.get_buffer().write_advance(buff_v.write_offset()); + + return void_t{}; + } + + template <typename ToEncoding> + error_or<void> decode(data<Schema, encode::Json>& from_decode, data<Schema, ToEncoding>& to_decode){ + buffer_view buff_v{from_decode.get_buffer()}; + + auto eov = impl::json_decode<Schema, Schema, ToEncoding>::decode(buff_v, to_decode); + if(eov.is_error()){ + return std::move(eov.get_error()); + } + from_decode.get_buffer().read_advance(buff_v.read_offset()); + + return void_t {}; + } +}; +} + diff --git a/modules/codec-json/json.tmpl.h b/modules/codec-json/json.tmpl.h new file mode 100644 index 0000000..4b3a1a2 --- /dev/null +++ b/modules/codec-json/json.tmpl.h @@ -0,0 +1,734 @@ +#pragma once + +#include <charconv> +#include <sstream> + +#include <iostream> + +namespace saw { +namespace impl { +template<typename Schema, typename RootSchema, typename FromEncode> +class json_encode { + static_assert(always_false<Schema, RootSchema, FromEncode>, "This schema type is not being handled by the json encoding."); +}; + +template<typename T, size_t N, typename RootSchema, typename FromEncode> +struct json_encode<schema::Primitive<T,N>, RootSchema, FromEncode> { + static error_or<void> encode(const data<schema::Primitive<T,N>, FromEncode>& from, buffer& to) { + auto val = from.get(); + std::array<uint8_t, 256> data; + auto tc_result = std::to_chars(reinterpret_cast<char*>(data.data()), reinterpret_cast<char*>(data.data())+data.size(), val); + + if(tc_result.ec != std::errc{}){ + return make_error<err::critical>(); + } + + size_t bytes_written = 0; + for(char* ptr = reinterpret_cast<char*>(data.data()); ptr != tc_result.ptr; ++ptr){ + ++bytes_written; + } + + auto& buff = to; + error err = buff.write_require_length(bytes_written); + if(!err.template is_type<err::no_error>()){ + return std::move(err); + } + + for(char* ptr = reinterpret_cast<char*>(data.data()); ptr != tc_result.ptr; ++ptr){ + uint8_t* un_ptr = reinterpret_cast<uint8_t*>(ptr); + buff.push(un_ptr[0]); + } + + return void_t{}; + } +}; + +template<typename RootSchema, typename FromEncode> +struct json_encode<schema::String, RootSchema, FromEncode> { + static error_or<void> encode(const data<schema::String, FromEncode>& from, buffer& to) { + { + auto err = to.push('"'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + for(std::size_t i = 0; i < from.size(); ++i){ + auto err = to.push(from.get_at(i)); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + { + auto err = to.push('"'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + + return void_t{}; + } +}; + +template<typename... T, typename RootSchema, typename FromEncode> +struct json_encode<schema::Tuple<T...>, RootSchema, FromEncode> { + template<size_t i> + static error_or<void> encode_element(const data<schema::Tuple<T...>, FromEncode>& from, buffer& to){ + auto eov = json_encode<typename parameter_pack_type<i, T...>::type, RootSchema, FromEncode>::encode(from.template get<i>(), to); + + if(eov.is_error()){ + return eov; + } + + if constexpr ( (i+1) < sizeof...(T)){ + { + auto eov_ele = to.push(','); + if(!eov_ele.template is_type<err::no_error>()){ + return eov_ele; + } + } + { + auto eov_ele = encode_element<i+1>(from, to); + if(eov_ele.is_error()){ + return eov_ele; + } + } + + + } + return void_t{}; + } + + static error_or<void> encode(const data<schema::Tuple<T...>, FromEncode>& from, buffer& to) { + { + auto err = to.push('['); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + if constexpr ( sizeof...(T) > 0 ){ + auto eov = encode_element<0>(from, to); + if(eov.is_error()){ + return eov; + } + } + { + auto err = to.push(']'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + + return void_t{}; + } +}; + +template<typename T, size_t D, typename RootSchema, typename FromEncode> +struct json_encode<schema::Array<T,D>, RootSchema, FromEncode> { + template<size_t Level> + static error_or<void> encode_level(const data<schema::Array<T,D>, FromEncode>& from, buffer& to, std::array<std::size_t, D>& index){ + if constexpr (Level == D){ + auto eov = json_encode<T, RootSchema, FromEncode>::encode(from.at(index), to); + if(eov.is_error()){ + return eov; + } + } else { + { + auto err = to.push('['); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + for(std::size_t i = 0; i < from.get_dim_size(Level); ++i){ + if( i > 0 ){ + auto err = to.push(','); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + { + index[Level] = i; + auto eov = encode_level<Level+1>(from, to, index); + if(eov.is_error()){ + return eov; + } + } + } + { + auto err = to.push(']'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + } + return void_t{}; + } + + static error_or<void> encode(const data<schema::Array<T,D>, FromEncode>& from, buffer& to) { + std::array<std::size_t, D> index; + return encode_level<0>(from, to, index); + } +}; + +template<typename... T, string_literal... Key, typename RootSchema, typename FromEncode> +struct json_encode<schema::Struct<schema::Member<T,Key>...>, RootSchema, FromEncode> { + + template<size_t i> + static error_or<void> encode_element(const data<schema::Struct<schema::Member<T,Key>...>, FromEncode>& from, buffer& to){ + // Encode the name + { + std::string_view view = parameter_key_pack_type<i, Key...>::literal.view(); + error err = to.push('"'); + if(!err.template is_type<err::no_error>()){ + return err; + } + err = to.push(*reinterpret_cast<const uint8_t *>(view.data()), view.size()); + if(!err.template is_type<err::no_error>()){ + return err; + } + err = to.push('"'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + // Add the separator + { + auto eov_ele = to.push(':'); + if(!eov_ele.template is_type<err::no_error>()){ + return eov_ele; + } + } + + // Encode the value + auto eov = json_encode<typename parameter_pack_type<i, T...>::type, RootSchema, FromEncode>::encode(from.template get<parameter_key_pack_type<i, Key...>::literal>(), to); + + // Go to the next element + if constexpr ( (i+1) < sizeof...(T)){ + { + auto eov_ele = to.push(','); + if(!eov_ele.template is_type<err::no_error>()){ + return eov_ele; + } + } + { + auto eov_ele = encode_element<i+1>(from, to); + if(eov_ele.is_error()){ + return eov_ele; + } + } + + + } + + return void_t{}; + } + static error_or<void> encode(const data<schema::Struct<schema::Member<T,Key>...>, FromEncode>& from, buffer& to) { + { + auto err = to.push('{'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + if constexpr ( sizeof...(T) > 0 ){ + auto eov = encode_element<0>(from, to); + if(eov.is_error()){ + return eov; + } + } + { + auto err = to.push('}'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + + return void_t{}; + } +}; + +template<typename... T, string_literal... Key, typename RootSchema, typename FromEncode> +struct json_encode<schema::Union<schema::Member<T,Key>...>, RootSchema, FromEncode> { + + template<size_t i> + static error_or<void> encode_element(const data<schema::Union<schema::Member<T,Key>...>, FromEncode>& from, buffer& to){ + if(from.template holds_alternative<typename parameter_key_pack_type<i, Key...>::literal>()){ + // Encode the name + { + std::string_view view = parameter_key_pack_type<i, Key...>::literal.view(); + error err = to.push('"'); + if(!err.template is_type<err::no_error>()){ + return err; + } + err = to.push(*reinterpret_cast<const uint8_t *>(view.data()), view.size()); + if(!err.template is_type<err::no_error>()){ + return err; + } + err = to.push('"'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + // Add the separator + { + auto eov_ele = to.push(':'); + if(!eov_ele.template is_type<err::no_error>()){ + return eov_ele; + } + } + + // Encode the value + auto eov = json_encode<typename parameter_pack_type<i, T...>::type, RootSchema, FromEncode>::encode(from.template get<parameter_key_pack_type<i, Key...>::literal>(), to); + } + // Go to the next element + if constexpr ( (i+1) < sizeof...(T)){ + { + auto eov_ele = encode_element<i+1>(from, to); + if(eov_ele.is_error()){ + return eov_ele; + } + } + + + } + + return void_t{}; + } + static error_or<void> encode(const data<schema::Union<schema::Member<T,Key>...>, FromEncode>& from, buffer& to) { + { + auto err = to.push('{'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + if constexpr ( sizeof...(T) > 0 ){ + auto eov = encode_element<0>(from, to); + if(eov.is_error()){ + return eov; + } + } + { + auto err = to.push('}'); + if(!err.template is_type<err::no_error>()){ + return err; + } + } + + return void_t{}; + } +}; + +struct json_helper { + static bool is_whitespace(int8_t ch) { + return ch == '\t' || ch == ' ' || ch == '\r' || ch == '\n'; + } + + static void skip_whitespace(buffer_view& buff) { + while(buff.read_composite_length() > 0 && json_helper::is_whitespace(buff.read())) { + buff.read_advance(1); + } + } +}; + +template<typename Schema, typename RootSchema, typename ToDecode> +struct json_decode; + +template<typename T, size_t N, typename RootSchema, typename ToDecode> +struct json_decode<schema::Primitive<T,N>, RootSchema, ToDecode> { + static error_or<void> decode(buffer_view& buff, data<schema::Primitive<T,N>, ToDecode>& to) { + assert( + ( buff.read() >= '0' && buff.read() <= '9') + || ( buff.read() == '+' || buff.read() == '-') + ); + + std::size_t offset = 0; + + if (buff.read() == '-'){ + ++offset; + } else if (buff.read() == '+'){ + return make_error<err::not_supported>(); + } + if (offset >= buff.read_composite_length()) { + return make_error<err::buffer_exhausted>(); + } + if (buff.read(offset) >= '1' && buff.read(offset) <= '9') { + ++offset; + + if(offset >= buff.read_composite_length()) { + return make_error<err::buffer_exhausted>(); + } + + while(1){ + if (buff.read(offset) >= '0' && buff.read(offset) <= '9') { + ++offset; + + if(offset >= buff.read_composite_length()) { + break; + } + continue; + } + break; + } + } else if (buff.read(offset) == '0' ) { + ++offset; + } else { + return make_error<err::buffer_exhausted>(); + } + + { + std::string_view num_view{reinterpret_cast<char*>(&buff.read()), offset}; + typename native_data_type<schema::Primitive<T,N>>::type result; + auto fc_result = std::from_chars(num_view.data(), num_view.data() + num_view.size(), result); + if(fc_result.ec != std::errc{}){ + return make_error<err::invalid_state>(); + } + + to.set(result); + } + buff.read_advance(offset); + + return void_t{}; + } +}; + +template<typename RootSchema, typename ToDecode> +struct json_decode<schema::String, RootSchema, ToDecode> { + static error_or<void> decode(buffer_view& buff, data<schema::String, ToDecode>& to){ + assert(buff.read() == '"'); + buff.read_advance(1); + + std::stringstream iss; + bool string_done = false; + while(!string_done){ + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + + switch(buff.read()){ + case '\\':{ + buff.read_advance(1); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + switch(buff.read()){ + case '\\': + case '/': + case '"': + iss<< buff.read(); + break; + case 'b': + iss<<'\b'; + break; + case 'f': + iss<<'\f'; + break; + case 'n': + iss<<'\n'; + break; + case 'r': + iss<<'\r'; + break; + case 't': + iss<<'\t'; + break; + case 'u': { + buff.read_advance(1); + if(buff.read_composite_length() < 4){ + return make_error<err::buffer_exhausted>(); + } + iss<<'?'; + iss<<'?'; + iss<<'?'; + iss<<'?'; + + buff.read_advance(3); + } break; + } + } break; + case '"': + string_done = true; + break; + default:{ + // Avoids Control sequences + if(buff.read() >= ' ' && buff.read() <= '~'){ + iss<<buff.read(); + } + // Avoid Unicode + else if (buff.read() < 0) { + do { + buff.read_advance(1); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + } while( buff.read() < 0 ); + iss<<'?'; + } + break; + } + } + buff.read_advance(1); + } + + std::string raw = iss.str(); + to.set(std::move(raw)); + + return void_t{}; + } +}; + +template<typename... T, string_literal... Lits, typename RootSchema, typename ToDecode> +struct json_decode<schema::Struct<schema::Member<T,Lits>...>, RootSchema, ToDecode> { + template<std::size_t i> + static error_or<void> decode_field_search(buffer_view& buff, data<schema::Struct<schema::Member<T,Lits>...>, ToDecode>& to, std::array<bool, sizeof...(T)>& fields, const data<schema::String,ToDecode>& search_name){ + if constexpr ( i < sizeof...(T)){ + using Type = typename parameter_pack_type<i, T...>::type; + constexpr static string_literal Literal = parameter_key_pack_type<i, Lits...>::literal; + if(search_name == Literal.view()){ + if(fields[i]){ + return make_error<err::invalid_state>(); + } + fields[i] = true; + auto eov = json_decode<Type, RootSchema, ToDecode>::decode(buff, to.template get<Literal>()); + if(eov.is_error()){ + return eov; + } + }else { + decode_field_search<i+1>(buff, to, fields, search_name); + } + }else { + return make_error<err::invalid_state>(); + } + return void_t{}; + } + + static error_or<void> decode_fields(buffer_view& buff, data<schema::Struct<schema::Member<T,Lits>...>, ToDecode>& to, std::array<bool, sizeof...(T)>& fields){ + for(;;){ + data<schema::String, ToDecode> name; + auto eov = json_decode<schema::String, RootSchema, ToDecode>::decode(buff, name); + if(eov.is_error()){ + return eov; + } + json_helper::skip_whitespace(buff); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + if(buff.read() != ':'){ + return make_error<err::invalid_state>(); + } + buff.read_advance(1); + json_helper::skip_whitespace(buff); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + { + auto eov = decode_field_search<0>(buff, to, fields, name); + if(eov.is_error()){ + return eov; + } + } + json_helper::skip_whitespace(buff); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + if(buff.read() == ','){ + buff.read_advance(1); + }else if(buff.read() == '}'){ + // If not all fields are set, the dataset is incomplete + for(auto& iter : fields){ + if(!iter){ + return make_error<err::invalid_state>(); + } + } + buff.read_advance(1); + return void_t{}; + }else{ + return make_error<err::invalid_state>(); + } + json_helper::skip_whitespace(buff); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + } + return void_t{}; + } + + static error_or<void> decode(buffer_view& buff, data<schema::Struct<schema::Member<T,Lits>...>, ToDecode>& to){ + std::array<bool, sizeof...(T)> found_fields; + std::fill(found_fields.begin(), found_fields.end(), false); + + assert(buff.read() == '{'); + buff.read_advance(1); + json_helper::skip_whitespace(buff); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + + // Check if there are no elements present in the JSON Struct + if(buff.read() == '}'){ + if(sizeof...(T) > 0){ + return make_error<err::invalid_state>(); + } + buff.read_advance(1); + return void_t{}; + } + + auto eov = decode_fields(buff, to, found_fields); + if(eov.is_error()){ + return eov; + } + + return void_t{}; + } +}; + +template<typename... T, typename RootSchema, typename ToDecode> +struct json_decode<schema::Tuple<T...>, RootSchema, ToDecode> { + template<std::size_t i> + static error_or<void> decode_element(buffer_view& buff, data<schema::Tuple<T...>, ToDecode>& to){ + if constexpr (i < sizeof...(T)){ + if constexpr ( i > 0 ){ + if(buff.read() != ','){ + return make_error<err::invalid_state>(); + } + buff.read_advance(1); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + } + using Type = typename parameter_pack_type<i, T...>::type; + + auto eov = json_decode<Type, RootSchema, ToDecode>::decode(buff, to.template get<i>()); + if(eov.is_error()){ + return eov; + } + json_helper::skip_whitespace(buff); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + + eov = decode_element<i+1>(buff, to); + if(eov.is_error()){ + return eov; + } + }else{ + if(buff.read() != ']'){ + return make_error<err::invalid_state>(); + } + buff.read_advance(1); + } + return void_t{}; + } + + static error_or<void> decode(buffer_view& buff, data<schema::Tuple<T...>, ToDecode>& to){ + assert(buff.read() == '['); + buff.read_advance(1); + + json_helper::skip_whitespace(buff); + if(buff.read_composite_length() == 0){ + return make_error<err::buffer_exhausted>(); + } + + auto eov = decode_element<0>(buff, to); + if(eov.is_error()){ + return eov; + } + + return void_t{}; + } +}; + +// The whole std::vector approach is hacky af. ToDo change it maybe? +template<typename T, size_t D, typename RootSchema, typename ToDecode> +struct json_decode<schema::Array<T,D>, RootSchema, ToDecode> { + template<size_t Level> + static error_or<void> decode_flat_level(buffer_view& buff, std::vector<data<T, encode::Native>>& to, std::array<std::size_t, D>& index, std::array<std::size_t, D>& dims, bool log_dim){ + if constexpr (Level == D) { + json_helper::skip_whitespace(buff); + try { + to.push_back({}); + }catch(std::exception& e){ + return make_error<err::out_of_memory>(); + } + auto eov = json_decode<T, RootSchema, ToDecode>::decode(buff, to.back()); + if(eov.is_error()){ + return eov; + } + } else { + assert(buff.read() == '['); + buff.read_advance(1); + + json_helper::skip_whitespace(buff); + if ( buff.read_composite_length() == 0 ){ + return make_error<err::buffer_exhausted>(); + } + + index[Level] = 0; + for(;;){ + // We should have an element right now + auto eov = decode_flat_level<Level+1>(buff,to,index,dims, index[Level] == 0 && log_dim); + if(eov.is_error()){ + return eov; + } + json_helper::skip_whitespace(buff); + if ( buff.read_composite_length() == 0 ){ + return make_error<err::buffer_exhausted>(); + } + + ++index[Level]; + if(buff.read() == ','){ + buff.read_advance(1); + } else if(buff.read() == ']'){ + buff.read_advance(1); + break; + } else { + return make_error<err::invalid_state>(); + } + json_helper::skip_whitespace(buff); + if ( buff.read_composite_length() == 0 ){ + return make_error<err::buffer_exhausted>(); + } + } + if(log_dim){ + dims[Level] = index[Level]; + }else if (dims[Level] != index[Level]){ + return make_error<err::invalid_state>(); + } + } + return void_t{}; + } + + template<std::size_t Level> + static error_or<void> decode_unflat_level(std::vector<data<T,encode::Native>>& flat, data<schema::Array<T,D>, ToDecode>& to, std::array<std::size_t, D>& index, std::size_t& flat_index) { + if constexpr ( Level == D ){ + auto& flat_data = flat.at(flat_index); + to.at(index) = std::move(flat_data); + ++flat_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_unflat_level<Level+1>(flat, to, index, flat_index); + if(eov.is_error()){ + return eov; + } + } + } + return void_t{}; + } + + static error_or<void> decode(buffer_view& buff, data<schema::Array<T,D>, ToDecode>& to){ + std::array<std::size_t, D> index; + std::array<std::size_t, D> dims; + std::fill(dims.begin(), dims.end(), 0); + std::vector<data<T,encode::Native>> flat_array; + auto eov = decode_flat_level<0>(buff, flat_array, index, dims, true); + if(eov.is_error()){ + return eov; + } + + to = {dims}; + std::size_t flat_index = 0; + + return decode_unflat_level<0>(flat_array, to, index, flat_index); + } +}; +} +} |