423 lines
11 KiB
C++
423 lines
11 KiB
C++
#pragma once
|
|
|
|
#include "buffer.h"
|
|
#include "message.h"
|
|
#include "stream_endian.h"
|
|
|
|
#include <iostream>
|
|
|
|
namespace gin {
|
|
/// @todo replace types with these
|
|
/*
|
|
* I'm not really sure if anyone will use a union which is
|
|
* bigger than uint32_t max. At least I hope noone would do this
|
|
*/
|
|
using msg_union_id_t = uint32_t;
|
|
using msg_packet_length_t = uint64_t;
|
|
|
|
template <typename T> struct ProtoKelEncodeImpl;
|
|
|
|
template <typename T> struct ProtoKelEncodeImpl<MessagePrimitive<T>> {
|
|
static Error encode(typename MessagePrimitive<T>::Reader data,
|
|
Buffer &buffer) {
|
|
Error error = StreamValue<T>::encode(data.get(), buffer);
|
|
return error;
|
|
}
|
|
|
|
static size_t size(typename MessagePrimitive<T>::Reader) {
|
|
return StreamValue<T>::size();
|
|
}
|
|
};
|
|
|
|
template <> struct ProtoKelEncodeImpl<MessagePrimitive<std::string>> {
|
|
static Error encode(typename MessagePrimitive<std::string>::Reader data,
|
|
Buffer &buffer) {
|
|
std::string_view view = data.get();
|
|
size_t size = view.size();
|
|
if ((sizeof(size) + size) > buffer.writeCompositeLength()) {
|
|
return recoverableError("Buffer too small");
|
|
}
|
|
Error error = StreamValue<size_t>::encode(size, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
for (size_t i = 0; i < view.size(); ++i) {
|
|
buffer.write(i) = view[i];
|
|
}
|
|
buffer.writeAdvance(view.size());
|
|
return noError();
|
|
}
|
|
|
|
static size_t size(typename MessagePrimitive<std::string>::Reader reader) {
|
|
return sizeof(size_t) + reader.get().size();
|
|
}
|
|
};
|
|
|
|
template <typename... T> struct ProtoKelEncodeImpl<MessageList<T...>> {
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(T), Error>::type
|
|
encodeMembers(typename MessageList<T...>::Reader, Buffer &) {
|
|
return noError();
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(T), Error>::type
|
|
encodeMembers(typename MessageList<T...>::Reader data, Buffer &buffer) {
|
|
Error error =
|
|
ProtoKelEncodeImpl<typename ParameterPackType<i, T...>::type>::
|
|
encode(data.template get<i>(), buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
|
|
return encodeMembers<i + 1>(data, buffer);
|
|
}
|
|
|
|
static Error encode(typename MessageList<T...>::Reader data,
|
|
Buffer &buffer) {
|
|
return encodeMembers<0>(data, buffer);
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(T), size_t>::type
|
|
sizeMembers(typename MessageList<T...>::Reader data) {
|
|
return 0;
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(T), size_t>::type
|
|
sizeMembers(typename MessageList<T...>::Reader reader) {
|
|
return ProtoKelEncodeImpl<typename ParameterPackType<i, T...>::type>::
|
|
size(reader.template get<i>()) +
|
|
sizeMembers<i + 1>(reader);
|
|
}
|
|
|
|
static size_t size(typename MessageList<T...>::Reader reader) {
|
|
return sizeMembers<0>(reader);
|
|
}
|
|
};
|
|
|
|
template <typename... V, typename... K>
|
|
struct ProtoKelEncodeImpl<MessageStruct<MessageStructMember<V, K>...>> {
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(V), Error>::type
|
|
encodeMembers(typename MessageStruct<MessageStructMember<V, K>...>::Reader,
|
|
Buffer &) {
|
|
return noError();
|
|
}
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(V), Error>::type encodeMembers(
|
|
typename MessageStruct<MessageStructMember<V, K>...>::Reader data,
|
|
Buffer &buffer) {
|
|
|
|
Error error =
|
|
ProtoKelEncodeImpl<typename ParameterPackType<i, V...>::type>::
|
|
encode(data.template get<i>(), buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
return encodeMembers<i + 1>(data, buffer);
|
|
}
|
|
|
|
static Error
|
|
encode(typename MessageStruct<MessageStructMember<V, K>...>::Reader data,
|
|
Buffer &buffer) {
|
|
return encodeMembers<0>(data, buffer);
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(V), size_t>::type
|
|
sizeMembers(typename MessageStruct<MessageStructMember<V, K>...>::Reader) {
|
|
return 0;
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(V), size_t>::type
|
|
sizeMembers(typename MessageStruct<MessageStructMember<V, K>...>::Reader
|
|
reader) {
|
|
return ProtoKelEncodeImpl<typename ParameterPackType<i, V...>::type>::
|
|
size(reader.template get<i>()) +
|
|
sizeMembers<i + 1>(reader);
|
|
}
|
|
|
|
static size_t
|
|
size(typename MessageStruct<MessageStructMember<V, K>...>::Reader reader) {
|
|
return sizeMembers<0>(reader);
|
|
}
|
|
};
|
|
|
|
template <typename... V, typename... K>
|
|
struct ProtoKelEncodeImpl<MessageUnion<MessageUnionMember<V, K>...>> {
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(V), Error>::type
|
|
encodeMembers(typename MessageUnion<MessageUnionMember<V, K>...>::Reader,
|
|
Buffer &) {
|
|
return noError();
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(V), Error>::type encodeMembers(
|
|
typename MessageUnion<MessageUnionMember<V, K>...>::Reader reader,
|
|
Buffer &buffer) {
|
|
if (reader.template holdsAlternative<
|
|
typename ParameterPackType<i, K...>::type>()) {
|
|
Error error = StreamValue<msg_union_id_t>::encode(i, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
return ProtoKelEncodeImpl<typename ParameterPackType<
|
|
i, V...>::type>::encode(reader.template get<i>(), buffer);
|
|
}
|
|
return encodeMembers<i + 1>(reader, buffer);
|
|
}
|
|
|
|
static Error
|
|
encode(typename MessageUnion<MessageUnionMember<V, K>...>::Reader reader,
|
|
Buffer &buffer) {
|
|
return encodeMembers<0>(reader, buffer);
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(V), size_t>::type
|
|
sizeMembers(typename MessageUnion<MessageUnionMember<V, K>...>::Reader) {
|
|
return 0;
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(V), size_t>::type sizeMembers(
|
|
typename MessageUnion<MessageUnionMember<V, K>...>::Reader reader) {
|
|
if (reader.template holdsAlternative<
|
|
typename ParameterPackType<i, K...>::type>()) {
|
|
return ProtoKelEncodeImpl<typename ParameterPackType<
|
|
i, V...>::type>::size(reader.template get<i>());
|
|
}
|
|
return sizeMembers<i + 1>(reader);
|
|
}
|
|
|
|
/*
|
|
* Size of union id + member size
|
|
*/
|
|
static size_t
|
|
size(typename MessageUnion<MessageUnionMember<V, K>...>::Reader reader) {
|
|
return sizeof(uint32_t) + sizeMembers<0>(reader);
|
|
}
|
|
};
|
|
/*
|
|
* Decode Implementations
|
|
*/
|
|
template <typename T> struct ProtoKelDecodeImpl;
|
|
|
|
template <typename T> struct ProtoKelDecodeImpl<MessagePrimitive<T>> {
|
|
static Error decode(typename MessagePrimitive<T>::Builder data,
|
|
Buffer &buffer) {
|
|
T val = 0;
|
|
Error error = StreamValue<T>::decode(val, buffer);
|
|
data.set(val);
|
|
return error;
|
|
}
|
|
};
|
|
|
|
template <> struct ProtoKelDecodeImpl<MessagePrimitive<std::string>> {
|
|
static Error decode(typename MessagePrimitive<std::string>::Builder data,
|
|
Buffer &buffer) {
|
|
size_t size = 0;
|
|
if (sizeof(size) > buffer.readCompositeLength()) {
|
|
return recoverableError("Buffer too small");
|
|
}
|
|
|
|
Error error = StreamValue<size_t>::decode(size, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
|
|
if (size > buffer.readCompositeLength()) {
|
|
return recoverableError("Buffer too small");
|
|
}
|
|
|
|
std::string value;
|
|
value.resize(size);
|
|
|
|
if (size > buffer.readCompositeLength()) {
|
|
return recoverableError("Buffer too small");
|
|
}
|
|
for (size_t i = 0; i < value.size(); ++i) {
|
|
value[i] = buffer.read(i);
|
|
}
|
|
buffer.readAdvance(value.size());
|
|
data.set(std::move(value));
|
|
return noError();
|
|
}
|
|
};
|
|
|
|
template <typename... T> struct ProtoKelDecodeImpl<MessageList<T...>> {
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(T), Error>::type
|
|
decodeMembers(typename MessageList<T...>::Builder, Buffer &) {
|
|
return noError();
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(T), Error>::type
|
|
decodeMembers(typename MessageList<T...>::Builder builder,
|
|
Buffer &buffer) {
|
|
|
|
Error error =
|
|
ProtoKelDecodeImpl<typename ParameterPackType<i, T...>::type>::
|
|
decode(builder.template init<i>(), buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
return decodeMembers<i + 1>(builder, buffer);
|
|
}
|
|
|
|
static Error decode(typename MessageList<T...>::Builder builder,
|
|
Buffer &buffer) {
|
|
return decodeMembers<0>(builder, buffer);
|
|
}
|
|
};
|
|
|
|
template <typename... V, typename... K>
|
|
struct ProtoKelDecodeImpl<MessageStruct<MessageStructMember<V, K>...>> {
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(V), Error>::type
|
|
decodeMembers(typename MessageStruct<MessageStructMember<V, K>...>::Builder,
|
|
Buffer &) {
|
|
return noError();
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(V), Error>::type decodeMembers(
|
|
typename MessageStruct<MessageStructMember<V, K>...>::Builder
|
|
builder,
|
|
Buffer &buffer) {
|
|
|
|
Error error =
|
|
ProtoKelDecodeImpl<typename ParameterPackType<i, V...>::type>::
|
|
decode(builder.template init<i>(), buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
return decodeMembers<i + 1>(builder, buffer);
|
|
}
|
|
|
|
static Error decode(
|
|
typename MessageStruct<MessageStructMember<V, K>...>::Builder builder,
|
|
Buffer &buffer) {
|
|
return decodeMembers<0>(builder, buffer);
|
|
}
|
|
};
|
|
|
|
template <typename... V, typename... K>
|
|
struct ProtoKelDecodeImpl<MessageUnion<MessageUnionMember<V, K>...>> {
|
|
template <size_t i = 0>
|
|
static typename std::enable_if<i == sizeof...(V), Error>::type
|
|
decodeMembers(typename MessageUnion<MessageUnionMember<V, K>...>::Builder,
|
|
Buffer &, msg_union_id_t) {
|
|
return noError();
|
|
}
|
|
|
|
template <size_t i = 0>
|
|
static typename std::enable_if <
|
|
i<sizeof...(V), Error>::type decodeMembers(
|
|
typename MessageUnion<MessageUnionMember<V, K>...>::Builder builder,
|
|
Buffer &buffer, msg_union_id_t id) {
|
|
|
|
if (id == i) {
|
|
Error error =
|
|
ProtoKelDecodeImpl<typename ParameterPackType<i, V...>::type>::
|
|
decode(builder.template init<i>(), buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
}
|
|
return decodeMembers<i + 1>(builder, buffer, id);
|
|
}
|
|
|
|
static Error
|
|
decode(typename MessageUnion<MessageUnionMember<V, K>...>::Builder builder,
|
|
Buffer &buffer) {
|
|
msg_union_id_t id = 0;
|
|
Error error = StreamValue<msg_union_id_t>::decode(id, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
if (id >= sizeof...(V)) {
|
|
return criticalError("Union doesn't have this many id's");
|
|
}
|
|
|
|
return decodeMembers<0>(builder, buffer, id);
|
|
}
|
|
};
|
|
|
|
class ProtoKelCodec {
|
|
public:
|
|
struct Version {
|
|
size_t major;
|
|
size_t minor;
|
|
size_t security;
|
|
};
|
|
|
|
const Version version() const { return Version{0, 0, 0}; }
|
|
|
|
template <typename T>
|
|
Error encode(typename T::Reader reader, Buffer &buffer) {
|
|
msg_packet_length_t packet_length = ProtoKelEncodeImpl<T>::size(reader);
|
|
// Check the size of the packet for the first
|
|
// message length description
|
|
|
|
if (buffer.writeCompositeLength() <
|
|
(packet_length + sizeof(msg_packet_length_t))) {
|
|
return recoverableError("Buffer too small");
|
|
}
|
|
{
|
|
Error error =
|
|
StreamValue<msg_packet_length_t>::encode(packet_length, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
}
|
|
{
|
|
Error error = ProtoKelEncodeImpl<T>::encode(reader, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
}
|
|
|
|
return noError();
|
|
};
|
|
|
|
template <typename T>
|
|
Error decode(typename T::Builder builder, Buffer &buffer) {
|
|
msg_packet_length_t packet_length = 0;
|
|
{
|
|
Error error =
|
|
StreamValue<msg_packet_length_t>::decode(packet_length, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
}
|
|
{
|
|
Error error = ProtoKelDecodeImpl<T>::decode(builder, buffer);
|
|
if (error.failed()) {
|
|
return error;
|
|
}
|
|
}
|
|
{
|
|
if (ProtoKelEncodeImpl<T>::size(builder.asReader()) !=
|
|
packet_length) {
|
|
return criticalError("Bad packet format");
|
|
}
|
|
}
|
|
return noError();
|
|
}
|
|
};
|
|
|
|
} // namespace gin
|