diff options
author | Claudius Holeksa <mail@keldu.de> | 2023-06-26 15:25:29 +0200 |
---|---|---|
committer | Claudius Holeksa <mail@keldu.de> | 2023-06-26 15:25:29 +0200 |
commit | 44b97c0c13c3cb05a5fed70326285b45bc7b37a6 (patch) | |
tree | bf26986e1b8f7415e4e3b72301cdde521efa5b6a | |
parent | 9b5fdb05609c24a03d4e0e4386e9eadbfe6c5405 (diff) |
c++,codec: Added kelsimple array and struct decoding / encoding
-rw-r--r-- | src/codec/data.h | 14 | ||||
-rw-r--r-- | src/codec/simple.h | 166 | ||||
-rw-r--r-- | src/codec/stream_value.h | 34 | ||||
-rw-r--r-- | tests/codec.cpp | 114 |
4 files changed, 303 insertions, 25 deletions
diff --git a/src/codec/data.h b/src/codec/data.h index 5cb1267..a43fdd8 100644 --- a/src/codec/data.h +++ b/src/codec/data.h @@ -195,16 +195,18 @@ class data<schema::Array<T,Dim>, encode::Native> { 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_): - dims_{size_...}, - value_{} + data{{size_...}} { - for(auto& iter : dims_){ - assert(iter > 0); - } static_assert(sizeof...(Dims)==Dim, "Argument size must be equal to the Dimension"); - value_.resize(get_full_size()); } data<T, encode::Native>& at(const std::array<std::size_t, Dim>& ind){ diff --git a/src/codec/simple.h b/src/codec/simple.h index 1b6cf0d..ad66f66 100644 --- a/src/codec/simple.h +++ b/src/codec/simple.h @@ -1,6 +1,12 @@ #pragma once #include "data.h" +#include "stream_value.h" + +#include <iostream> + +#include <forstio/core/buffer.h> +#include <forstio/core/error.h> namespace saw { namespace encode { @@ -15,7 +21,7 @@ public: data() = default; buffer& get_buffer(){ - return buffer; + return buffer_; } }; @@ -25,13 +31,152 @@ 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 Schema, typename FromEnc> +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 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 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 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); + } + }; } @@ -54,9 +199,20 @@ public: 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 = kelsimple_encode::encode(from_env, buff_v); + auto eov = impl::kelsimple_encode<Schema, FromEnc>::encode(from_enc, buff_v); - return void_t{}; + 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/src/codec/stream_value.h b/src/codec/stream_value.h index df1334b..09203cb 100644 --- a/src/codec/stream_value.h +++ b/src/codec/stream_value.h @@ -1,7 +1,9 @@ #pragma once -#include "buffer.h" -#include "error.h" +#include "schema.h" + +#include <forstio/core/buffer.h> +#include <forstio/core/error.h> #include <cstdint> #include <cstring> @@ -13,44 +15,48 @@ namespace saw { * platform independent. So it does not matter if the memory layout is * little endian or big endian */ -template <typename T, size_t N> class ShiftStreamValue { +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() < sizeof(T)) { + 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 < sizeof(T); ++i) { - raw |= (static_cast<uint64_t>(buff.read(i)) << (i * 8)); + 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, sizeof(T)); - buff.read_advance(sizeof(T)); + 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(sizeof(T)); + 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, sizeof(T)); + memcpy(&raw, &val, N); - for (size_t i = 0; i < sizeof(T); ++i) { - buffer.write(i) = raw >> (i * 8); + for (size_t i = 0; i < N; ++i) { + buff.write(i) = raw >> (i * 8); } - buffer.write_advance(sizeof(T)); + buff.write_advance(N); return void_t{}; } - inline static size_t size() const { return N; } + inline static size_t size() { return N; } }; template <typename T> using stream_value = shift_stream_value<T>; diff --git a/tests/codec.cpp b/tests/codec.cpp index 09e7228..5186df3 100644 --- a/tests/codec.cpp +++ b/tests/codec.cpp @@ -1,5 +1,6 @@ #include <forstio/test/suite.h> #include <forstio/codec/data.h> +#include <forstio/codec/simple.h> namespace { namespace schema { @@ -10,6 +11,10 @@ using OneDimArray = Array<Int32,1>; using TwoDimArray = Array<Int32,2>; using ThreeDimArray = Array<Int32,3>; +using TestStruct = Struct< + Member<TwoDimArray, "two_dim_array">, + Member<UInt64, "number"> +>; } SAW_TEST("One Dimensional Array") { using namespace saw; @@ -75,4 +80,113 @@ SAW_TEST("Three Dimensional Array") { } SAW_EXPECT(sum == 124750, std::to_string(sum) + " is not 124750. Expected that data stays correct"); } + +SAW_TEST("KelSimple UInt16 write"){ + using namespace saw; + data<schema::UInt16, encode::Native> native; + data<schema::UInt16, encode::KelSimple> simple; + + codec<schema::UInt16, encode::KelSimple> codec; + + auto& buff = simple.get_buffer(); + + for(uint16_t i = 0; i < 256; ++i){ + for(uint16_t j = 0; j < 256; ++j){ + native.set(i + j * 256); + error_or<void> eov = codec.encode(native, simple); + SAW_EXPECT(eov.is_value(), "Encoding error"); + + SAW_EXPECT(buff.read_composite_length() == 2, "Written incorrect size"); + SAW_EXPECT((buff.read(0) == i && buff.read(1) == j), std::string{"Incorrect values in encoding: "} + std::to_string(static_cast<uint16_t>(buff.read(0))) + " " + std::to_string(static_cast<uint16_t>(buff.read(1)))); + buff.read_advance(2); + + } + } +} + +SAW_TEST("KelSimple UInt32 write"){ + using namespace saw; + data<schema::UInt32, encode::Native> native; + data<schema::UInt32, encode::KelSimple> simple; + + codec<schema::UInt32, encode::KelSimple> codec; + + auto& buff = simple.get_buffer(); + + for(uint16_t i = 0; i < 256; ++i){ + for(uint16_t j = 0; j < 256; ++j){ + native.set(i + j * 256 * 256); + error_or<void> eov = codec.encode(native, simple); + SAW_EXPECT(eov.is_value(), "Encoding error"); + + SAW_EXPECT(buff.read_composite_length() == 4, "Written incorrect size"); + SAW_EXPECT((buff.read(0) == i && buff.read(1) == 0 && buff.read(2) == j && buff.read(3) == 0), std::string{"Incorrect values in encoding: "} + std::to_string(static_cast<uint16_t>(buff.read(0))) + " " + std::to_string(static_cast<uint16_t>(buff.read(1)))); + buff.read_advance(4); + + } + } +} + +SAW_TEST("KelSimple Array write and read back"){ + using namespace saw; + data<schema::TwoDimArray, encode::Native> native{2,3}; + data<schema::TwoDimArray, encode::KelSimple> simple; + + codec<schema::TwoDimArray, encode::KelSimple> codec; + + for(std::size_t i = 0; i < 2; ++i) { + for(std::size_t j = 0; j < 3; ++j){ + native.at(i,j).set(i+2*j); + } + } + + auto eov = codec.encode(native,simple); + SAW_EXPECT(eov.is_value(), "Encoding error"); + + for(std::size_t i = 0; i < 2; ++i) { + for(std::size_t j = 0; j < 3; ++j){ + native.at(i,j).set(0); + } + } + + eov = codec.decode(simple, native); + SAW_EXPECT(eov.is_value(), "Decoding error"); + + for(std::size_t i = 0; i < 2; ++i) { + for(std::size_t j = 0; j < 3; ++j){ + SAW_EXPECT(native.at(i,j).get() == static_cast<int32_t>(i+2*j), "Values incorrectly decoded"); + } + } +} + +SAW_TEST("KelSimple Struct write and read back"){ + using namespace saw; + + data<schema::TestStruct,encode::Native> native; + data<schema::TestStruct,encode::KelSimple> simple; + + auto& tda = native.template get<"two_dim_array">(); + tda = {1,2}; + + tda.at(0,0).set(5); + tda.at(0,1).set(3); + native.template get<"number">().set(410); + + codec<schema::TestStruct, encode::KelSimple> codec; + + auto eov = codec.encode(native, simple); + SAW_EXPECT(eov.is_value(), "Encoding error"); + + // Reset values + native = {}; + + eov = codec.decode(simple, native); + SAW_EXPECT(eov.is_value(), "Decoding error"); + + auto& dec_tda = native.template get<"two_dim_array">(); + + SAW_EXPECT(dec_tda.at(0,0).get() == 5, "Incorrect Decoding in array 0,0"); + SAW_EXPECT(dec_tda.at(0,1).get() == 3, "Incorrect Decoding in array 0,1"); + SAW_EXPECT(native.template get<"number">().get() == 410, "Incorrect Decoding in number"); +} } |