forstio/source/forstio/proto_kel.h

588 lines
16 KiB
C++

#pragma once
#include "buffer.h"
#include "message.h"
#include "stream_endian.h"
namespace saw {
class proto_kel_codec {
public:
using UnionIdT = uint32_t;
using ArrayLengthT = uint64_t;
using PacketLengthT = uint64_t;
private:
struct read_context {
buffer &buffer;
size_t offset = 0;
};
template <typename T> friend struct proto_kel_encode_impl;
template <typename T> friend struct proto_kel_decode_impl;
public:
struct limits {
proto_kel_codec::PacketLengthT packet_size;
limits() : packet_size{4096} {}
limits(proto_kel_codec::PacketLengthT ps) : packet_size{ps} {}
};
struct version {
size_t major;
size_t minor;
size_t security;
};
const version get_version() const { return version{0, 0, 0}; }
template <class Schema, class Container = message_container<Schema>>
error encode(typename message<Schema, Container>::reader reader,
buffer &buffer);
template <class Schema, class Container = message_container<Schema>>
error decode(typename message<Schema, Container>::builder builder,
buffer &buffer, const limits &lim = limits{});
};
template <class T> struct proto_kel_encode_impl;
template <class T, size_t N, class Container>
struct proto_kel_encode_impl<message<schema::Primitive<T, N>, Container>> {
static error
encode(typename message<schema::Primitive<T, N>, Container>::reader data,
buffer &buffer) {
error err = stream_value<typename PrimitiveTypeHelper<
schema::Primitive<T, N>>::Type>::encode(data.get(), buffer);
return err;
}
static size_t
size(typename message<schema::Primitive<T, N>, Container>::reader) {
return stream_value<typename PrimitiveTypeHelper<
schema::Primitive<T, N>>::Type>::size();
}
};
template <class Container>
struct proto_kel_encode_impl<message<schema::String, Container>> {
static error
encode(typename message<schema::String, Container>::reader data,
buffer &buffer) {
std::string_view view = data.get();
size_t size = view.size();
error err = buffer.write_require_length(sizeof(size) + size);
if (err.failed()) {
return err;
}
err = stream_value<size_t>::encode(size, buffer);
if (err.failed()) {
return err;
}
for (size_t i = 0; i < view.size(); ++i) {
buffer.write(i) = view[i];
}
buffer.write_advance(view.size());
return no_error();
}
static size_t
size(typename message<schema::String, Container>::reader reader) {
return sizeof(size_t) + reader.get().size();
}
};
template <class... T, class Container>
struct proto_kel_encode_impl<message<schema::Tuple<T...>, Container>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(T), error>::type
encode_members(typename message<schema::Tuple<T...>, Container>::reader,
buffer &) {
return no_error();
}
template <size_t i = 0>
static typename std::enable_if<(i < sizeof...(T)), error>::type
encode_members(
typename message<schema::Tuple<T...>, Container>::reader data,
buffer &buffer) {
error err =
proto_kel_encode_impl<typename Container::template ElementType<i>>::
encode(data.template get<i>(), buffer);
if (err.failed()) {
return err;
}
return encode_members<i + 1>(data, buffer);
}
static error
encode(typename message<schema::Tuple<T...>, Container>::reader data,
buffer &buffer) {
return encode_members<0>(data, buffer);
}
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(T), size_t>::type
size_members(typename message<schema::Tuple<T...>, Container>::reader) {
return 0;
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(T), size_t>::type size_members(
typename message<schema::Tuple<T...>, Container>::reader reader) {
return proto_kel_encode_impl<typename Container::template ElementType<
i>>::size(reader.template get<i>()) +
size_members<i + 1>(reader);
}
static size_t
size(typename message<schema::Tuple<T...>, Container>::reader reader) {
return size_members<0>(reader);
}
};
template <typename... V, string_literal... K, class Container>
struct proto_kel_encode_impl<
message<schema::Struct<schema::NamedMember<V, K>...>, Container>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), error>::type
encode_members(
typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::reader,
buffer &) {
return no_error();
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), error>::type encode_members(
typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::reader data,
buffer &buffer) {
error err =
proto_kel_encode_impl<typename Container::template ElementType<i>>::
encode(data.template get<i>(), buffer);
if (err.failed()) {
return err;
}
return encode_members<i + 1>(data, buffer);
}
static error
encode(typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::reader data,
buffer &buffer) {
return encode_members<0>(data, buffer);
}
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), size_t>::type
size_members(typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::reader) {
return 0;
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), size_t>::type size_members(
typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::reader reader) {
return proto_kel_encode_impl<typename Container::template ElementType<
i>>::size(reader.template get<i>()) +
size_members<i + 1>(reader);
}
static size_t
size(typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::reader reader) {
return size_members<0>(reader);
}
};
template <typename... V, string_literal... K, class Container>
struct proto_kel_encode_impl<
message<schema::Union<schema::NamedMember<V, K>...>, Container>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), error>::type
encode_members(typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::reader,
buffer &) {
return no_error();
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), error>::type encode_members(
typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::reader reader,
buffer &buffer) {
if (reader.index() == i) {
error err =
stream_value<proto_kel_codec::UnionIdT>::encode(i, buffer);
if (err.failed()) {
return err;
}
return proto_kel_encode_impl<
typename Container::template ElementType<i>>::
encode(reader.template get<i>(), buffer);
}
return encode_members<i + 1>(reader, buffer);
}
static error
encode(typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::reader reader,
buffer &buffer) {
return encode_members<0>(reader, buffer);
}
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), size_t>::type
size_members(typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::reader) {
return 0;
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), size_t>::type size_members(
typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::reader reader) {
if (reader.index() == i) {
return proto_kel_encode_impl<
typename Container::template ElementType<i>>::
size(reader.template get<i>());
}
return size_members<i + 1>(reader);
}
/*
* Size of union id + member size
*/
static size_t
size(typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::reader reader) {
return sizeof(proto_kel_codec::UnionIdT) + size_members<0>(reader);
}
};
template <class T, class Container>
struct proto_kel_encode_impl<message<schema::Array<T>, Container>> {
static error
encode(typename message<schema::Array<T>, Container>::reader data,
buffer &buffer) {
proto_kel_codec::ArrayLengthT array_length = data.size();
{
error err = stream_value<proto_kel_codec::ArrayLengthT>::encode(
array_length, buffer);
if (err.failed()) {
return err;
}
}
for (size_t i = 0; i < array_length; ++i) {
error err =
proto_kel_encode_impl<typename Container::ElementType>::encode(
data.get(i), buffer);
if (err.failed()) {
return err;
}
}
return no_error();
}
/*
*
*/
static size_t
size(typename message<schema::Array<T>, Container>::reader data) {
size_t members = sizeof(proto_kel_codec::ArrayLengthT);
for (size_t i = 0; i < data.size(); ++i) {
members +=
proto_kel_encode_impl<typename Container::ElementType>::size(
data.get(i));
}
return members;
}
};
/*
* Decode Implementations
*/
template <typename T> struct proto_kel_decode_impl;
template <class T, size_t N, class Container>
struct proto_kel_decode_impl<message<schema::Primitive<T, N>, Container>> {
static error
decode(typename message<schema::Primitive<T, N>, Container>::builder data,
buffer &buffer) {
typename PrimitiveTypeHelper<schema::Primitive<T, N>>::Type val = 0;
error err = stream_value<typename PrimitiveTypeHelper<
schema::Primitive<T, N>>::Type>::decode(val, buffer);
data.set(val);
return err;
}
};
template <class Container>
struct proto_kel_decode_impl<message<schema::String, Container>> {
static error
decode(typename message<schema::String, Container>::builder data,
buffer &buffer) {
size_t size = 0;
if (sizeof(size) > buffer.read_composite_length()) {
return recoverable_error("Buffer too small");
}
error err = stream_value<size_t>::decode(size, buffer);
if (err.failed()) {
return err;
}
if (size > buffer.read_composite_length()) {
return recoverable_error("Buffer too small");
}
std::string value;
value.resize(size);
if (size > buffer.read_composite_length()) {
return recoverable_error("Buffer too small");
}
for (size_t i = 0; i < value.size(); ++i) {
value[i] = buffer.read(i);
}
buffer.read_advance(value.size());
data.set(std::move(value));
return no_error();
}
};
template <class... T, class Container>
struct proto_kel_decode_impl<message<schema::Tuple<T...>, Container>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(T), error>::type
decode_members(typename message<schema::Tuple<T...>, Container>::builder,
buffer &) {
return no_error();
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(T), error>::type decode_members(
typename message<schema::Tuple<T...>, Container>::builder builder,
buffer &buffer) {
error err =
proto_kel_decode_impl<typename Container::template ElementType<i>>::
decode(builder.template init<i>(), buffer);
if (err.failed()) {
return err;
}
return decode_members<i + 1>(builder, buffer);
}
static error
decode(typename message<schema::Tuple<T...>, Container>::builder builder,
buffer &buffer) {
return decode_members<0>(builder, buffer);
}
};
template <class... V, string_literal... K, class Container>
struct proto_kel_decode_impl<
message<schema::Struct<schema::NamedMember<V, K>...>, Container>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), error>::type
decode_members(
typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::builder,
buffer &) {
return no_error();
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), error>::type decode_members(
typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::builder builder,
buffer &buffer) {
error err =
proto_kel_decode_impl<typename Container::template ElementType<i>>::
decode(builder.template init<i>(), buffer);
if (err.failed()) {
return err;
}
return decode_members<i + 1>(builder, buffer);
}
static error
decode(typename message<schema::Struct<schema::NamedMember<V, K>...>,
Container>::builder builder,
buffer &buffer) {
return decode_members<0>(builder, buffer);
}
};
template <class... V, string_literal... K, class Container>
struct proto_kel_decode_impl<
message<schema::Union<schema::NamedMember<V, K>...>, Container>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), error>::type
decode_members(typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::builder,
buffer &, proto_kel_codec::UnionIdT) {
return no_error();
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), error>::type decode_members(
typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::builder builder,
buffer &buffer, proto_kel_codec::UnionIdT id) {
if (id == i) {
error err =
proto_kel_decode_impl<typename Container::template ElementType<
i>>::decode(builder.template init<i>(), buffer);
if (err.failed()) {
return err;
}
}
return decode_members<i + 1>(builder, buffer, id);
}
static error
decode(typename message<schema::Union<schema::NamedMember<V, K>...>,
Container>::builder builder,
buffer &buffer) {
proto_kel_codec::UnionIdT id = 0;
error err = stream_value<proto_kel_codec::UnionIdT>::decode(id, buffer);
if (err.failed()) {
return err;
}
if (id >= sizeof...(V)) {
return critical_error("Union doesn't have this many id's");
}
return decode_members<0>(builder, buffer, id);
}
};
template <class T, class Container>
struct proto_kel_decode_impl<message<schema::Array<T>, Container>> {
static error
decode(typename message<schema::Array<T>, Container>::builder data,
buffer &buffer) {
proto_kel_codec::ArrayLengthT array_length = 0;
{
error err = stream_value<proto_kel_codec::ArrayLengthT>::decode(
array_length, buffer);
if (err.failed()) {
return err;
}
}
data.resize(array_length);
for (size_t i = 0; i < array_length; ++i) {
error err =
proto_kel_decode_impl<typename Container::ElementType>::decode(
data.init(i), buffer);
if (err.failed()) {
return err;
}
}
return no_error();
}
};
template <class Schema, class Container>
error proto_kel_codec::encode(
typename message<Schema, Container>::reader reader, buffer &buffer) {
buffer_view view{buffer};
proto_kel_codec::PacketLengthT packet_length =
proto_kel_encode_impl<message<Schema, Container>>::size(reader);
// Check the size of the packet for the first
// message length description
error err = view.write_require_length(
packet_length + sizeof(proto_kel_codec::PacketLengthT));
if (err.failed()) {
return err;
}
{
error err = stream_value<proto_kel_codec::PacketLengthT>::encode(
packet_length, view);
if (err.failed()) {
return err;
}
}
{
error err = proto_kel_encode_impl<message<Schema, Container>>::encode(
reader, view);
if (err.failed()) {
return err;
}
}
buffer.write_advance(view.write_offset());
return no_error();
}
template <class Schema, class Container>
error proto_kel_codec::decode(
typename message<Schema, Container>::builder builder, buffer &buffer,
const limits &limits) {
buffer_view view{buffer};
proto_kel_codec::PacketLengthT packet_length = 0;
{
error err = stream_value<proto_kel_codec::PacketLengthT>::decode(
packet_length, view);
if (err.failed()) {
return err;
}
}
if (packet_length > limits.packet_size) {
return critical_error(
[packet_length]() {
return std::string{"Packet size too big: "} +
std::to_string(packet_length);
},
"Packet size too big");
}
{
error err = proto_kel_decode_impl<message<Schema, Container>>::decode(
builder, view);
if (err.failed()) {
return err;
}
}
{
if (proto_kel_encode_impl<message<Schema, Container>>::size(
builder.as_reader()) != packet_length) {
return critical_error("Bad packet format");
}
}
buffer.read_advance(view.read_offset());
return no_error();
}
} // namespace saw