#pragma once #include "buffer.h" #include "message.h" #include "stream_endian.h" #include 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 struct ProtoKelEncodeImpl; template struct ProtoKelEncodeImpl> { static Error encode(typename MessagePrimitive::Reader data, Buffer &buffer) { Error error = StreamValue::encode(data.get(), buffer); return error; } static size_t size(typename MessagePrimitive::Reader) { return StreamValue::size(); } }; template <> struct ProtoKelEncodeImpl> { static Error encode(typename MessagePrimitive::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::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::Reader reader) { return sizeof(size_t) + reader.get().size(); } }; template struct ProtoKelEncodeImpl> { template static typename std::enable_if::type encodeMembers(typename MessageList::Reader, Buffer &) { return noError(); } template static typename std::enable_if < i::type encodeMembers(typename MessageList::Reader data, Buffer &buffer) { Error error = ProtoKelEncodeImpl::type>:: encode(data.template get(), buffer); if (error.failed()) { return error; } return encodeMembers(data, buffer); } static Error encode(typename MessageList::Reader data, Buffer &buffer) { return encodeMembers<0>(data, buffer); } template static typename std::enable_if::type sizeMembers(typename MessageList::Reader data) { return 0; } template static typename std::enable_if < i::type sizeMembers(typename MessageList::Reader reader) { return ProtoKelEncodeImpl::type>:: size(reader.template get()) + sizeMembers(reader); } static size_t size(typename MessageList::Reader reader) { return sizeMembers<0>(reader); } }; template struct ProtoKelEncodeImpl...>> { template static typename std::enable_if::type encodeMembers(typename MessageStruct...>::Reader, Buffer &) { return noError(); } template static typename std::enable_if < i::type encodeMembers( typename MessageStruct...>::Reader data, Buffer &buffer) { Error error = ProtoKelEncodeImpl::type>:: encode(data.template get(), buffer); if (error.failed()) { return error; } return encodeMembers(data, buffer); } static Error encode(typename MessageStruct...>::Reader data, Buffer &buffer) { return encodeMembers<0>(data, buffer); } template static typename std::enable_if::type sizeMembers(typename MessageStruct...>::Reader) { return 0; } template static typename std::enable_if < i::type sizeMembers(typename MessageStruct...>::Reader reader) { return ProtoKelEncodeImpl::type>:: size(reader.template get()) + sizeMembers(reader); } static size_t size(typename MessageStruct...>::Reader reader) { return sizeMembers<0>(reader); } }; template struct ProtoKelEncodeImpl...>> { template static typename std::enable_if::type encodeMembers(typename MessageUnion...>::Reader, Buffer &) { return noError(); } template static typename std::enable_if < i::type encodeMembers( typename MessageUnion...>::Reader reader, Buffer &buffer) { if (reader.template holdsAlternative< typename ParameterPackType::type>()) { Error error = StreamValue::encode(i, buffer); if (error.failed()) { return error; } return ProtoKelEncodeImpl::type>::encode(reader.template get(), buffer); } return encodeMembers(reader, buffer); } static Error encode(typename MessageUnion...>::Reader reader, Buffer &buffer) { return encodeMembers<0>(reader, buffer); } template static typename std::enable_if::type sizeMembers(typename MessageUnion...>::Reader) { return 0; } template static typename std::enable_if < i::type sizeMembers( typename MessageUnion...>::Reader reader) { if (reader.template holdsAlternative< typename ParameterPackType::type>()) { return ProtoKelEncodeImpl::type>::size(reader.template get()); } return sizeMembers(reader); } /* * Size of union id + member size */ static size_t size(typename MessageUnion...>::Reader reader) { return sizeof(uint32_t) + sizeMembers<0>(reader); } }; /* * Decode Implementations */ template struct ProtoKelDecodeImpl; template struct ProtoKelDecodeImpl> { static Error decode(typename MessagePrimitive::Builder data, Buffer &buffer) { T val = 0; Error error = StreamValue::decode(val, buffer); data.set(val); return error; } }; template <> struct ProtoKelDecodeImpl> { static Error decode(typename MessagePrimitive::Builder data, Buffer &buffer) { size_t size = 0; if (sizeof(size) > buffer.readCompositeLength()) { return recoverableError("Buffer too small"); } Error error = StreamValue::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 struct ProtoKelDecodeImpl> { template static typename std::enable_if::type decodeMembers(typename MessageList::Builder, Buffer &) { return noError(); } template static typename std::enable_if < i::type decodeMembers(typename MessageList::Builder builder, Buffer &buffer) { Error error = ProtoKelDecodeImpl::type>:: decode(builder.template init(), buffer); if (error.failed()) { return error; } return decodeMembers(builder, buffer); } static Error decode(typename MessageList::Builder builder, Buffer &buffer) { return decodeMembers<0>(builder, buffer); } }; template struct ProtoKelDecodeImpl...>> { template static typename std::enable_if::type decodeMembers(typename MessageStruct...>::Builder, Buffer &) { return noError(); } template static typename std::enable_if < i::type decodeMembers( typename MessageStruct...>::Builder builder, Buffer &buffer) { Error error = ProtoKelDecodeImpl::type>:: decode(builder.template init(), buffer); if (error.failed()) { return error; } return decodeMembers(builder, buffer); } static Error decode( typename MessageStruct...>::Builder builder, Buffer &buffer) { return decodeMembers<0>(builder, buffer); } }; template struct ProtoKelDecodeImpl...>> { template static typename std::enable_if::type decodeMembers(typename MessageUnion...>::Builder, Buffer &, msg_union_id_t) { return noError(); } template static typename std::enable_if < i::type decodeMembers( typename MessageUnion...>::Builder builder, Buffer &buffer, msg_union_id_t id) { if (id == i) { Error error = ProtoKelDecodeImpl::type>:: decode(builder.template init(), buffer); if (error.failed()) { return error; } } return decodeMembers(builder, buffer, id); } static Error decode(typename MessageUnion...>::Builder builder, Buffer &buffer) { msg_union_id_t id = 0; Error error = StreamValue::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 Error encode(typename T::Reader reader, Buffer &buffer) { msg_packet_length_t packet_length = ProtoKelEncodeImpl::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::encode(packet_length, buffer); if (error.failed()) { return error; } } { Error error = ProtoKelEncodeImpl::encode(reader, buffer); if (error.failed()) { return error; } } return noError(); }; template Error decode(typename T::Builder builder, Buffer &buffer) { msg_packet_length_t packet_length = 0; { Error error = StreamValue::decode(packet_length, buffer); if (error.failed()) { return error; } } { Error error = ProtoKelDecodeImpl::decode(builder, buffer); if (error.failed()) { return error; } } { if (ProtoKelEncodeImpl::size(builder.asReader()) != packet_length) { return criticalError("Bad packet format"); } } return noError(); } }; } // namespace gin