diff --git a/.clang-format b/.clang-format index 6205f67..710226b 100644 --- a/.clang-format +++ b/.clang-format @@ -112,9 +112,6 @@ SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION TabWidth: 4 UseTab: ForContinuationAndIndentation ... diff --git a/SConstruct b/SConstruct index 706e9dc..eb1f0eb 100644 --- a/SConstruct +++ b/SConstruct @@ -29,10 +29,10 @@ def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, ta sources.append( self.StaticObject( target=target_name, source=path ) ) pass -env=Environment(CPPPATH=['#source/kelgin','#source','#','#driver'], +env=Environment(ENV=os.environ, CPPPATH=['#source/kelgin','#source','#','#driver'], CXX='clang++', CPPDEFINES=['GIN_UNIX'], - CXXFLAGS=['-std=c++17','-g','-Wall','-Wextra'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], LIBS=['gnutls']) env.__class__.add_source_files = add_kel_source_files diff --git a/source/kelgin/json.h b/source/kelgin/json.h deleted file mode 100644 index 189a491..0000000 --- a/source/kelgin/json.h +++ /dev/null @@ -1,1094 +0,0 @@ -#pragma once - -#include "buffer.h" -#include "message.h" -#include "message_dynamic.h" - -#include "error.h" - -#include -#include -#include -#include -#include - -#include - -namespace gin { -class JsonCodec { -public: - struct Limits { - size_t depth; - size_t elements; - - Limits() : depth{16}, elements{1024} {} - Limits(size_t d, size_t e) : depth{d}, elements{e} {} - }; - -private: - bool isWhitespace(int8_t letter); - - void skipWhitespace(Buffer &buffer); - - Error decodeBool(DynamicMessageBool::Builder message, Buffer &buffer); - - // Not yet clear if double or integer - Error decodeNumber(Own &message, Buffer &buffer); - - Error decodeNull(Buffer &buffer); - - Error decodeValue(Own &message, Buffer &buffer, - const Limits &limits, Limits &counter); - - Error decodeRawString(std::string &raw, Buffer &buffer); - - Error decodeList(DynamicMessageList::Builder builder, Buffer &buffer, - const Limits &limits, Limits &counter); - - Error decodeStruct(DynamicMessageStruct::Builder message, Buffer &buffer, - const Limits &limits, Limits &counter); - - ErrorOr> decodeDynamic(Buffer &buffer, - const Limits &limits); - -public: - template - Error encode(typename T::Reader reader, Buffer &buffer); - - template - Error decode(typename T::Builder builder, Buffer &buffer, - const Limits &limits = Limits{}); -}; - -template struct JsonEncodeImpl; - -template struct JsonEncodeImpl> { - static Error encode(typename MessagePrimitive::Reader data, - Buffer &buffer) { - std::string stringified = std::to_string(data.get()); - Error error = - buffer.push(*reinterpret_cast(stringified.data()), - stringified.size()); - if (error.failed()) { - return error; - } - return noError(); - } -}; - -template <> struct JsonEncodeImpl> { - static Error encode(typename MessagePrimitive::Reader data, - Buffer &buffer) { - std::string str = - std::string{"\""} + std::string{data.get()} + std::string{"\""}; - Error error = buffer.push( - *reinterpret_cast(str.data()), str.size()); - if (error.failed()) { - return error; - } - return noError(); - } -}; - -template <> struct JsonEncodeImpl> { - static Error encode(typename MessagePrimitive::Reader data, - Buffer &buffer) { - std::string str = data.get() ? "true" : "false"; - Error error = buffer.push( - *reinterpret_cast(str.data()), str.size()); - if (error.failed()) { - return error; - } - return noError(); - } -}; - -template struct JsonEncodeImpl> { - template - static typename std::enable_if::type - encodeMembers(typename MessageList::Reader data, Buffer &buffer) { - (void)data; - (void)buffer; - return noError(); - } - template - static typename std::enable_if < - i::type - encodeMembers(typename MessageList::Reader data, Buffer &buffer) { - if (data.template get().isSetExplicitly()) { - { - Error error = - JsonEncodeImpl::type>:: - encode(data.template get(), buffer); - if (error.failed()) { - return error; - } - } - } else { - { - std::string_view str = "null"; - Error error = buffer.push( - *reinterpret_cast(str.data()), str.size()); - if (error.failed()) { - return error; - } - } - } - if constexpr ((i + 1u) < sizeof...(T)) { - Error error = buffer.push(','); - if (error.failed()) { - return error; - } - } - { - Error error = - JsonEncodeImpl>::encodeMembers(data, - buffer); - if (error.failed()) { - return error; - } - } - return noError(); - } - - static Error encode(typename MessageList::Reader data, - Buffer &buffer) { - Error error = buffer.push('['); - if (error.failed()) { - return error; - } - error = - JsonEncodeImpl>::encodeMembers<0>(data, buffer); - if (error.failed()) { - return error; - } - error = buffer.push(']'); - if (error.failed()) { - return error; - } - return noError(); - } -}; - -template -struct JsonEncodeImpl...>> { - template - static typename std::enable_if::type - encodeMembers( - typename MessageStruct...>::Reader data, - Buffer &buffer) { - (void)data; - (void)buffer; - return Error{}; - } - template - static typename std::enable_if < - i::type encodeMembers( - typename MessageStruct...>::Reader data, - Buffer &buffer) { - { - Error error = buffer.push('\"'); - if (error.failed()) { - return error; - } - std::string_view view = ParameterPackType::type::view(); - error = buffer.push(*reinterpret_cast(view.data()), - view.size()); - if (error.failed()) { - return error; - } - error = buffer.push('\"'); - if (error.failed()) { - return error; - } - error = buffer.push(':'); - if (error.failed()) { - return error; - } - } - if (data.template get().isSetExplicitly()) { - Error error = - JsonEncodeImpl::type>:: - encode(data.template get(), buffer); - if (error.failed()) { - return error; - } - } else { - std::string_view str = "null"; - Error error = buffer.push( - *reinterpret_cast(str.data()), str.size()); - if (error.failed()) { - return error; - } - } - if constexpr ((i + 1u) < sizeof...(V)) { - Error error = buffer.push(','); - if (error.failed()) { - return error; - } - } - { - Error error = - JsonEncodeImpl...>>:: - encodeMembers(data, buffer); - if (error.failed()) { - return error; - } - } - return noError(); - } - - static Error - encode(typename MessageStruct...>::Reader data, - Buffer &buffer) { - Error error = buffer.push('{'); - if (error.failed()) { - return error; - } - error = JsonEncodeImpl...>>:: - encodeMembers<0>(data, buffer); - if (error.failed()) { - return error; - } - error = buffer.push('}'); - if (error.failed()) { - return error; - } - return noError(); - } -}; - -template -struct JsonEncodeImpl...>> { - template - static typename std::enable_if::type encodeMember( - typename MessageUnion...>::Reader data, - Buffer &buffer) { - (void)data; - (void)buffer; - return noError(); - } - template - static typename std::enable_if < - i::type encodeMember( - typename MessageUnion...>::Reader reader, - Buffer &buffer) { - /// @todo only encode if alternative is set, skip in other cases - /// use holds_alternative - - if (reader.template holdsAlternative< - typename ParameterPackType::type>()) { - { - Error error = buffer.push('{'); - if (error.failed()) { - return error; - } - } - { - Error error = buffer.push('\"'); - if (error.failed()) { - return error; - } - std::string_view view = - ParameterPackType::type::view(); - error = - buffer.push(*reinterpret_cast(view.data()), - view.size()); - if (error.failed()) { - return error; - } - error = buffer.push('\"'); - if (error.failed()) { - return error; - } - error = buffer.push(':'); - if (error.failed()) { - return error; - } - } - - Error error = - JsonEncodeImpl::type>:: - encode(reader.template get(), buffer); - if (error.failed()) { - return error; - } - { - Error error = buffer.push('}'); - if (error.failed()) { - return error; - } - } - return noError(); - } - - Error error = - JsonEncodeImpl...>>:: - encodeMember(reader, buffer); - if (error.failed()) { - return error; - } - - return noError(); - } - - static Error - encode(typename MessageUnion...>::Reader reader, - Buffer &buffer) { - return encodeMember<0>(reader, buffer); - } -}; - -/* - * For JSON decoding we need a dynamic layer where we can query information from - */ -template struct JsonDecodeImpl; - -template struct JsonDecodeImpl> { - // static void decode(BufferView view, typename - // MessagePrimitive::Builder){} - static Error decode(typename MessagePrimitive::Builder, - DynamicMessage::DynamicReader) { - - // This is also a valid null implementation :) - return noError(); - } -}; -template <> struct JsonDecodeImpl> { - // static void decode(BufferView view, typename - // MessagePrimitive::Builder){} - static Error decode(typename MessagePrimitive::Builder data, - DynamicMessage::DynamicReader reader) { - if (reader.type() != DynamicMessage::Type::Bool) { - return criticalError("Not a boolean"); - } - DynamicMessageBool::Reader b_reader = reader.as(); - data.set(b_reader.get()); - return noError(); - } -}; -template <> struct JsonDecodeImpl> { - // static void decode(BufferView view, typename - // MessagePrimitive::Builder){} - static Error decode(typename MessagePrimitive::Builder data, - DynamicMessage::DynamicReader reader) { - if (reader.type() != DynamicMessage::Type::Signed) { - return criticalError("Not an integer"); - } - DynamicMessageSigned::Reader s_reader = - reader.as(); - data.set(s_reader.get()); - return noError(); - } -}; -template <> struct JsonDecodeImpl> { - // static void decode(BufferView view, typename - // MessagePrimitive::Builder){} - static Error decode(typename MessagePrimitive::Builder builder, - DynamicMessage::DynamicReader reader) { - if (reader.type() != DynamicMessage::Type::Signed) { - return criticalError("Not an integer"); - } - DynamicMessageSigned::Reader s_reader = - reader.as(); - int64_t val = s_reader.get(); - if (val < 0) { - return criticalError("Not an unsigned integer"); - } - builder.set(static_cast(val)); - return noError(); - } -}; -template <> struct JsonDecodeImpl> { - // static void decode(BufferView view, typename - // MessagePrimitive::Builder){} - static Error decode(typename MessagePrimitive::Builder data, - DynamicMessage::DynamicReader reader) { - if (reader.type() != DynamicMessage::Type::Signed) { - return criticalError("Not an integer"); - } - DynamicMessageSigned::Reader s_reader = - reader.as(); - int64_t val = s_reader.get(); - data.set(static_cast(val)); - return noError(); - } -}; - -template <> struct JsonDecodeImpl> { - static Error decode(typename MessagePrimitive::Builder builder, - DynamicMessage::DynamicReader reader) { - if (reader.type() != DynamicMessage::Type::String) { - return criticalError("Not a string"); - } - DynamicMessageString::Reader s_reader = - reader.as(); - builder.set(s_reader.get()); - - return noError(); - } -}; - -template struct JsonDecodeImpl> { - template - static typename std::enable_if::type - decodeMembers(typename MessageList::Builder builder, - DynamicMessageList::Reader reader) { - (void)builder; - (void)reader; - return noError(); - } - - template - static typename std::enable_if < - i::type - decodeMembers(typename MessageList::Builder builder, - DynamicMessageList::Reader reader) { - - DynamicMessage::DynamicReader member_reader = reader.get(i); - - { - Error error = - JsonDecodeImpl::type>:: - decode(builder.template init(), member_reader); - - if (error.failed()) { - return error; - } - } - { - Error error = - JsonDecodeImpl>::decodeMembers(builder, - reader); - if (error.failed()) { - return error; - } - } - return noError(); - } - - static Error decode(typename MessageList::Builder builder, - DynamicMessage::DynamicReader reader) { - if (reader.type() != DynamicMessage::Type::List) { - return criticalError("Not a list"); - } - - Error error = JsonDecodeImpl>::decodeMembers<0>( - builder, reader.as()); - if (error.failed()) { - return error; - } - return noError(); - } -}; - -template -struct JsonDecodeImpl...>> { - template - static typename std::enable_if::type - decodeMembers(typename MessageStruct...>::Builder, - DynamicMessageStruct::Reader) { - return noError(); - } - template - static typename std::enable_if < - i::type decodeMembers( - typename MessageStruct...>::Builder - builder, - DynamicMessageStruct::Reader reader) { - DynamicMessage::DynamicReader member_reader = - reader.get(ParameterPackType::type::view()); - { - Error error = - JsonDecodeImpl::type>:: - decode(builder.template init(), member_reader); - if (error.failed()) { - return error; - } - } - { - Error error = - JsonDecodeImpl...>>:: - decodeMembers(builder, reader); - if (error.failed()) { - return error; - } - } - return noError(); - } - static Error decode( - typename MessageStruct...>::Builder builder, - DynamicMessage::DynamicReader reader) { - if (reader.type() != DynamicMessage::Type::Struct) { - return criticalError("Not a struct"); - } - Error error = - JsonDecodeImpl...>>:: - decodeMembers<0>(builder, reader.as()); - if (error.failed()) { - return error; - } - return noError(); - } -}; - -bool JsonCodec::isWhitespace(int8_t letter) { - return letter == '\t' || letter == ' ' || letter == '\r' || letter == '\n'; -} - -void JsonCodec::skipWhitespace(Buffer &buffer) { - while (buffer.readCompositeLength() > 0 && isWhitespace(buffer.read())) { - buffer.readAdvance(1); - } -} - -struct JsonCodecLimitGuardHelper { - JsonCodec::Limits &counter; - - JsonCodecLimitGuardHelper(JsonCodec::Limits &l) : counter{l} { - ++counter.depth; - ++counter.elements; - } - - ~JsonCodecLimitGuardHelper() { --counter.depth; } - - static bool inLimit(const JsonCodec::Limits &counter, - const JsonCodec::Limits &top) { - return counter.depth < top.depth && counter.elements < top.elements; - } -}; - -Error JsonCodec::decodeBool(DynamicMessageBool::Builder message, - Buffer &buffer) { - assert((buffer.read() == 'T') || (buffer.read() == 't') || - (buffer.read() == 'F') || (buffer.read() == 'f')); - - bool is_true = buffer.read() == 'T' || buffer.read() == 't'; - buffer.readAdvance(1); - - if (is_true) { - std::array check = {'r', 'u', 'e'}; - for (size_t i = 0; buffer.readCompositeLength() > 0 && i < 3; ++i) { - if (buffer.read() != check[i]) { - return criticalError("Assumed true value, but it is invalid"); - } - buffer.readAdvance(1); - } - } else { - std::array check = {'a', 'l', 's', 'e'}; - for (size_t i = 0; buffer.readCompositeLength() > 0 && i < 4; ++i) { - if (buffer.read() != check[i]) { - return criticalError("Assumed false value, but it is invalid"); - } - buffer.readAdvance(1); - } - } - - message.set(is_true); - - return noError(); -} - -// Not yet clear if double or integer -Error JsonCodec::decodeNumber(Own &message, Buffer &buffer) { - assert((buffer.read() >= '0' && buffer.read() <= '9') || - buffer.read() == '+' || buffer.read() == '-'); - size_t offset = 0; - - if (buffer.read() == '-') { - ++offset; - } else if (buffer.read() == '+') { - return criticalError("Not a valid number with +"); - } - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - bool integer = true; - if (buffer.read(offset) >= '1' && buffer.read(offset) <= '9') { - ++offset; - - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - - while (1) { - if (buffer.read(offset) >= '0' && buffer.read(offset) <= '9') { - ++offset; - - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - continue; - } - break; - } - } else if (buffer.read(offset) == '0') { - ++offset; - } else { - return criticalError("Not a JSON number"); - } - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - if (buffer.read(offset) == '.') { - integer = false; - ++offset; - - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - - size_t partial_start = offset; - - while (1) { - if (buffer.read(offset) >= '0' && buffer.read(offset) <= '9') { - ++offset; - - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - continue; - } - break; - } - - if (offset == partial_start) { - return criticalError("No numbers after '.'"); - } - } - if (buffer.read(offset) == 'e' || buffer.read(offset) == 'E') { - integer = false; - ++offset; - - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - - if (buffer.read(offset) == '+' || buffer.read(offset) == '-') { - ++offset; - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - } - - size_t exp_start = offset; - - while (1) { - if (buffer.read(offset) >= '0' && buffer.read(offset) <= '9') { - ++offset; - - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - continue; - } - break; - } - if (offset == exp_start) { - return criticalError("No numbers after exponent token"); - } - } - - if (offset >= buffer.readCompositeLength()) { - return recoverableError("Buffer too short"); - } - - std::string_view number_view{reinterpret_cast(&buffer.read()), - offset}; - if (integer) { - int64_t result; - auto fc_result = - std::from_chars(number_view.data(), - number_view.data() + number_view.size(), result); - if (fc_result.ec != std::errc{}) { - return criticalError("Not an integer"); - } - - // - auto int_msg = std::make_unique(); - DynamicMessageSigned::Builder builder{*int_msg}; - builder.set(result); - message = std::move(int_msg); - } else { - std::string double_hack{number_view}; - double result; - // This is hacky because technically c++17 allows noexcept from_chars - // doubles, but clang++ and g++ don't implement it since that is - // apparently hard. - try { - result = std::stod(double_hack); - } catch (const std::exception &) { - return criticalError("Not a double"); - } - - /* - auto fc_result = - std::from_chars(number_view.data(), - number_view.data() + number_view.size(), result); - if (fc_result.ec != std::errc{}) { - return criticalError("Not a double"); - } - */ - - // - auto dbl_msg = std::make_unique(); - DynamicMessageDouble::Builder builder{*dbl_msg}; - builder.set(result); - message = std::move(dbl_msg); - } - - buffer.readAdvance(offset); - skipWhitespace(buffer); - return noError(); -} - -Error JsonCodec::decodeNull(Buffer &buffer) { - assert(buffer.read() == 'N' || buffer.read() == 'n'); - - buffer.readAdvance(1); - - std::array check = {'u', 'l', 'l'}; - for (size_t i = 0; buffer.readCompositeLength() > 0 && i < 3; ++i) { - if (buffer.read() != check[i]) { - return criticalError("Assumed null value, but it is invalid"); - } - buffer.readAdvance(1); - } - - return noError(); -} - -Error JsonCodec::decodeValue(Own &message, Buffer &buffer, - const Limits &limits, Limits &counter) { - skipWhitespace(buffer); - - JsonCodecLimitGuardHelper ctr_helper{counter}; - - if (!JsonCodecLimitGuardHelper::inLimit(counter, limits)) { - return criticalError("Not in limit"); - } - - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - - switch (buffer.read()) { - case '"': { - std::string str; - Error error = decodeRawString(str, buffer); - if (error.failed()) { - return error; - } - Own msg_string = - std::make_unique(); - DynamicMessageString::Builder builder{*msg_string}; - builder.set(std::move(str)); - message = std::move(msg_string); - } break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': { - Error error = decodeNumber(message, buffer); - if (error.failed()) { - return error; - } - } break; - case 't': - case 'T': - case 'f': - case 'F': { - Own msg_bool = - std::make_unique(); - decodeBool(DynamicMessageBool::Builder{*msg_bool}, buffer); - message = std::move(msg_bool); - } break; - case '{': { - Own msg_struct = - std::make_unique(); - Error error = decodeStruct(DynamicMessageStruct::Builder{*msg_struct}, - buffer, limits, counter); - if (error.failed()) { - return error; - } - message = std::move(msg_struct); - } break; - case '[': { - Own msg_list = - std::make_unique(); - decodeList(DynamicMessageList::Builder{*msg_list}, buffer, limits, - counter); - message = std::move(msg_list); - } break; - case 'n': - case 'N': { - Own msg_null = - std::make_unique(); - decodeNull(buffer); - message = std::move(msg_null); - } break; - default: { - return criticalError("Cannot identify next JSON value"); - } - } - - skipWhitespace(buffer); - return noError(); -} - -Error JsonCodec::decodeRawString(std::string &raw, Buffer &buffer) { - assert(buffer.read() == '"'); - - buffer.readAdvance(1); - std::stringstream iss; - bool string_done = false; - while (!string_done) { - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - switch (buffer.read()) { - case '\\': - buffer.readAdvance(1); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - switch (buffer.read()) { - case '\\': - case '/': - case '"': - iss << buffer.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': { - buffer.readAdvance(1); - if (buffer.readCompositeLength() < 4) { - return recoverableError("Broken unicode or short buffer"); - } - /// @todo correct unicode handling - iss << '?'; // dummy line - iss << '?'; - iss << '?'; - iss << '?'; - // There is always a skip at the end so here we skip 3 - // instead of 4 bytes - buffer.readAdvance(3); - } break; - } - break; - case '"': - string_done = true; - break; - default: - iss << buffer.read(); - break; - } - buffer.readAdvance(1); - } - raw = iss.str(); - return noError(); -} - -Error JsonCodec::decodeList(DynamicMessageList::Builder builder, Buffer &buffer, - const Limits &limits, Limits &counter) { - assert(buffer.read() == '['); - buffer.readAdvance(1); - skipWhitespace(buffer); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - - while (buffer.read() != ']') { - - Own message = nullptr; - { - Error error = decodeValue(message, buffer, limits, counter); - if (error.failed()) { - return error; - } - } - builder.push(std::move(message)); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - - switch (buffer.read()) { - case ']': - break; - case ',': - buffer.readAdvance(1); - skipWhitespace(buffer); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - break; - default: - return criticalError("Not a JSON Object"); - } - } - buffer.readAdvance(1); - return noError(); -} - -Error JsonCodec::decodeStruct(DynamicMessageStruct::Builder message, - Buffer &buffer, const Limits &limits, - Limits &counter) { - assert(buffer.read() == '{'); - buffer.readAdvance(1); - skipWhitespace(buffer); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - - while (buffer.read() != '}') { - if (buffer.read() == '"') { - std::string key_string; - { - Error error = decodeRawString(key_string, buffer); - if (error.failed()) { - return error; - } - } - skipWhitespace(buffer); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - if (buffer.read() != ':') { - return criticalError("Expecting a ':' token"); - } - buffer.readAdvance(1); - Own msg = nullptr; - { - Error error = decodeValue(msg, buffer, limits, counter); - if (error.failed()) { - return error; - } - } - message.init(key_string, std::move(msg)); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - - switch (buffer.read()) { - case '}': - break; - case ',': - buffer.readAdvance(1); - skipWhitespace(buffer); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - break; - default: - return criticalError("Not a JSON Object"); - } - } else { - return criticalError("Not a JSON Object"); - } - } - buffer.readAdvance(1); - return noError(); -} - -ErrorOr> JsonCodec::decodeDynamic(Buffer &buffer, - const Limits &limits) { - Limits counter{0, 0}; - - skipWhitespace(buffer); - if (buffer.readCompositeLength() == 0) { - return recoverableError("Buffer too short"); - } - if (buffer.read() == '{') { - - Own message = - std::make_unique(); - Error error = decodeStruct(DynamicMessageStruct::Builder{*message}, - buffer, limits, counter); - if (error.failed()) { - return error; - } - skipWhitespace(buffer); - - return Own{std::move(message)}; - } else if (buffer.read() == '[') { - - Own message = - std::make_unique(); - Error error = decodeList(*message, buffer, limits, counter); - if (error.failed()) { - return error; - } - skipWhitespace(buffer); - - return Own{std::move(message)}; - } else { - return criticalError("Not a JSON Object"); - } -} - -template -Error JsonCodec::encode(typename T::Reader reader, Buffer &buffer) { - BufferView view{buffer}; - Error error = JsonEncodeImpl::encode(reader, view); - if (error.failed()) { - return error; - } - - buffer.writeAdvance(view.writeOffset()); - - return error; -} - -template -Error JsonCodec::decode(typename T::Builder builder, Buffer &buffer, - const Limits &limits) { - - BufferView view{buffer}; - - ErrorOr> error_or_message = decodeDynamic(view, limits); - if (error_or_message.isError()) { - return std::move(error_or_message.error()); - } - - Own message = std::move(error_or_message.value()); - if (!message) { - return criticalError("No message object created"); - } - if (message->type() == DynamicMessage::Type::Null) { - return criticalError("Can't decode to json"); - } - - DynamicMessage::DynamicReader reader{*message}; - Error static_error = JsonDecodeImpl::decode(builder, reader); - - if (static_error.failed()) { - return static_error; - } - - buffer.readAdvance(view.readOffset()); - - return static_error; -} - -} // namespace gin diff --git a/source/kelgin/message.h b/source/kelgin/message.h index 9ddb58e..0752778 100644 --- a/source/kelgin/message.h +++ b/source/kelgin/message.h @@ -2,457 +2,440 @@ #include #include +#include #include #include +#include + #include "common.h" +#include "message_container.h" +#include "schema.h" #include "string_literal.h" namespace gin { -class Message { +class MessageBase { protected: bool set_explicitly = false; public: - template T &as() { - static_assert(std::is_base_of()); - return reinterpret_cast(*this); + template T &as() { + static_assert(std::is_base_of()); + return dynamic_cast(*this); } - template const T &as() const { - static_assert(std::is_base_of()); - return reinterpret_cast(*this); + template const T &as() const { + static_assert(std::is_base_of()); + return dynamic_cast(*this); } }; /* - * Representing all primitive types + * Representing all message types + * Description which type to use happens through the use of the schema classes + * in schema.h The message classes are wrapper classes which store the data + * according to the specified container class. + * + * The reader and builder classe exist to create some clarity while implementing + * parsers. Also minor guarantess are provided if the message is used as a + * template parameter. */ -template class MessagePrimitive : public Message { + +/* + * Struct Message + */ +template +class Message...>, Container> final + : public MessageBase { private: - T value; + using SchemaType = schema::Struct...>; + using MessageType = Message; + Container container; + + static_assert(std::is_same_v, + "Container should have same the schema as Message"); friend class Builder; friend class Reader; public: - MessagePrimitive() = default; - class Reader; class Builder { private: - MessagePrimitive &message; + MessageType &message; public: - Builder(MessagePrimitive &message) : message{message} {} - - constexpr void set(const T &value) { - message.value = value; - message.set_explicitly = true; - } + Builder(MessageType &msg) : message{msg} {} Reader asReader() { return Reader{message}; } - }; - class Reader { - private: - MessagePrimitive &message; - - public: - Reader(MessagePrimitive &message) : message{message} {} - - const T &get() const { return message.value; } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() { return Builder{message}; } - }; -}; - -template <> class MessagePrimitive : public Message { -private: - std::string value; - - friend class Builder; - friend class Reader; - -public: - MessagePrimitive() = default; - - class Reader; - class Builder { - private: - MessagePrimitive &message; - - public: - Builder(MessagePrimitive &message) : message{message} {} - - void set(std::string_view value) { - message.value = std::string{value}; - message.set_explicitly = true; - } /* - void set(std::string &&value) { - message.value = std::move(value); - message.set_explicitly = true; - } - */ - - Reader asReader() { return Reader{message}; } - }; - - class Reader { - private: - MessagePrimitive &message; - - public: - Reader(MessagePrimitive &message) : message{message} {} - - std::string_view get() const { return std::string_view{message.value}; } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() { return Builder{message}; } - }; -}; - -template class MessageList : public Message { -private: - using tuple_type = std::tuple; - tuple_type elements; - friend class Builder; - friend class Reader; - -public: - class Reader; - class Builder { - private: - MessageList &message; - - public: - Builder(MessageList &message) : message{message} { - message.set_explicitly = true; - } - + * Initialize a member by index + */ template - constexpr typename std::tuple_element_t::Builder init() { - std::tuple_element_t &msg_ref = - std::get(message.elements); - return - typename std::tuple_element_t::Builder{msg_ref}; + typename Container::template ElementType::Builder init() { + return typename Container::template ElementType::Builder{ + message.container.template get()}; } - Reader asReader() { return Reader{message}; } - }; - - class Reader { - private: - MessageList &message; - - public: - Reader(MessageList &message) : message{message} {} - - template - constexpr typename std::tuple_element_t::Reader get() { - return std::get(message.elements); - } - - size_t size() const { return std::tuple_size::value; } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() { return Builder{message}; } - }; -}; - -/// @todo how to do initialization? -template class MessageArray : public Message { -private: - using array_type = std::vector; - array_type elements; - friend class Builder; - friend class Reader; - -public: - class Reader; - class Builder { - private: - MessageArray &message; - - public: - Builder(MessageArray &message) : message{message} { - message.set_explicitly = true; - } - - constexpr typename T::Builder init(size_t i) { - T &msg_ref = message.elements.at(i); - return typename T::Builder{msg_ref}; - } - - Reader asReader() { return Reader{message}; } - }; - - class Reader { - private: - MessageArray &message; - - public: - Reader(MessageArray &message) : message{message} {} - - constexpr typename T::Reader get(size_t i) { - return message.elements.at(i); - } - - size_t size() const { return message.elements.size(); } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() { return Builder{message}; } - }; -}; - -template struct MessageStructMember; - -template -struct MessageStructMember> { - T value; -}; - -/* - * Helper structs which retrieve - * the index of a parameter pack based on the index - * or - * a type based on the index - * Pass N as the index for the desired type - * or - * a type to get the first occurence of a type index - */ -template struct ParameterPackType; - -template struct ParameterPackType<0, T, TL...> { - using type = T; -}; - -template -struct ParameterPackType { - using type = typename ParameterPackType::type; -}; - -template struct ParameterPackIndex; - -template struct ParameterPackIndex { - static constexpr size_t value = 0u; -}; - -template -struct ParameterPackIndex { - static constexpr size_t value = 1u + ParameterPackIndex::value; -}; - -template class MessageStruct; - -/* - * Since no value is retrieved from the keys, I only need a value tuple - */ -template -class MessageStruct...> : public Message { -private: - using value_type = std::tuple; - value_type values; - friend class Builder; - friend class Reader; - -public: - class Reader; - class Builder { - private: - MessageStruct...> &message; - - public: - Builder(MessageStruct...> &message) - : message{message} { - message.set_explicitly = true; - } - - template - constexpr typename std::tuple_element_t::Builder init() { - std::tuple_element_t &msg_ref = - std::get(message.values); - return - typename std::tuple_element_t::Builder{msg_ref}; - } - - template - constexpr - typename std::tuple_element_t::value, - value_type>::Builder - init() { - std::tuple_element_t::value, value_type> - &msg_ref = std::get::value>( - message.values); - return typename std::tuple_element_t< - ParameterPackIndex::value, value_type>::Builder{ - msg_ref}; - } - - Reader asReader() { return Reader{message}; } - }; - class Reader { - private: - MessageStruct...> &message; - - public: - Reader(MessageStruct...> &message) - : message{message} {} - - template - constexpr typename std::tuple_element_t::Reader get() { - std::tuple_element_t &msg_ref = - std::get(message.values); - return - typename std::tuple_element_t::Reader{msg_ref}; - } - - template - constexpr - typename std::tuple_element_t::value, - value_type>::Reader - get() { - std::tuple_element_t::value, value_type> - &msg_ref = std::get::value>( - message.values); - return typename std::tuple_element_t< - ParameterPackIndex::value, value_type>::Reader{ - msg_ref}; - } - - constexpr size_t size() { return std::tuple_size::value; } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() { return Builder{message}; } - }; -}; - -template struct MessageUnionMember; - -template -struct MessageUnionMember> { - T value; -}; - -template class MessageUnion; - -/// @todo copied from MessageStruct, but the acces is different, since -/// only one value can be set at the same time. -template -class MessageUnion...> : public Message { -private: - using value_type = std::variant...>; - value_type values; - friend class Builder; - friend class Reader; - -public: - class Reader; - class Builder { - private: - MessageUnion...> &message; - - public: - Builder(MessageUnion...> &message) - : message{message} { - message.set_explicitly = true; - } - - template - constexpr typename ParameterPackType::type::Builder init() { - message.values = - typename std::variant_alternative_t{}; - typename ParameterPackType::type &msg_ref = - std::get(message.values).value; - return typename ParameterPackType::type::Builder{msg_ref}; - } - - template - constexpr typename ParameterPackType::value, - V...>::type::Builder + /* + * Initialize a member by their name + * This is the preferred method for schema::Struct messages + */ + template + typename Container::template ElementType< + MessageParameterKeyPackIndex::Value>::Builder init() { - message.values = typename std::variant_alternative_t< - ParameterPackIndex::value, value_type>{}; - typename ParameterPackType::value, - V...>::type &msg_ref = - std::get::value>(message.values) - .value; - return - typename ParameterPackType::value, - V...>::type::Builder{msg_ref}; + constexpr size_t i = + MessageParameterKeyPackIndex::Value; + + return init(); + } + }; + + class Reader { + private: + MessageType &message; + + public: + Reader(MessageType &msg) : message{msg} {} + + Builder asBuilder() { return Builder{message}; } + + /* + * Get member by index + */ + template + typename Container::template ElementType::Reader get() { + return typename Container::template ElementType::Reader{ + message.container.template get()}; } + /* + * Get member by name + * This is the preferred method for schema::Struct messages + */ + template + typename Container::template ElementType< + MessageParameterKeyPackIndex::Value>::Reader + get() { + // The index of the first match + constexpr size_t i = + MessageParameterKeyPackIndex::Value; + + return get(); + } + }; + + Builder build() { return Builder{*this}; } + + Reader read() { return Reader{*this}; } +}; + +/* + * Union message class. Wrapper object + */ +template +class Message...>, Container> final + : public MessageBase { +private: + using SchemaType = schema::Union...>; + using MessageType = Message; + + Container container; + + static_assert(std::is_same_v, + "Container should have same the schema as Message"); + + friend class Builder; + friend class Reader; + +public: + class Reader; + class Builder { + private: + MessageType &message; + + public: + Builder(MessageType &msg) : message{msg} {} + Reader asReader() { return Reader{message}; } + + template + typename Container::template ElementType::Builder init() { + return typename Container::template ElementType::Builder{ + message.container.template get()}; + } + + template + typename Container::template ElementType< + MessageParameterKeyPackIndex::Value>::Builder + init() { + constexpr size_t i = + MessageParameterKeyPackIndex::Value; + + return init(); + } + }; + + class Reader { + private: + MessageType &message; + + public: + Reader(MessageType &msg) : message{msg} {} + + Builder asBuilder() { return Builder{message}; } + + template + typename Container::template ElementType::Reader get() { + return typename Container::template ElementType::Reader{ + message.container.template get()}; + } + + template + typename Container::template ElementType< + MessageParameterKeyPackIndex::Value>::Reader + get() { + // The index of the first match + constexpr size_t i = + MessageParameterKeyPackIndex::Value; + + return get(); + } + + template + constexpr size_t toIndex() const noexcept { + return MessageParameterKeyPackIndex::Value; + } + + size_t index() const noexcept { return message.container.index(); } + + template bool hasAlternative() const { + return index() == toIndex(); + } + }; +}; + +/* + * Array message class. Wrapper around an array schema element + * @todo Array class needs either a resize function or each message class has an + * individual call for Array children + */ +/* +template +class Message, Container> final : public MessageBase { +private: + using SchemaType = schema::Array; + using MessageType = Message; + + Container container; + + static_assert(std::is_same_v, + "Container should have same Schema as Message"); + + friend class Builder; + friend class Reader; + +public: + class Reader; + class Builder { + private: + MessageType & message; + public: + Builder(MessageType& msg):message{msg}{} + + Reader asReader(){return Reader{message};} + + template + typename Container::MessageType::Builder init(){ + return typename +Container::MessageType::Builder{message.container.get()}; + } + }; + + class Reader { + private: + MessageType& message; + public: + Reader(MessageType& msg):message{msg}{} + + Builder asBuilder(){return Builder{message};} + + template + typename Container::MessageType::Reader get(){ + return typename +Container::MessageType::Reader{message.container.get()}; + } + }; +}; +*/ + +/* + * Tuple message class. Wrapper around a tuple schema + */ +template +class Message, Container> final : public MessageBase { +private: + using SchemaType = schema::Tuple; + using MessageType = Message; + + Container container; + + static_assert(std::is_same_v, + "Container should have same the schema as Message"); + + friend class Builder; + friend class Reader; + +public: + class Reader; + class Builder { + MessageType &message; + + public: + Builder(MessageType &msg) : message{msg} {} + + Reader asReader() { return Reader{message}; } + + template + typename Container::template ElementType::Builder init() { + return typename Container::template ElementType::Builder{ + message.container.template get()}; + } }; class Reader { private: - MessageUnion...> &message; + MessageType &message; public: - Reader(MessageUnion...> &message) - : message{message} {} - - template - constexpr typename ParameterPackType::type::Reader get() { - typename ParameterPackType::type &msg_ref = - std::get(message.values).value; - return typename ParameterPackType::type::Reader{msg_ref}; - } - - template - constexpr typename ParameterPackType::value, - V...>::type::Reader - get() { - typename ParameterPackType::value, - V...>::type &msg_ref = - std::get::value>(message.values) - .value; - return - typename ParameterPackType::value, - V...>::type::Reader{msg_ref}; - } - - template constexpr bool holdsAlternative() { - return std::holds_alternative::value, value_type>>( - message.values); - } - - size_t index() const { return message.values.index(); } - - bool isSetExplicitly() const { return message.set_explicitly; } - - constexpr size_t size() { return std::variant_size::value; } + Reader(MessageType &msg) : message{msg} {} Builder asBuilder() { return Builder{message}; } + + template + typename Container::template ElementType::Reader get() { + return typename Container::template ElementType::Reader{ + message.container.template get()}; + } }; }; -class MessageReader { +/* + * Primitive type (float, double, uint8_t, uint16_t, uint32_t, uint64_t, int8_t, + * int16_t, int32_t, int64_t) message class + */ +template +class Message, Container> final : public MessageBase { +private: + using SchemaType = schema::Primitive; + using MessageType = Message; + + Container container; + + static_assert(std::is_same_v, + "Container should have same the schema as Message"); + + friend class Builder; + friend class Reader; + public: - virtual ~MessageReader() = default; + class Reader; + class Builder { + private: + MessageType &message; + + public: + Builder(MessageType &msg) : message{msg} {} + + Reader asReader() { return Reader{message}; } + + void set(const typename Container::ValueType &value) { + message.container.set(value); + } + }; + + class Reader { + private: + MessageType &message; + + public: + Reader(Message &msg) : message{msg} {} + + Builder asBuilder() { return Builder{message}; } + + const typename Container::ValueType &get() const { + return message.container.get(); + } + }; }; -class MessageBuilder { +template +class Message final : public MessageBase { private: - Own root_message = nullptr; + using SchemaType = schema::String; + using MessageType = Message; + + Container container; + + static_assert(std::is_same_v, + "Container should have same the schema as Message"); + + friend class Builder; + friend class Reader; public: - virtual ~MessageBuilder() = default; + class Reader; + class Builder { + private: + MessageType &message; - template typename MessageRoot::Builder initRoot() { - root_message = std::make_unique(); - MessageRoot &msg_ref = root_message->as(); - return typename MessageRoot::Builder{msg_ref}; + public: + Builder(MessageType &msg) : message{msg} {} + + Reader asReader() { return Reader{message}; } + + void set(const std::string &str) { message.container.set(str); } + }; + + class Reader { + private: + MessageType &message; + + public: + Reader(MessageType &msg) : message{msg} {} + + Builder asBuilder() { return Builder{message}; } + + std::string_view get() { return message.container.get(); } + }; +}; + +template > +class HeapMessageRoot { +private: + Own> root; + +public: + HeapMessageRoot(Own> r) : root{std::move(r)} {} + + typename Message::Builder build() { + assert(root); + return typename Message::Builder{*root}; + } + + typename Message::Reader read() { + assert(root); + return typename Message::Reader{*root}; } }; -inline MessageBuilder heapMessageBuilder() { return MessageBuilder{}; } +/* + * Minor helper for creating a message root + */ +template > +inline HeapMessageRoot heapMessageRoot() { + Own> root = heap>(); + return HeapMessageRoot{std::move(root)}; +} } // namespace gin diff --git a/source/kelgin/message_container.h b/source/kelgin/message_container.h new file mode 100644 index 0000000..15930cd --- /dev/null +++ b/source/kelgin/message_container.h @@ -0,0 +1,226 @@ +#pragma once + +#include "schema.h" + +namespace gin { +template class MessageContainer; + +template class Message; + +template struct MessageParameterPackType; + +template struct MessageParameterPackType<0, TN, T...> { + using Type = TN; +}; + +template +struct MessageParameterPackType { + static_assert(sizeof...(T) > 0, "Exhausted parameters"); + using Type = typename MessageParameterPackType::Type; +}; + +template struct MessageParameterPackIndex; + +template struct MessageParameterPackIndex { + static constexpr size_t Value = 0u; +}; + +template +struct MessageParameterPackIndex { + static constexpr size_t Value = + 1u + MessageParameterPackIndex::Value; +}; + +/* + * Nightmare inducing compiler problems found here. Somehow non-type + * StringLiterals cannot be resolved as non-type primitive template values can. + * This is the workaround + */ +template +struct MessageParameterKeyPackIndexHelper { + static constexpr size_t Value = + (V == Key0) + ? (0u) + : (1u + MessageParameterKeyPackIndexHelper::Value); +}; + +template +struct MessageParameterKeyPackIndexHelper { + static constexpr size_t Value = (V == Key0) ? (0u) : (1u); +}; + +template +struct MessageParameterKeyPackIndex { + static constexpr size_t Value = + MessageParameterKeyPackIndexHelper::Value; + static_assert(Value < sizeof...(Keys), + "Provided StringLiteral doesn't exist in searched list"); +}; + +template +class MessageContainer...>> { +private: + using ValueType = std::tuple>...>; + ValueType values; + +public: + using SchemaType = schema::Struct...>; + + template + using ElementType = typename MessageParameterPackType< + i, Message>...>::Type; + + template ElementType &get() { return std::get(values); } +}; + +/* + * Union storage + */ +template +class MessageContainer...>> { +private: + using ValueType = std::variant>...>; + ValueType value; + +public: + using SchemaType = schema::Union...>; + + template + using ElementType = typename MessageParameterPackType< + i, Message>...>::Type; + + template ElementType &get() { + if (i != value.index()) { + using MessageIV = typename MessageParameterPackType::Type; + value = Message>{}; + } + return std::get(value); + } + + size_t index() const noexcept { return value.index(); } +}; + +/* + * Array storage + */ + +/* +template +class MessageContainer { +private: + using ValueType = std::vector>>; + ValueType values; +public: + using SchemaType = schema::Array; + + template Message>& get(){ + return values.at(i); + } +}; +*/ + +/* + * Tuple storage + */ +template class MessageContainer> { +private: + using ValueType = std::tuple>...>; + ValueType values; + +public: + using SchemaType = schema::Tuple; + + template + using ElementType = typename MessageParameterPackType< + i, Message>...>::Type; + + template ElementType &get() { return std::get(values); } +}; + +/* + * Helper for the basic message container, so the class doesn't have to be + * specialized 10 times. + */ +template struct PrimitiveTypeHelper; + +template <> +struct PrimitiveTypeHelper> { + using Type = int8_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = int16_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = int32_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = int64_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = uint8_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = uint16_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = uint32_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = uint64_t; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = float; +}; + +template <> +struct PrimitiveTypeHelper> { + using Type = double; +}; + +template class MessageContainer> { +public: + using SchemaType = schema::Primitive; + using ValueType = + typename PrimitiveTypeHelper>::Type; + +private: + ValueType value; + +public: + MessageContainer() : value{0} {} + + void set(const ValueType &v) { value = v; } + + const ValueType &get() const { return value; } +}; + +template <> class MessageContainer { +public: + using SchemaType = schema::String; + using ValueType = std::string; + +private: + ValueType value; + +public: + void set(const ValueType &v) { value = v; } + + const ValueType &get() const { return value; } +}; +} // namespace gin diff --git a/source/kelgin/message_dynamic.cpp b/source/kelgin/message_dynamic.cpp deleted file mode 100644 index 9ad430a..0000000 --- a/source/kelgin/message_dynamic.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "message_dynamic.h" - -namespace gin { -template <> -DynamicMessage::Type DynamicMessagePrimitive::type() const { - return DynamicMessage::Type::Unsigned; -}; -template <> -DynamicMessage::Type DynamicMessagePrimitive::type() const { - return DynamicMessage::Type::Signed; -}; -template <> -DynamicMessage::Type DynamicMessagePrimitive::type() const { - return DynamicMessage::Type::String; -}; -template <> DynamicMessage::Type DynamicMessagePrimitive::type() const { - return DynamicMessage::Type::Bool; -}; -template <> DynamicMessage::Type DynamicMessagePrimitive::type() const { - return DynamicMessage::Type::Double; -}; -} // namespace gin \ No newline at end of file diff --git a/source/kelgin/message_dynamic.h b/source/kelgin/message_dynamic.h deleted file mode 100644 index a0c6065..0000000 --- a/source/kelgin/message_dynamic.h +++ /dev/null @@ -1,274 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "common.h" - -/// @todo Move implementation to cpp file - -namespace gin { -/* - * Base class for each message data structure - */ -class DynamicMessage { -protected: - /* - * The encoder and decoders use this as a hint if this was set by a default - * value or not - */ - bool set_explicitly = false; - -public: - /* - * Later use for dynamic access - */ - enum class Type : uint16_t { - Null, - Struct, - List, - Array, - Union, - String, - Signed, - Unsigned, - Bool, - Double - }; - - virtual ~DynamicMessage() = default; - - virtual Type type() const = 0; - - template T &as() { - static_assert(std::is_base_of()); - return reinterpret_cast(*this); - } - - template const T &as() const { - static_assert(std::is_base_of()); - return reinterpret_cast(*this); - } - - class DynamicReader; - class DynamicBuilder { - private: - DynamicMessage &message; - - public: - DynamicBuilder(DynamicMessage &message) : message{message} {} - - DynamicReader asReader() const { return DynamicReader{message}; } - - DynamicMessage::Type type() const { return message.type(); } - - template typename T::Builder as() { - static_assert(std::is_base_of()); - return typename T::Builder{reinterpret_cast(message)}; - } - }; - - class DynamicReader { - private: - DynamicMessage &message; - - public: - DynamicReader(DynamicMessage &message) : message{message} {} - - DynamicBuilder asBuilder() const { return DynamicBuilder{message}; } - - DynamicMessage::Type type() const { return message.type(); } - - template typename T::Reader as() { - static_assert(std::is_base_of()); - return typename T::Reader{reinterpret_cast(message)}; - } - }; -}; - -class DynamicMessageNull : public DynamicMessage { -public: - DynamicMessage::Type type() const override { - return DynamicMessage::Type::Null; - } -}; -static DynamicMessageNull dynamicNullMessage; - -template class DynamicMessagePrimitive : public DynamicMessage { -private: - T value; - friend class Builder; - friend class Reader; - -public: - DynamicMessagePrimitive() = default; - - DynamicMessage::Type type() const override; - - class Reader; - class Builder { - private: - DynamicMessagePrimitive &message; - - public: - Builder(DynamicMessagePrimitive &message) : message{message} {} - - constexpr void set(const T &value) { - message.value = value; - message.set_explicitly = true; - } - - constexpr void set(T &&value) { - message.value = std::move(value); - message.set_explicitly = true; - } - - Reader asReader() const { return Reader{message}; } - }; - - class Reader { - private: - DynamicMessagePrimitive &message; - - public: - Reader(DynamicMessagePrimitive &message) : message{message} {} - - constexpr const T &get() { return message.value; } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() const { return Builder{message}; } - }; -}; - -using DynamicMessageString = DynamicMessagePrimitive; -using DynamicMessageSigned = DynamicMessagePrimitive; -using DynamicMessageUnsigned = DynamicMessagePrimitive; -using DynamicMessageBool = DynamicMessagePrimitive; -using DynamicMessageDouble = DynamicMessagePrimitive; - -class DynamicMessageStruct : public DynamicMessage { -private: - std::map, std::less<>> messages; - friend class Builder; - friend class Reader; - -public: - DynamicMessage::Type type() const override { - return DynamicMessage::Type::Struct; - } - class Reader; - class Builder { - private: - DynamicMessageStruct &message; - - public: - Builder(DynamicMessageStruct &message) : message{message} { - message.set_explicitly = true; - } - - template typename T::Builder init(const std::string &key) { - Own msg = std::make_unique(); - typename T::Builder builder{*msg}; - /*auto insert = */ message.messages.insert( - std::make_pair(key, std::move(msg))); - return builder; - } - - DynamicMessage::DynamicBuilder init(const std::string &key, - Own &&msg) { - DynamicMessage::DynamicBuilder builder{*msg}; - message.messages.insert(std::make_pair(key, std::move(msg))); - return builder; - } - - Reader asReader() const { return Reader{message}; } - }; - - class Reader { - private: - DynamicMessageStruct &message; - - public: - Reader(DynamicMessageStruct &message) : message{message} {} - - DynamicMessage::DynamicReader get(std::string_view key) { - auto find = message.messages.find(key); - if (find != message.messages.end()) { - return DynamicMessage::DynamicReader{*(find->second)}; - } else { - return DynamicMessage::DynamicReader{dynamicNullMessage}; - } - } - - size_t size() const { return message.messages.size(); } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() const { return Builder{message}; } - }; -}; - -class DynamicMessageList : public DynamicMessage { -private: - std::deque> messages; - friend class Builder; - friend class Reader; - -public: - DynamicMessage::Type type() const override { - return DynamicMessage::Type::List; - } - class Reader; - class Builder { - private: - DynamicMessageList &message; - - public: - Builder(DynamicMessageList &message) : message{message} { - message.set_explicitly = true; - } - - template typename T::Builder push() { - static_assert(std::is_base_of()); - Own msg = std::make_unique(); - typename T::Builder builder{*msg}; - message.messages.push_back(std::move(msg)); - return builder; - } - - DynamicMessage::DynamicBuilder push(Own &&msg) { - DynamicMessage::DynamicBuilder builder{*msg}; - message.messages.push_back(std::move(msg)); - return builder; - } - - Reader asReader() const { return Reader{message}; } - }; - - class Reader { - private: - DynamicMessageList &message; - - public: - Reader(DynamicMessageList &message) : message{message} {} - - DynamicMessage::DynamicReader get(size_t element) { - if (element < message.messages.size()) { - return DynamicMessage::DynamicReader{ - *(message.messages[element])}; - } else { - return DynamicMessage::DynamicReader{dynamicNullMessage}; - } - } - - size_t size() const { return message.messages.size(); } - - bool isSetExplicitly() const { return message.set_explicitly; } - - Builder asBuilder() const { return Builder{message}; } - }; -}; -} // namespace gin \ No newline at end of file diff --git a/source/kelgin/proto_kel.h b/source/kelgin/proto_kel.h index e940a02..c379753 100644 --- a/source/kelgin/proto_kel.h +++ b/source/kelgin/proto_kel.h @@ -42,31 +42,39 @@ public: const Version version() const { return Version{0, 0, 0}; } - template - Error encode(typename T::Reader reader, Buffer &buffer); + template > + Error encode(typename Message::Reader reader, + Buffer &buffer); - template - Error decode(typename T::Builder builder, Buffer &buffer, - const Limits &limits = Limits{}); + template > + Error decode(typename Message::Builder builder, + Buffer &buffer, const Limits &limits = Limits{}); }; -template struct ProtoKelEncodeImpl; +template struct ProtoKelEncodeImpl; -template struct ProtoKelEncodeImpl> { - static Error encode(typename MessagePrimitive::Reader data, - Buffer &buffer) { - Error error = StreamValue::encode(data.get(), buffer); +template +struct ProtoKelEncodeImpl, Container>> { + static Error + encode(typename Message, Container>::Reader data, + Buffer &buffer) { + Error error = StreamValue>::Type>::encode(data.get(), buffer); return error; } - static size_t size(typename MessagePrimitive::Reader) { - return StreamValue::size(); + static size_t + size(typename Message, Container>::Reader) { + return StreamValue>::Type>::size(); } }; -template <> struct ProtoKelEncodeImpl> { - static Error encode(typename MessagePrimitive::Reader data, - Buffer &buffer) { +template +struct ProtoKelEncodeImpl> { + static Error + encode(typename Message::Reader data, + Buffer &buffer) { std::string_view view = data.get(); size_t size = view.size(); @@ -86,24 +94,27 @@ template <> struct ProtoKelEncodeImpl> { return noError(); } - static size_t size(typename MessagePrimitive::Reader reader) { + static size_t + size(typename Message::Reader reader) { return sizeof(size_t) + reader.get().size(); } }; -template struct ProtoKelEncodeImpl> { +template +struct ProtoKelEncodeImpl, Container>> { template static typename std::enable_if::type - encodeMembers(typename MessageList::Reader, Buffer &) { + encodeMembers(typename Message, Container>::Reader, + Buffer &) { return noError(); } template - static typename std::enable_if < - i::type - encodeMembers(typename MessageList::Reader data, Buffer &buffer) { + static typename std::enable_if<(i < sizeof...(T)), Error>::type + encodeMembers(typename Message, Container>::Reader data, + Buffer &buffer) { Error error = - ProtoKelEncodeImpl::type>:: + ProtoKelEncodeImpl>:: encode(data.template get(), buffer); if (error.failed()) { return error; @@ -112,47 +123,52 @@ template struct ProtoKelEncodeImpl> { return encodeMembers(data, buffer); } - static Error encode(typename MessageList::Reader data, - Buffer &buffer) { + static Error + encode(typename Message, Container>::Reader data, + Buffer &buffer) { return encodeMembers<0>(data, buffer); } template static typename std::enable_if::type - sizeMembers(typename MessageList::Reader) { + sizeMembers(typename Message, Container>::Reader) { return 0; } template static typename std::enable_if < - i::type - sizeMembers(typename MessageList::Reader reader) { - return ProtoKelEncodeImpl::type>:: + i::type sizeMembers( + typename Message, Container>::Reader reader) { + return ProtoKelEncodeImpl>:: size(reader.template get()) + sizeMembers(reader); } - static size_t size(typename MessageList::Reader reader) { + static size_t + size(typename Message, Container>::Reader reader) { return sizeMembers<0>(reader); } }; -template -struct ProtoKelEncodeImpl...>> { +template +struct ProtoKelEncodeImpl< + Message...>, Container>> { template static typename std::enable_if::type - encodeMembers(typename MessageStruct...>::Reader, + encodeMembers(typename Message...>, + Container>::Reader, Buffer &) { return noError(); } template static typename std::enable_if < i::type encodeMembers( - typename MessageStruct...>::Reader data, + typename Message...>, + Container>::Reader data, Buffer &buffer) { Error error = - ProtoKelEncodeImpl::type>:: + ProtoKelEncodeImpl>:: encode(data.template get(), buffer); if (error.failed()) { return error; @@ -161,79 +177,87 @@ struct ProtoKelEncodeImpl...>> { } static Error - encode(typename MessageStruct...>::Reader data, + encode(typename Message...>, + Container>::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) { + sizeMembers(typename Message...>, + Container>::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()); + typename Message...>, + Container>::Reader reader) { + return ProtoKelEncodeImpl>:: + size(reader.template get()) + + sizeMembers(reader); + } + + static size_t + size(typename Message...>, + Container>::Reader reader) { + return sizeMembers<0>(reader); + } +}; + +template +struct ProtoKelEncodeImpl< + Message...>, Container>> { + + template + static typename std::enable_if::type + encodeMembers(typename Message...>, + Container>::Reader, + Buffer &) { + return noError(); + } + + template + static typename std::enable_if < + i::type encodeMembers( + typename Message...>, + Container>::Reader reader, + Buffer &buffer) { + if (reader.index() == i) { + Error error = StreamValue::encode(i, buffer); + if (error.failed()) { + return error; + } + return ProtoKelEncodeImpl>::encode(reader.template get(), buffer); + } + return encodeMembers(reader, buffer); + } + + static Error + encode(typename Message...>, + Container>::Reader reader, + Buffer &buffer) { + return encodeMembers<0>(reader, buffer); + } + + template + static typename std::enable_if::type + sizeMembers(typename Message...>, + Container>::Reader) { + return 0; + } + + template + static typename std::enable_if < + i::type sizeMembers( + typename Message...>, + Container>::Reader reader) { + if (reader.index() == i) { + return ProtoKelEncodeImpl>::size(reader.template get()); } return sizeMembers(reader); } @@ -242,8 +266,9 @@ struct ProtoKelEncodeImpl...>> { * Size of union id + member size */ static size_t - size(typename MessageUnion...>::Reader reader) { - return sizeof(uint32_t) + sizeMembers<0>(reader); + size(typename Message...>, + Container>::Reader reader) { + return sizeof(msg_union_id_t) + sizeMembers<0>(reader); } }; /* @@ -251,19 +276,24 @@ struct ProtoKelEncodeImpl...>> { */ template struct ProtoKelDecodeImpl; -template struct ProtoKelDecodeImpl> { - static Error decode(typename MessagePrimitive::Builder data, - Buffer &buffer) { - T val = 0; - Error error = StreamValue::decode(val, buffer); +template +struct ProtoKelDecodeImpl, Container>> { + static Error + decode(typename Message, Container>::Builder data, + Buffer &buffer) { + typename PrimitiveTypeHelper>::Type val = 0; + Error error = StreamValue>::Type>::decode(val, buffer); data.set(val); return error; } }; -template <> struct ProtoKelDecodeImpl> { - static Error decode(typename MessagePrimitive::Builder data, - Buffer &buffer) { +template +struct ProtoKelDecodeImpl> { + static Error + decode(typename Message::Builder data, + Buffer &buffer) { size_t size = 0; if (sizeof(size) > buffer.readCompositeLength()) { return recoverableError("Buffer too small"); @@ -293,21 +323,23 @@ template <> struct ProtoKelDecodeImpl> { } }; -template struct ProtoKelDecodeImpl> { +template +struct ProtoKelDecodeImpl, Container>> { template static typename std::enable_if::type - decodeMembers(typename MessageList::Builder, Buffer &) { + decodeMembers(typename Message, Container>::Builder, + Buffer &) { return noError(); } template static typename std::enable_if < - i::type - decodeMembers(typename MessageList::Builder builder, - Buffer &buffer) { + i::type decodeMembers( + typename Message, Container>::Builder builder, + Buffer &buffer) { Error error = - ProtoKelDecodeImpl::type>:: + ProtoKelDecodeImpl>:: decode(builder.template init(), buffer); if (error.failed()) { return error; @@ -315,17 +347,21 @@ template struct ProtoKelDecodeImpl> { return decodeMembers(builder, buffer); } - static Error decode(typename MessageList::Builder builder, - Buffer &buffer) { + static Error + decode(typename Message, Container>::Builder builder, + Buffer &buffer) { return decodeMembers<0>(builder, buffer); } }; -template -struct ProtoKelDecodeImpl...>> { +template +struct ProtoKelDecodeImpl< + Message...>, Container>> { + template static typename std::enable_if::type - decodeMembers(typename MessageStruct...>::Builder, + decodeMembers(typename Message...>, + Container>::Builder, Buffer &) { return noError(); } @@ -333,12 +369,12 @@ struct ProtoKelDecodeImpl...>> { template static typename std::enable_if < i::type decodeMembers( - typename MessageStruct...>::Builder - builder, + typename Message...>, + Container>::Builder builder, Buffer &buffer) { Error error = - ProtoKelDecodeImpl::type>:: + ProtoKelDecodeImpl>:: decode(builder.template init(), buffer); if (error.failed()) { return error; @@ -346,18 +382,21 @@ struct ProtoKelDecodeImpl...>> { return decodeMembers(builder, buffer); } - static Error decode( - typename MessageStruct...>::Builder builder, - Buffer &buffer) { + static Error + decode(typename Message...>, + Container>::Builder builder, + Buffer &buffer) { return decodeMembers<0>(builder, buffer); } }; -template -struct ProtoKelDecodeImpl...>> { +template +struct ProtoKelDecodeImpl< + Message...>, Container>> { template static typename std::enable_if::type - decodeMembers(typename MessageUnion...>::Builder, + decodeMembers(typename Message...>, + Container>::Builder, Buffer &, msg_union_id_t) { return noError(); } @@ -365,13 +404,14 @@ struct ProtoKelDecodeImpl...>> { template static typename std::enable_if < i::type decodeMembers( - typename MessageUnion...>::Builder builder, + typename Message...>, + Container>::Builder builder, Buffer &buffer, msg_union_id_t id) { if (id == i) { Error error = - ProtoKelDecodeImpl::type>:: - decode(builder.template init(), buffer); + ProtoKelDecodeImpl>::decode(builder.template init(), buffer); if (error.failed()) { return error; } @@ -380,7 +420,8 @@ struct ProtoKelDecodeImpl...>> { } static Error - decode(typename MessageUnion...>::Builder builder, + decode(typename Message...>, + Container>::Builder builder, Buffer &buffer) { msg_union_id_t id = 0; Error error = StreamValue::decode(id, buffer); @@ -395,11 +436,13 @@ struct ProtoKelDecodeImpl...>> { } }; -template -Error ProtoKelCodec::encode(typename T::Reader reader, Buffer &buffer) { +template +Error ProtoKelCodec::encode(typename Message::Reader reader, + Buffer &buffer) { BufferView view{buffer}; - msg_packet_length_t packet_length = ProtoKelEncodeImpl::size(reader); + msg_packet_length_t packet_length = + ProtoKelEncodeImpl>::size(reader); // Check the size of the packet for the first // message length description @@ -417,7 +460,8 @@ Error ProtoKelCodec::encode(typename T::Reader reader, Buffer &buffer) { } } { - Error error = ProtoKelEncodeImpl::encode(reader, view); + Error error = ProtoKelEncodeImpl>::encode( + reader, view); if (error.failed()) { return error; } @@ -427,9 +471,10 @@ Error ProtoKelCodec::encode(typename T::Reader reader, Buffer &buffer) { return noError(); } -template -Error ProtoKelCodec::decode(typename T::Builder builder, Buffer &buffer, - const Limits &limits) { +template +Error ProtoKelCodec::decode( + typename Message::Builder builder, Buffer &buffer, + const Limits &limits) { BufferView view{buffer}; msg_packet_length_t packet_length = 0; @@ -446,13 +491,15 @@ Error ProtoKelCodec::decode(typename T::Builder builder, Buffer &buffer, } { - Error error = ProtoKelDecodeImpl::decode(builder, view); + Error error = ProtoKelDecodeImpl>::decode( + builder, view); if (error.failed()) { return error; } } { - if (ProtoKelEncodeImpl::size(builder.asReader()) != packet_length) { + if (ProtoKelEncodeImpl>::size( + builder.asReader()) != packet_length) { return criticalError("Bad packet format"); } } @@ -461,4 +508,4 @@ Error ProtoKelCodec::decode(typename T::Builder builder, Buffer &buffer, return noError(); } -} // namespace gin \ No newline at end of file +} // namespace gin diff --git a/source/kelgin/schema.h b/source/kelgin/schema.h new file mode 100644 index 0000000..40bdb3e --- /dev/null +++ b/source/kelgin/schema.h @@ -0,0 +1,51 @@ +#pragma once + +#include "string_literal.h" + +namespace gin { +namespace schema { + +template struct NamedMember {}; + +template struct Struct; + +template +struct Struct...> {}; + +template struct Union; + +template struct Union...> {}; + +template struct Array {}; + +template struct Tuple {}; + +struct String {}; + +struct SignedInteger {}; +struct UnsignedInteger {}; +struct FloatingPoint {}; + +template struct Primitive { + static_assert(((std::is_same_v || + std::is_same_v)&&(N == 1 || N == 2 || + N == 4 || N == 8)) || + (std::is_same_v && (N == 4 || N == 8)), + "Primitive Type is not supported"); +}; + +using Int8 = Primitive; +using Int16 = Primitive; +using Int32 = Primitive; +using Int64 = Primitive; + +using UInt8 = Primitive; +using UInt16 = Primitive; +using UInt32 = Primitive; +using UInt64 = Primitive; + +using Float32 = Primitive; +using Float64 = Primitive; + +} // namespace schema +} // namespace gin diff --git a/source/kelgin/string_literal.h b/source/kelgin/string_literal.h index b19cb54..40d4101 100644 --- a/source/kelgin/string_literal.h +++ b/source/kelgin/string_literal.h @@ -9,17 +9,32 @@ namespace gin { * literal. It guarantees compile time uniqueness and thus allows using strings * in template parameters. */ -template class StringLiteral { +template class StringLiteral { public: - static constexpr std::array data = {Chars..., - '\0'}; - static constexpr std::string_view view() { + constexpr StringLiteral(const CharT (&input)[N]) noexcept { + for (size_t i = 0; i < N; ++i) { + data[i] = input[i]; + } + } + + std::array data{}; + + constexpr std::string_view view() const noexcept { return std::string_view{data.data()}; } + + constexpr bool + operator==(const StringLiteral &) const noexcept = default; + + template + constexpr bool + operator==(const StringLiteral &) const noexcept { + return false; + } }; -} // namespace gin template -constexpr gin::StringLiteral operator""_t() { - return {}; -} \ No newline at end of file +constexpr gin::StringLiteral operator""_key() { + return gin::StringLiteral{Chars..., '\0'}; +} +} // namespace gin diff --git a/test/data/json.h b/test/data/json.h deleted file mode 100644 index 998c811..0000000 --- a/test/data/json.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -#include "message.h" - -namespace gin { -const std::string_view json_org_example = -R"({ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML"] - }, - "GlossSee": "markup" - } - } - } - } -})"; -} \ No newline at end of file diff --git a/test/json.cpp b/test/json.cpp deleted file mode 100644 index adccd5b..0000000 --- a/test/json.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include "suite/suite.h" - -#include -#include - -#include "buffer.h" -#include "source/kelgin/message.h" -#include "source/kelgin/json.h" - -#include "./data/json.h" - -using gin::MessageList; -using gin::MessageStruct; -using gin::MessageStructMember; -using gin::MessagePrimitive; -using gin::heapMessageBuilder; - -using gin::JsonCodec; -using gin::Error; - -using gin::RingBuffer; - -namespace { -typedef MessageList, MessagePrimitive > TestList; - -GIN_TEST("JSON List Encoding"){ - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - auto uint = root.init<0>(); - uint.set(12); - auto string = root.init<1>(); - string.set("free"); - - RingBuffer temp_buffer; - JsonCodec codec; - codec.encode(root.asReader(), temp_buffer); - - std::string tmp_string = temp_buffer.toString(); - GIN_EXPECT(tmp_string == "[12,\"free\"]", std::string{"Bad encoding:\n"} + tmp_string); -} - -typedef MessageStruct< - MessageStructMember, decltype("test_uint"_t)>, - MessageStructMember, decltype("test_string"_t)>, - MessageStructMember, decltype("test_name"_t)>, - MessageStructMember, decltype("test_bool"_t)> -> TestStruct; - -GIN_TEST("JSON Struct Encoding"){ - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - - auto uint = root.init(); - uint.set(23); - - std::string test_string = "foo"; - auto string = root.init(); - string.set(test_string); - - auto string_name = root.init(); - string_name.set("test_name"_t.view()); - - root.init().set(false); - - JsonCodec codec; - RingBuffer temp_buffer; - codec.encode(root.asReader(), temp_buffer); - - std::string expected_result{"{\"test_uint\":23,\"test_string\":\"foo\",\"test_name\":\"test_name\",\"test_bool\":false}"}; - - std::string tmp_string = temp_buffer.toString(); - GIN_EXPECT(tmp_string == expected_result, std::string{"Bad encoding:\n"} + tmp_string); -} - -typedef gin::MessageUnion< - gin::MessageUnionMember, decltype("test_uint"_t)>, - gin::MessageUnionMember, decltype("test_string"_t)> -> TestUnion; - -GIN_TEST("JSON Union Encoding"){ - using namespace gin; - { - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - - auto test_uint = root.init(); - test_uint.set(23); - - RingBuffer buffer; - JsonCodec codec; - - Error error = codec.encode(root.asReader(), buffer); - - GIN_EXPECT(!error.failed(), error.message()); - - std::string expected_result{"{\"test_uint\":23}"}; - - std::string tmp_string = buffer.toString(); - GIN_EXPECT(tmp_string == expected_result, std::string{"Bad encoding:\n"} + tmp_string); - } - { - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - - auto test_string = root.init(); - test_string.set("foo"); - - RingBuffer buffer; - JsonCodec codec; - - Error error = codec.encode(root.asReader(), buffer); - - GIN_EXPECT(!error.failed(), error.message()); - - std::string expected_result{"{\"test_string\":\"foo\"}"}; - - std::string tmp_string = buffer.toString(); - GIN_EXPECT(tmp_string == expected_result, std::string{"Bad encoding:\n"} + tmp_string); - } -} - -GIN_TEST("JSON Struct Decoding"){ - std::string json_string = R"( - { - "test_string" :"banana" , - "test_uint" : 5, - "test_name" : "keldu", - "test_bool" : true - })"; - - auto builder = heapMessageBuilder(); - TestStruct::Builder root = builder.initRoot(); - - JsonCodec codec; - RingBuffer temp_buffer; - temp_buffer.push(*reinterpret_cast(json_string.data()), json_string.size()); - Error error = codec.decode(root, temp_buffer); - GIN_EXPECT( !error.failed(), error.message() ); - - auto reader = root.asReader(); - GIN_EXPECT( reader.get().get() == "banana", "Test String has wrong value" ); - GIN_EXPECT( reader.get().get() == 5, "Test Unsigned has wrong value" ); - GIN_EXPECT( reader.get().get() == "keldu", "Test Name has wrong value" ); - GIN_EXPECT( reader.get().get() == true, "Test Bool has wrong value" ); -} - -GIN_TEST("JSON List Decoding"){ - std::string json_string = R"( - [ - 12, - "free" - ])"; - - auto builder = heapMessageBuilder(); - TestList::Builder root = builder.initRoot(); - - JsonCodec codec; - RingBuffer temp_buffer; - temp_buffer.push(*reinterpret_cast(json_string.data()), json_string.size()); - Error error = codec.decode(root, temp_buffer); - GIN_EXPECT( !error.failed(), error.message() ); - - auto reader = root.asReader(); - GIN_EXPECT( reader.get<0>().get() == 12, "Test Unsigned has wrong value" ); - GIN_EXPECT( reader.get<1>().get() == "free", "Test String has wrong value" ); -} - -typedef MessageStruct< - MessageStructMember, decltype("test_uint"_t)>, - MessageStructMember, - MessageStructMember, decltype("test_name"_t)> -> TestStructDepth; - -GIN_TEST("JSON Struct Decoding Two layer"){ - std::string json_string = R"( - { - "test_struct" :{ - "test_string" : "banana", - "test_uint": 40, - "test_name":"HaDiKo", - "test_bool" :false - }, - "test_uint": 5, - "test_name" : "keldu" - })"; - - auto builder = heapMessageBuilder(); - TestStructDepth::Builder root = builder.initRoot(); - - JsonCodec codec; - RingBuffer temp_buffer; - temp_buffer.push(*reinterpret_cast(json_string.data()), json_string.size()); - Error error = codec.decode(root, temp_buffer); - GIN_EXPECT( !error.failed(), error.message() ); - - auto reader = root.asReader(); - - auto inner_reader = reader.get(); - - GIN_EXPECT( inner_reader.get().get() == "banana", "Test String has wrong value" ); - GIN_EXPECT( inner_reader.get().get() == 40, "Test Unsigned has wrong value" ); - GIN_EXPECT( inner_reader.get().get() == "HaDiKo", "Test Name has wrong value" ); - GIN_EXPECT( inner_reader.get().get() == false, "Test Bool has wrong value" ); - GIN_EXPECT( reader.get().get() == 5, "Test Unsigned has wrong value" ); - GIN_EXPECT( reader.get().get() == "keldu", "Test Name has wrong value" ); -} - -typedef MessageStruct< - MessageStructMember< - MessageStruct< - MessageStructMember< MessagePrimitive, decltype("title"_t)>, - MessageStructMember< - MessageStruct< - MessageStructMember,decltype("title"_t)>, - MessageStructMember< - MessageStruct< - MessageStructMember< - MessageStruct< - MessageStructMember,decltype("ID"_t)>, - MessageStructMember,decltype("SortAs"_t)>, - MessageStructMember,decltype("GlossTerm"_t)>, - MessageStructMember,decltype("Acronym"_t)>, - MessageStructMember,decltype("Abbrev"_t)>, - MessageStructMember< - MessageStruct< - MessageStructMember, decltype("para"_t)>, - MessageStructMember< - MessageList< - MessagePrimitive, - MessagePrimitive - > - , decltype("GlossSeeAlso"_t)> - > - , decltype("GlossDef"_t)>, - MessageStructMember, decltype("GlossSee"_t)> - > - , decltype("GlossEntry"_t)> - > - , decltype("GlossList"_t)> - > - , decltype("GlossDiv"_t)> - > - , decltype("glossary"_t)> -> TestJsonOrgExample; - -GIN_TEST ("JSON.org Decoding Example"){ - auto builder = heapMessageBuilder(); - TestJsonOrgExample::Builder root = builder.initRoot(); - - JsonCodec codec; - RingBuffer temp_buffer; - temp_buffer.push(*reinterpret_cast(gin::json_org_example.data()), gin::json_org_example.size()); - - Error error = codec.decode(root, temp_buffer); - GIN_EXPECT(!error.failed(), error.message()); - - auto reader = root.asReader(); - - auto glossary_reader = reader.get(); - - GIN_EXPECT(glossary_reader.get().get() == "example glossary", "Bad glossary title"); - - auto gloss_div_reader = glossary_reader.get(); - - GIN_EXPECT(gloss_div_reader.get().get() == "S", "bad gloss div value" ); - - auto gloss_list_reader = gloss_div_reader.get(); - - auto gloss_entry_reader = gloss_list_reader.get(); - - GIN_EXPECT(gloss_entry_reader.get().get() == "SGML", "bad ID value" ); - GIN_EXPECT(gloss_entry_reader.get().get() == "SGML", "bad SortAs value" ); - GIN_EXPECT(gloss_entry_reader.get().get() == "Standard Generalized Markup Language", "bad GlossTerm value" ); - GIN_EXPECT(gloss_entry_reader.get().get() == "SGML", "bad Acronym value" ); - GIN_EXPECT(gloss_entry_reader.get().get() == "ISO 8879:1986", "bad Abbrev value" ); - GIN_EXPECT(gloss_entry_reader.get().get() == "markup", "bad GlossSee value" ); - - auto gloss_def_reader = gloss_entry_reader.get(); - - GIN_EXPECT(gloss_def_reader.get().get() == "A meta-markup language, used to create markup languages such as DocBook.", "para value wrong"); - - auto gloss_see_also_reader = gloss_def_reader.get(); - - GIN_EXPECT(gloss_see_also_reader.get<0>().get() == "GML", "List 0 value wrong"); - GIN_EXPECT(gloss_see_also_reader.get<1>().get() == "XML", "List 1 value wrong"); - - // (void) gloss_div_reader; -} -} diff --git a/test/message.cpp b/test/message.cpp index 7cd6042..182b457 100644 --- a/test/message.cpp +++ b/test/message.cpp @@ -4,50 +4,50 @@ #include #include "source/kelgin/message.h" -using gin::MessageList; -using gin::MessageStruct; -using gin::MessageStructMember; -using gin::MessagePrimitive; -using gin::heapMessageBuilder; +#include "source/kelgin/schema.h" namespace { -typedef MessageList, MessagePrimitive > TestList; +namespace schema { + using namespace gin::schema; +} + +using TestTuple = schema::Tuple; GIN_TEST("MessageList"){ std::string test_string_1 = "banana"; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - auto uint = root.init<0>(); + auto root = gin::heapMessageRoot(); + auto builder = root.build(); + auto uint = builder.init<0>(); uint.set(10); - auto string = root.init<1>(); + auto string = builder.init<1>(); string.set(test_string_1); - auto root_reader = root.asReader(); - auto uint_reader = root_reader.get<0>(); - auto string_reader = root_reader.get<1>(); + auto reader = root.read(); + auto uint_reader = reader.get<0>(); + auto string_reader = reader.get<1>(); GIN_EXPECT( uint_reader.get() == 10 && string_reader.get() == test_string_1, "wrong values"); } -typedef MessageList, MessagePrimitive>, MessagePrimitive > NestedTestList; +using NestedTestTuple = schema::Tuple, schema::String>; GIN_TEST("MessageList nested"){ std::string test_string_1 = "banana"; std::string test_string_2 = "bat"; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - auto inner_list = root.init<0>(); + auto root = gin::heapMessageRoot(); + auto builder = root.build(); + auto inner_list = builder.init<0>(); auto uint = inner_list.init<0>(); uint.set(20); auto inner_string = inner_list.init<1>(); inner_string.set(test_string_2); - auto string = root.init<1>(); + auto string = builder.init<1>(); string.set(test_string_1); - auto root_reader = root.asReader(); + auto root_reader = root.read(); auto inner_reader = root_reader.get<0>(); auto uint_reader = inner_reader.get<0>(); auto inner_string_reader = inner_reader.get<1>(); @@ -56,30 +56,33 @@ GIN_TEST("MessageList nested"){ GIN_EXPECT(uint_reader.get() == 20 && inner_string_reader.get() == test_string_2 && string_reader.get() == test_string_1, "wrong values"); } -typedef MessageStruct< - MessageStructMember, decltype("test_uint"_t)>, - MessageStructMember, decltype("test_string"_t)>, - MessageStructMember, decltype("test_name"_t)> -> TestStruct; +using TestStruct = schema::Struct< + schema::NamedMember, + schema::NamedMember, + schema::NamedMember +>; GIN_TEST("MessageStruct"){ std::string test_string = "foo"; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - auto uint = root.init(); + auto root = gin::heapMessageRoot(); + auto builder = root.build(); + auto uint = builder.init<"test_uint">(); uint.set(23); - auto string = root.init(); + auto string = builder.init<"test_string">(); string.set(test_string); - auto string_name = root.init(); - string_name.set(&"test_name"_t.data[0]); + auto string_name = builder.init<"test_name">(); + string_name.set("test_name"); - auto reader = root.asReader(); - auto uint_reader = reader.get(); - auto string_reader = reader.get(); - auto name_reader = reader.get(); + auto reader = root.read(); + auto uint_reader = reader.get<"test_uint">(); + auto string_reader = reader.get<"test_string">(); + auto name_reader = reader.get<"test_name">(); + /* + * Set string to another value to guarantee no changes + */ test_string = "foo2"; GIN_EXPECT(uint_reader.get() == 23 && string_reader.get() != test_string && string_reader.get() == "foo" && name_reader.get() == "test_name", "wrong values"); } -} \ No newline at end of file +} diff --git a/test/proto_kel.cpp b/test/proto_kel.cpp index cd0a029..074ad3e 100644 --- a/test/proto_kel.cpp +++ b/test/proto_kel.cpp @@ -5,54 +5,59 @@ #include namespace { -typedef gin::MessagePrimitive TestSize; +namespace schema { + using namespace gin::schema; +} +using TestSize = schema::UInt32; -typedef gin::MessageList, gin::MessagePrimitive> TestList; +using TestTuple = schema::Tuple; -typedef gin::MessageStruct< - gin::MessageStructMember, decltype("test_uint"_t)>, - gin::MessageStructMember, decltype("test_string"_t)>, - gin::MessageStructMember, decltype("test_name"_t)> -> TestStruct; +using TestStruct = schema::Struct< + schema::NamedMember, + schema::NamedMember, + schema::NamedMember +>; -typedef gin::MessageUnion< - gin::MessageUnionMember, decltype("test_uint"_t)>, - gin::MessageUnionMember, decltype("test_string"_t)> -> TestUnion; +using TestUnion = schema::Union< + schema::NamedMember, + schema::NamedMember +>; GIN_TEST("Primitive Encoding"){ using namespace gin; uint32_t value = 5; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); + auto root = heapMessageRoot(); + auto builder = root.build(); - root.set(value); + builder.set(value); RingBuffer temp_buffer; + ProtoKelCodec codec; - Error error = ProtoKelEncodeImpl::encode(root.asReader(), temp_buffer); + Error error = codec.encode(root.read(), temp_buffer); GIN_EXPECT(!error.failed(), error.message()); - GIN_EXPECT(temp_buffer.readCompositeLength() == sizeof(value), "Bad Size: " + std::to_string(temp_buffer.readCompositeLength())); - GIN_EXPECT(temp_buffer[0] == 5 && temp_buffer[1] == 0 && temp_buffer[2] == 0 && temp_buffer[3] == 0, "Wrong encoded values"); + GIN_EXPECT(temp_buffer.readCompositeLength() == (sizeof(value)+sizeof(msg_packet_length_t)), "Bad Size: " + std::to_string(temp_buffer.readCompositeLength())); + constexpr size_t pkt_shift = sizeof(msg_packet_length_t); + GIN_EXPECT(temp_buffer[pkt_shift] == 5 && temp_buffer[pkt_shift+1] == 0 && temp_buffer[pkt_shift+2] == 0 && temp_buffer[pkt_shift+3] == 0, "Wrong encoded values"); } GIN_TEST("List Encoding"){ using namespace gin; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); + auto root = heapMessageRoot(); + auto builder = root.build(); - auto first = root.init<0>(); + auto first = builder.init<0>(); first.set(2135231); - auto second = root.init<1>(); + auto second = builder.init<1>(); second.set(43871); RingBuffer buffer; ProtoKelCodec codec; - Error error = codec.encode(root.asReader(), buffer); + Error error = codec.encode(root.read(), buffer); GIN_EXPECT(!error.failed(), error.message()); GIN_EXPECT(buffer.readCompositeLength() == 14, "Bad Size: " + std::to_string(buffer.readCompositeLength())); @@ -62,23 +67,23 @@ GIN_TEST("List Encoding"){ GIN_TEST("Struct Encoding"){ using namespace gin; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); + auto root = heapMessageRoot(); + auto builder = root.build(); - auto test_uint = root.init(); + auto test_uint = builder.init<"test_uint">(); test_uint.set(23); std::string test_string = "foo"; - auto string = root.init(); + auto string = builder.init<"test_string">(); string.set(test_string); - auto string_name = root.init(); - string_name.set("test_name"_t.view()); + auto string_name = builder.init<"test_name">(); + string_name.set("test_name"); RingBuffer buffer; ProtoKelCodec codec; - Error error = codec.encode(root.asReader(), buffer); + Error error = codec.encode(builder.asReader(), buffer); GIN_EXPECT(!error.failed(), error.message()); GIN_EXPECT(buffer.readCompositeLength() == 40, "Bad Size: " + std::to_string(buffer.readCompositeLength())); @@ -89,16 +94,16 @@ GIN_TEST("Struct Encoding"){ GIN_TEST("Union Encoding"){ using namespace gin; { - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); + auto root = heapMessageRoot(); + auto builder = root.build(); - auto test_uint = root.init(); + auto test_uint = builder.init<"test_uint">(); test_uint.set(23); RingBuffer buffer; ProtoKelCodec codec; - Error error = codec.encode(root.asReader(), buffer); + Error error = codec.encode(builder.asReader(), buffer); GIN_EXPECT(!error.failed(), error.message()); GIN_EXPECT(buffer.readCompositeLength() == 16, "Bad Size: " + std::to_string(buffer.readCompositeLength())); @@ -106,16 +111,16 @@ GIN_TEST("Union Encoding"){ == buffer.toHex(), "Not equal encoding:\n"+buffer.toHex()); } { - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); + auto root = heapMessageRoot(); + auto builder = root.build(); - auto test_string = root.init(); + auto test_string = builder.init<"test_string">(); test_string.set("foo"); RingBuffer buffer; ProtoKelCodec codec; - Error error = codec.encode(root.asReader(), buffer); + Error error = codec.encode(builder.asReader(), buffer); GIN_EXPECT(!error.failed(), error.message()); GIN_EXPECT(buffer.readCompositeLength() == 23, "Bad Size: " + std::to_string(buffer.readCompositeLength())); @@ -124,7 +129,7 @@ GIN_TEST("Union Encoding"){ } } -GIN_TEST("List Decoding"){ +GIN_TEST("Tuple Decoding"){ using namespace gin; const uint8_t buffer_raw[] = {0x06, 0, 0, 0, 0, 0, 0, 0, 0xbf, 0x94, 0x20, 0x00, 0x5f, 0xab}; @@ -133,13 +138,13 @@ GIN_TEST("List Decoding"){ ProtoKelCodec codec; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); + auto root = heapMessageRoot(); + auto builder = root.build(); - Error error = codec.decode(root, buffer); + Error error = codec.decode(builder, buffer); GIN_EXPECT(!error.failed(), error.message()); - auto reader = root.asReader(); + auto reader = builder.asReader(); auto first = reader.get<0>(); auto second = reader.get<1>(); @@ -156,15 +161,15 @@ GIN_TEST("Struct Decoding"){ ProtoKelCodec codec; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); + auto root = heapMessageRoot(); + auto builder = root.build(); - Error error = codec.decode(root, buffer); - auto reader = root.asReader(); + Error error = codec.decode(builder, buffer); + auto reader = builder.asReader(); - auto foo_string = reader.get(); - auto test_uint = reader.get(); - auto test_name = reader.get(); + auto foo_string = reader.get<"test_string">(); + auto test_uint = reader.get<"test_uint">(); + auto test_name = reader.get<"test_name">(); GIN_EXPECT(!error.failed(), error.message()); GIN_EXPECT(foo_string.get() == "foo" && test_uint.get() == 23 && test_name.get() == "test_name", "Values not correctly decoded"); @@ -179,15 +184,15 @@ GIN_TEST("Union Decoding"){ ProtoKelCodec codec; - auto builder = heapMessageBuilder(); - auto root = builder.initRoot(); - auto reader = root.asReader(); + auto root = heapMessageRoot(); + auto builder = root.build(); + auto reader = builder.asReader(); - Error error = codec.decode(root, buffer); + Error error = codec.decode(builder, buffer); GIN_EXPECT(!error.failed(), error.message()); - GIN_EXPECT(reader.holdsAlternative(), "Wrong union value"); - auto str_rd = reader.get(); + GIN_EXPECT(reader.hasAlternative<"test_string">(), "Wrong union value"); + auto str_rd = reader.get<"test_string">(); GIN_EXPECT(str_rd.get() == "foo", "Wrong value: " + std::string{str_rd.get()}); } }