diff --git a/README.md b/README.md index b01756e9e..ec49849f0 100644 --- a/README.md +++ b/README.md @@ -200,105 +200,80 @@ msgpacks. See section below to understand how to decode tuples. ### Data manipulation Now let's consider a bit more sophisticated requests. -Assume we have space with `id = 512` and following format on the server: -`CREATE TABLE t(id INT PRIMARY KEY, a TEXT, b DOUBLE);` -Preparing analogue of `t:replace(1, "111", 1.01);` request can be done this way: +Assume we have a space with `id = 512` and following format on the server: +`CREATE TABLE t(id INT PRIMARY KEY, name TEXT, coef DOUBLE);` +Preparing analogue of `t:replace{1, "111", 1.01};` request can be done this way: ``` -std::tuple data = std::make_tuple(1 /* field 1*/, "111" /* field 2*/, 1.01 /* field 3*/); -rid_t replace = conn.space[512].replace(data); +std::tuple data = std::make_tuple(1 /*id*/, "111" /*name*/, 1.01 /*coef*/); +rid_t my_replace = conn.space[512].replace(data); ``` -To execute select query `t.index[1]:select({1}, {limit = 1})`: - -``` -auto i = conn.space[512].index[1]; -rid_t select = i.select(std::make_tuple(1), 1, 0 /*offset*/, IteratorType::EQ); -``` - -### Data readers -Responses from server contain raw data (i.e. encoded into msgpuck tuples). To -decode client's data, users have to write their own decoders (based on featured -schema). Let's define structure describing data stored in space `t`: +As a good alternative, we could use structure instead of std::tuple, but we +would have to provide once a way how it must be encoded: ``` struct UserTuple { - uint64_t field1; - std::string field2; - double field3; + uint64_t id; + std::string name; + double coef; + + static constexpr auto mpp = std::make_tuple( + &UserTuple::id, &UserTuple::name, &UserTuple::coef); }; + +... + +UserTuple tuple{.id = 1, .name = "aa", .coef = 1.01}; +rid_t my_replace = conn.space[512].replace(data); ``` -Prototype of the base reader is given in `src/mpp/Dec.hpp`: +To execute select query `t.index[1]:select({1}, {limit = 1})`: + ``` -template -struct SimpleReaderBase : DefaultErrorHandler { - using BufferIterator_t = typename BUFFER::iterator; - /* Allowed type of values to be parsed. */ - static constexpr Type VALID_TYPES = TYPE; - BufferIterator_t* StoreEndIterator() { return nullptr; } -}; +auto i = conn.space[512].index[1]; +rid_t my_select = i.select(std::make_tuple(1), 1, 0 /*offset*/, IteratorType::EQ); ``` -So every new reader should inherit from it or directly from `DefaultErrorHandler`. -To parse particular value, we should define `Value()` method. First two arguments -are common and unused as a rule, but the third - defines parsed value. So in -case of POD stuctures it's enough to provide byte-to-byte copy. Since in our -schema there are fields of three different types, let's descripe three `Value()` -functions: + +### Decoding result + +Responses from server contain raw data (i.e. encoded into msgpuck tuples). +To decode client's data, we have to provide user storage that implicitly +describes tuple format. For example, we know that the space (and each tuple) +has three fields: unsigned, string and number. +Then std::tuple can be used as complete storage +for decoding tuples of such space. Since select returns a dynamic array of +tuples, the storage also must be a dynamic array (for example, vector): + ``` -struct UserTupleValueReader : mpp::DefaultErrorHandler { - /* Store instance of tuple to be parsed. */ - UserTuple& tuple; - /* Enumerate all types which can be parsed. Otherwise */ - static constexpr mpp::Type VALID_TYPES = mpp::MP_UINT | mpp::MP_STR | mpp::MP_DBL; - UserTupleValueReader(UserTuple& t) : tuple(t) {} - - /* Value's extractors. */ - void Value(const BufIter_t&, mpp::compact::Type, uint64_t u) - { - tuple.field1 = u; - } - void Value(const BufIter_t&, mpp::compact::Type, double d) - { - tuple.field3 = d; - } - void Value(const BufIter_t& itr, mpp::compact::Type, mpp::StrValue v) - { - BufIter_t tmp = itr; - tmp += v.offset; - std::string &dst = tuple.field2; - while (v.size) { - dst.push_back(*tmp); - ++tmp; - --v.size; - } - } -}; +rid_t my_select = i.select(....); +// wait for response... +assert(conn.futureIsReady(my_select)); +auto response = conn.getResponse(my_select); +std::vector> results; +response.body.data.decode(results); +// use results... ``` -It is worth mentioning that tuple itself is wrapped into array, so in fact -firstly we should parse array. Let's define another one reader: + +std::tuple is good since it has clearly readable format, but common structures +are much more convenient to use. To decode structures we have to declare their +format for decoder: + ``` -template -struct UserTupleReader : mpp::SimpleReaderBase { - mpp::Dec& dec; - UserTuple& tuple; - - UserTupleReader(mpp::Dec& d, UserTuple& t) : dec(d), tuple(t) {} - void Value(const iterator_t&, mpp::compact::Type, mpp::ArrValue) - { - dec.SetReader(false, UserTupleValueReader{tuple}); - } +struct UserTuple { + uint64_t id; + std::string name; + double coef; + + static constexpr auto mpp = std::make_tuple( + &UserTuple::id, &UserTuple::name, &UserTuple::coef); }; -``` -`SetReader();` sets the reader which is invoked while every entry of the array is -parsed. Now, to make these two readers work, we should create decoder, set -its iterator to the position of encoded tuple and invoke `Read()` method: -``` - UserTuple tuple; - mpp::Dec dec(conn.getInBuf()); - dec.SetPosition(*t.begin); - dec.SetReader(false, UserTupleReader{dec, tuple}); - dec.Read(); + +// Perform select and wait for result... +auto response = conn.getResponse(my_select); +std::vector results; +response.body.data.decode(results); +// use results... ``` ### Writing custom buffer and network provider diff --git a/src/Buffer/Buffer.hpp b/src/Buffer/Buffer.hpp index 647b121ba..6ff915536 100644 --- a/src/Buffer/Buffer.hpp +++ b/src/Buffer/Buffer.hpp @@ -675,8 +675,6 @@ template void Buffer::write(WData data) { - assert(data.size != 0); - char *new_end = m_end + data.size; if (TNT_LIKELY(isSameBlock(m_end, new_end))) { // new_addr is still in block. just copy and advance. @@ -1132,7 +1130,6 @@ template void Buffer::iterator_common::write(WData data) { - assert(data.size > 0); size_t left_in_block = N - (uintptr_t) m_position % N; while (TNT_UNLIKELY(data.size >= left_in_block)) { std::memcpy(m_position, data.data, left_in_block); @@ -1238,7 +1235,6 @@ template void Buffer::iterator_common::read(RData data) { - assert(data.size > 0); /* * The same implementation as in ::set() method buf vice versa: * buffer and data sources are swapped. diff --git a/src/mpp/Constants.hpp b/src/mpp/Constants.hpp index e4bc8f035..041c1c281 100644 --- a/src/mpp/Constants.hpp +++ b/src/mpp/Constants.hpp @@ -1,4 +1,3 @@ -#pragma once /* * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file. * @@ -29,14 +28,14 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#pragma once -#include // TODO - make output to iostream optional? +#include #include #include namespace mpp { -namespace compact { enum Family : uint8_t { MP_NIL /* = 0x00 */, MP_IGNR /* = 0x01 */, @@ -50,43 +49,6 @@ enum Family : uint8_t { MP_EXT /* = 0x09 */, MP_END }; -} // namespace compact { - -using FamilyUnder_t = uint32_t; -enum Family : FamilyUnder_t { - MP_NIL = 1u << compact::MP_NIL, - MP_IGNR = 1u << compact::MP_IGNR, - MP_BOOL = 1u << compact::MP_BOOL, - MP_INT = 1u << compact::MP_INT, - MP_FLT = 1u << compact::MP_FLT, - MP_STR = 1u << compact::MP_STR, - MP_BIN = 1u << compact::MP_BIN, - MP_ARR = 1u << compact::MP_ARR, - MP_MAP = 1u << compact::MP_MAP, - MP_EXT = 1u << compact::MP_EXT, - MP_NUM = (1u << compact::MP_INT) | (1u << compact::MP_FLT), - MP_NONE = 0, - MP_ANY = std::numeric_limits::max(), -}; - -enum ReadError_t { - READ_ERROR_NEED_MORE, - READ_ERROR_BAD_MSGPACK, - READ_ERROR_WRONG_TYPE, - READ_ERROR_MAX_DEPTH_REACHED, - READ_ERROR_ABORTED_BY_USER, - READ_ERROR_END -}; - -enum ReadResult_t : FamilyUnder_t { - READ_SUCCESS = 0, - READ_NEED_MORE = 1u << READ_ERROR_NEED_MORE, - READ_BAD_MSGPACK = 1u << READ_ERROR_BAD_MSGPACK, - READ_WRONG_TYPE = 1u << READ_ERROR_WRONG_TYPE, - READ_MAX_DEPTH_REACHED = 1u << READ_ERROR_MAX_DEPTH_REACHED, - READ_ABORTED_BY_USER = 1u << READ_ERROR_ABORTED_BY_USER, - READ_RESULT_END -}; inline const char *FamilyName[] = { "MP_NIL", @@ -102,7 +64,7 @@ inline const char *FamilyName[] = { "MP_BAD", "MP_NONE" }; -static_assert(std::size(FamilyName) == compact::MP_END + 2, "Smth is forgotten"); +static_assert(std::size(FamilyName) == MP_END + 2, "Smth is forgotten"); inline const char *FamilyHumanName[] = { "nil", @@ -118,84 +80,18 @@ inline const char *FamilyHumanName[] = { "bad", "none" }; -static_assert(std::size(FamilyHumanName) == compact::MP_END + 2, "Smth is forgotten"); - -inline const char *ReadErrorName[] = { - "READ_ERROR_NEED_MORE", - "READ_ERROR_BAD_MSGPACK", - "READ_ERROR_WRONG_TYPE", - "READ_ERROR_MAX_DEPTH_REACHED", - "READ_ERROR_ABORTED_BY_USER", - "READ_ERROR_UNKNOWN", - "READ_SUCCESS", -}; -static_assert(std::size(ReadErrorName) == READ_ERROR_END + 2, "Forgotten"); - -inline constexpr Family -operator|(Family a, Family b) -{ - return static_cast(static_cast(a) | - static_cast(b)); -} - -inline constexpr Family -operator&(Family a, Family b) -{ - return static_cast(static_cast(a) & - static_cast(b)); -} - -inline constexpr ReadResult_t -operator|(ReadResult_t a, ReadResult_t b) -{ - return static_cast(static_cast(a) | - static_cast(b)); -} - -inline constexpr ReadResult_t -operator&(ReadResult_t a, ReadResult_t b) -{ - return static_cast(static_cast(a) & - static_cast(b)); -} - -inline constexpr ReadResult_t -operator~(ReadResult_t a) -{ - return static_cast(~static_cast(a)); -} +static_assert(std::size(FamilyHumanName) == MP_END + 2, "Smth is forgotten"); inline std::ostream& -operator<<(std::ostream& strm, compact::Family t) +operator<<(std::ostream& strm, Family t) { - if (t >= compact::Family::MP_END) - return strm << FamilyName[compact::Family::MP_END] + if (t >= Family::MP_END) + return strm << FamilyName[Family::MP_END] << "(" << static_cast(t) << ")"; return strm << FamilyName[t]; } -inline std::ostream& -operator<<(std::ostream& strm, Family t) -{ - if (t == MP_NONE) - return strm << FamilyName[compact::Family::MP_END + 1]; - static_assert(sizeof(FamilyUnder_t) == sizeof(t), "Very wrong"); - FamilyUnder_t base = t; - bool first = true; - do { - static_assert(sizeof(unsigned) == sizeof(t), "Wrong ctz"); - unsigned part = __builtin_ctz(base); - base ^= 1u << part; - if (first) - first = false; - else - strm << "|"; - strm << static_cast(part); - } while (base != 0); - return strm; -} - -template +template struct family_sequence { static constexpr std::size_t size() noexcept { @@ -203,13 +99,13 @@ struct family_sequence { } }; -template +template static constexpr auto family_sequence_populate(struct family_sequence) { return family_sequence{}; } -template +template inline constexpr auto operator+(family_sequence, family_sequence) { @@ -218,90 +114,54 @@ operator+(family_sequence, family_sequence) namespace details { -template +template struct family_sequence_contains_impl_h { static constexpr bool value = family_sequence_contains_impl_h::value; }; -template +template struct family_sequence_contains_impl_h { static constexpr bool value = false; }; -template +template struct family_sequence_contains_impl_h { static constexpr bool value = true; }; -template +template struct family_sequence_contains_impl_h { static constexpr bool value = true; }; -template +template struct family_sequence_contains_h { static constexpr bool value = family_sequence_contains_impl_h::value; }; -template +template struct family_sequence_contains_h { static constexpr bool value = false; }; -} //namespace details +} // namespace details -template +template static constexpr bool family_sequence_contains(family_sequence) { return details::family_sequence_contains_h::value; } -template +template std::ostream& operator<<(std::ostream& strm, family_sequence) { if (sizeof ...(FAMILY) == 0) - return strm << FamilyName[compact::Family::MP_END + 1]; + return strm << FamilyName[Family::MP_END + 1]; size_t count = 0; ((strm << (count++ ? ", " : "") << FamilyName[FAMILY]), ...); return strm; } -inline std::ostream& -operator<<(std::ostream& strm, ReadError_t t) -{ - if (t >= READ_ERROR_END) - return strm << ReadErrorName[READ_ERROR_END] - << "(" << static_cast(t) << ")"; - return strm << ReadErrorName[t]; -} - -inline std::ostream& -operator<<(std::ostream& strm, ReadResult_t t) -{ - if (t == READ_SUCCESS) - return strm << ReadErrorName[READ_ERROR_END + 1]; - static_assert(sizeof(FamilyUnder_t) == sizeof(t), "Very wrong"); - FamilyUnder_t base = t; - bool first = true; - do { - static_assert(sizeof(unsigned) == sizeof(t), "Wrong ctz"); - unsigned part = __builtin_ctz(base); - base ^= 1u << part; - if (first) - first = false; - else - strm << "|"; - strm << static_cast(part); - } while (base != 0); - return strm; -} - -struct StrValue { uint32_t offset; uint32_t size; }; -struct BinValue { uint32_t offset; uint32_t size; }; -struct ArrValue { uint32_t offset; uint32_t size; }; -struct MapValue { uint32_t offset; uint32_t size; }; -struct ExtValue { int8_t type; uint8_t offset; uint32_t size; }; - -} // namespace mpp { +} // namespace mpp diff --git a/src/mpp/ContAdapter.hpp b/src/mpp/ContAdapter.hpp new file mode 100644 index 000000000..c869058de --- /dev/null +++ b/src/mpp/ContAdapter.hpp @@ -0,0 +1,273 @@ +#pragma once +/* + * Copyright 2010-2024 Tarantool AUTHORS: please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include "../Utils/CStr.hpp" +#include "../Utils/Traits.hpp" + +namespace mpp { + +namespace encode_details { + +/** Common data+size pair that is used for writing of variable-length data. */ +struct WData { + const char *data; + size_t size; +}; + +/** Random struct; used to check whether container has template write method. */ +struct TestWriteStruct { + uint32_t a; + uint16_t b; +}; + +/** Test that container if Buffer-like: has several needed write methods. */ +template +struct is_write_callable_h : std::false_type {}; + +template +struct is_write_callable_h().write(uint8_t{})), + decltype(std::declval().write(uint64_t{})), + decltype(std::declval().write(TestWriteStruct{})), + decltype(std::declval().write({(const char *)0, 1})), + decltype(std::declval().write(tnt::CStr<'a', 'b'>{})) + >> : std::true_type {}; + +template +constexpr bool is_write_callable_v = is_write_callable_h::value; + +template +class BufferWriter { +public: + explicit BufferWriter(CONT& cont_) : cont{cont_} {} + void write(WData data) { cont.write({data.data, data.size}); } + template + void write(tnt::CStr str) + { + cont.write(std::move(str)); + } + template + void write(T&& t) + { + cont.write(std::forward(t)); + } + +private: + CONT& cont; +}; + +template +class StdContWriter { +public: + static_assert(sizeof(*std::declval().data()) == 1); + explicit StdContWriter(CONT& cont_) : cont{cont_} {} + void write(WData data) + { + size_t old_size = std::size(cont); + cont.resize(old_size + data.size); + std::memcpy(std::data(cont) + old_size, data.data, data.size); + } + template + void write(tnt::CStr data) + { + size_t old_size = std::size(cont); + cont.resize(old_size + data.size); + std::memcpy(std::data(cont) + old_size, data.data, data.size); + } + + template + void write(T&& t) + { + static_assert(std::is_standard_layout_v>); + size_t old_size = std::size(cont); + cont.resize(old_size + sizeof(T)); + std::memcpy(std::data(cont) + old_size, &t, sizeof(T)); + } + +private: + CONT& cont; +}; + +template +class PtrWriter { +public: + static_assert(sizeof(C) == 1); + static_assert(!std::is_const_v); + explicit PtrWriter(C *& ptr_) : ptr{ptr_} {} + void write(WData data) + { + std::memcpy(ptr, data.data, data.size); + ptr += data.size; + } + template + void write(tnt::CStr data) + { + std::memcpy(ptr, data.data, data.size); + ptr += data.size; + } + + template + void write(T&& t) + { + static_assert(std::is_standard_layout_v>); + std::memcpy(ptr, &t, sizeof(t)); + ptr += sizeof(t); + } + +private: + C *& ptr; +}; + +template +auto +wr(CONT& cont) +{ + if constexpr (is_write_callable_v) + return BufferWriter{cont}; + else if constexpr (tnt::is_resizable_v && tnt::is_contiguous_v) + return StdContWriter{cont}; + else if constexpr (std::is_pointer_v) + return PtrWriter>{cont}; + else + static_assert(tnt::always_false_v); +} + +} // namespace encode_details + +namespace decode_details { + +/** Common data+size pair that is used for reading of variable-length data. */ +struct RData { + char *data; + size_t size; +}; + +/** Struct that when used as read argument, means skipping of 'size' data. */ +struct Skip { + size_t size; +}; + +/** Random struct; used to check whether container has template read method. */ +struct TestReadStruct { + uint8_t a; + uint64_t b; +}; + +/** Test that container if Buffer-like: has several needed read methods. */ +template +struct is_read_callable_h : std::false_type {}; + +template +struct is_read_callable_h().read(*(uint8_t*)0)), + decltype(std::declval().read(*(uint64_t*)0)), + decltype(std::declval().read(*(TestReadStruct*)0)), + decltype(std::declval().read({(char *)0, 1})), + decltype(std::declval().template get()), + decltype(std::declval().read({1})) + >> : std::true_type {}; + +template +constexpr bool is_read_callable_v = is_read_callable_h::value; + +template +class BufferReader { +public: + explicit BufferReader(CONT& cont_) : cont{cont_} {} + void read(RData data) { cont.read({data.data, data.size}); } + void read(Skip data) { cont.read({data.size}); } + template + void read(T&& t) + { + cont.read(std::forward(t)); + } + template + T get() + { + return cont.template get(); + } + +private: + CONT& cont; +}; + +template +class PtrReader { +public: + static_assert(sizeof(C) == 1); + explicit PtrReader(C *& ptr_) : ptr{ptr_} {} + void read(RData data) + { + std::memcpy(data.data, ptr, data.size); + ptr += data.size; + } + void read(Skip data) { ptr += data.size; } + + template + void read(T& t) + { + static_assert(std::is_standard_layout_v>); + std::memcpy(&t, ptr, sizeof(t)); + ptr += sizeof(t); + } + template + T get() + { + static_assert(std::is_standard_layout_v>); + T t; + std::memcpy(&t, ptr, sizeof(t)); + return t; + } + +private: + C *& ptr; +}; + +template +auto +rd(CONT& cont) +{ + if constexpr (is_read_callable_v) + return BufferReader{cont}; + else if constexpr (std::is_pointer_v) + return PtrReader>{cont}; + else + static_assert(tnt::always_false_v); +} + +} // namespace decode_details + +} // namespace mpp diff --git a/src/mpp/Dec.hpp b/src/mpp/Dec.hpp index 2b6311678..278013b52 100644 --- a/src/mpp/Dec.hpp +++ b/src/mpp/Dec.hpp @@ -33,12 +33,19 @@ #include #include #include +#include +#include #include +#include +#include "BSwap.hpp" #include "ClassRule.hpp" +#include "ContAdapter.hpp" #include "Constants.hpp" #include "Rules.hpp" #include "Spec.hpp" +#include "../Utils/CStr.hpp" +#include "../Utils/Traits.hpp" namespace mpp { @@ -60,7 +67,7 @@ constexpr bool is_any_putable_v = /** * If it is true, the object of type T will not be decoded - raw data will * be saved to it. - * + * * Now it supports only a pair of iterators (probably, wrapped with * mpp::as_raw). The check implicilty implies that BUF is an iterator, not * buffer - it would be strange to pass a pair of buffer to decoder. @@ -131,33 +138,33 @@ constexpr auto detectFamily() } else if constexpr (has_dec_rule_v) { return detectFamily())>(); } else if constexpr (tnt::is_optional_v) { - return family_sequence_populate( + return family_sequence_populate( detectFamily>()); } else if constexpr (tnt::is_variant_v) { return detectFamilyVariant(); } else if constexpr (std::is_convertible_v) { - return family_sequence{}; + return family_sequence{}; } else if constexpr (std::is_same_v) { - return family_sequence{}; + return family_sequence{}; } else if constexpr (tnt::is_integer_v) { - return family_sequence{}; + return family_sequence{}; } else if constexpr (std::is_floating_point_v) { - return family_sequence{}; + return family_sequence{}; } else if constexpr (tnt::is_contiguous_char_v) { - return family_sequence{}; + return family_sequence{}; } else if constexpr (tnt::is_tuplish_v) { if constexpr (tnt::tuple_size_v == 0) - return family_sequence{}; + return family_sequence{}; else if constexpr (tnt::is_tuplish_of_pairish_v) - return family_sequence{}; + return family_sequence{}; else - return family_sequence{}; + return family_sequence{}; } else if constexpr (is_any_putable_v || tnt::is_contiguous_v) { if constexpr (tnt::is_pairish_v>) - return family_sequence{}; + return family_sequence{}; else - return family_sequence{}; + return family_sequence{}; } else { static_assert(tnt::always_false_v, "Failed to recognise type"); @@ -165,7 +172,7 @@ constexpr auto detectFamily() } } -template +template constexpr bool hasChildren(family_sequence) { return (rule_by_family_t::has_children || ...); @@ -246,6 +253,24 @@ using path_push_t = STATIC_SIZE, STATIC_POS)>; +/* + * Construct a pair of two references to two given objects. + */ +template +static constexpr std::pair ref_pair(T& t, U& u) +{ + return std::pair{t, u}; +} + +/* + * Construct a pair of two references to given object and to std::ignore. + */ +template +static constexpr std::pair ref_uno(T& t) +{ + return ref_pair(t, std::ignore); +} + template struct Resolver { template @@ -263,6 +288,7 @@ struct Resolver { static_assert((requires_pos_and_size && POS < SIZE) || (!requires_pos_and_size && POS + SIZE == 0)); + /** Helper of dyn_arg_pos(). */ template static constexpr size_t dyn_arg_pos(tnt::iseq) { @@ -271,104 +297,101 @@ struct Resolver { return USR_ARG_COUNT + X; } + /** + * Find index of an argument that represents dynamic position of + * current (Ith) path element. + */ static constexpr size_t dyn_arg_pos() { return dyn_arg_pos(tnt::make_iseq{}); } + /** + * Base resolver. Simply resolve Ith element of path, which along + * with general types can return reference to an object (which is + * expected to have decoding rules) or pointer to member. + * Return a pair, with the resolved thing as the first member. The + * second member of the pair is exactly what was got from resolving + * the parent, (I-1)th element of the path (std::ignore if I == 0). + */ template - static constexpr size_t find_obj_index() - { - using E = decltype(extract(std::declval()...)); - using R = unwrap_t; - if constexpr (has_dec_rule_v) { - return I; - } else if constexpr (std::is_member_pointer_v || - tnt::is_tuplish_v) { - using PrevResolver = Resolver; - return PrevResolver::template find_obj_index(); - } else { - static_assert(tnt::always_false_v); - } - } - - template - static constexpr auto&& prev(T... t) - { - return unwrap(Resolver::get(t...)); - } - - template - static constexpr auto&& extract(T... t) + static constexpr auto get_raw(T... t) { if constexpr (TYPE == PIT_STATIC_L0) { - return std::get(std::tie(t...)).get(); + return ref_uno(std::get(std::tie(t...)).get()); } else if constexpr (is_path_item_static(PI)) { - return tnt::get(prev(t...)); + auto p = Resolver::get_pair(t...); + auto& b = unwrap(p.first); + return ref_pair(tnt::get(b), p.second); } else if constexpr (TYPE == PIT_DYN_POS) { + auto p = Resolver::get_pair(t...); + auto& b = unwrap(p.first); constexpr size_t ARG_POS = dyn_arg_pos(); uint64_t arg = std::get(std::tie(t...)); - return std::data(prev(t...))[arg >> 32]; + return ref_pair(std::data(b)[arg >> 32], p.second); } else if constexpr (TYPE == PIT_DYN_BACK) { - return prev(t...).back(); + auto p = Resolver::get_pair(t...); + auto& b = unwrap(p.first); + return ref_pair(b.back(), p.second); } else if constexpr (TYPE == PIT_DYN_ADD) { - return prev(t...); + return Resolver::get_pair(t...); } else if constexpr (TYPE == PIT_DYN_KEY) { - return prev(t...); + return Resolver::get_pair(t...); } else if constexpr (TYPE == PIT_OPTIONAL) { - auto &&opt = prev(t...); + auto p = Resolver::get_pair(t...); + auto& opt = unwrap(p.first); assert(opt.has_value()); - return *opt; + return ref_pair(*opt, p.second); } else if constexpr (TYPE == PIT_VARIANT) { - return tnt::get(prev(t...)); + auto p = Resolver::get_pair(t...); + auto& var = unwrap(p.first); + return ref_pair(tnt::get(var), p.second); } else { static_assert(tnt::always_false_v); } } - template - static constexpr auto&& unrule(T... t) - { - using E = decltype(extract(std::declval()...)); - using R = unwrap_t; - if constexpr (has_dec_rule_v) - return get_dec_rule(); - else - return extract(t...); - } - - template - static constexpr auto&& self_unwrap(T&& t) + /** + * Transform a pair that was got from the get_raw method above. + * The result is also a pair of references. + * 1. If the first member of pair is an object with decoding rule - + * create pair with decoding rule and the object and recall this + * function recursively. + * 2. If the first member of pair is pointer to member - apply it to + * object in the second member. Create a pair with the result as the + * first member and recall this function recursively. + * 3. Return original pair otherwise. + */ + template + static constexpr auto transform_raw(std::pair p) { using R = unwrap_t; if constexpr (has_dec_rule_v) { - using RULE = unwrap_t())>; - if constexpr (std::is_member_pointer_v) { - return self_unwrap(unwrap(std::forward(t)).* - unwrap(get_dec_rule())); - } else { - return std::forward(t); - } + auto q = ref_pair(get_dec_rule(), p.first); + return transform_raw(q); + } else if constexpr (std::is_member_pointer_v) { + auto q = ref_uno(unwrap(p.second).*unwrap(p.first)); + return transform_raw(q); } else { - return std::forward(t); + return p; } } + /** Get transformed pair. */ template - static constexpr auto&& get(T... t) + static constexpr auto get_pair(T... t) { - using U = decltype(unrule(std::declval()...)); - using R = unwrap_t; - if constexpr (std::is_member_pointer_v) { - constexpr size_t OBJ_I = find_obj_index(); - using Res = Resolver; - return self_unwrap(unwrap(Res::extract(t...)).* - unwrap(unrule(t...))); - } else { - return unrule(t...); - } + return transform_raw(get_raw(t...)); } + /** Resolve path to Ith element. */ + template + static constexpr auto& get(T... t) + { + return get_pair(t...).first; + } + + /** Get the expected number of argument in get(T...) above. */ static constexpr size_t expected_arg_count() { return dyn_arg_pos() + (is_path_item_dynamic(ITEM) ? 1 : 0); @@ -393,7 +416,7 @@ constexpr auto&& path_resolve_parent(tnt::iseq, T... t) constexpr size_t SIMPLEX_SUBRULE = 16; -template +template constexpr auto get_subrules() { using RULE = rule_by_family_t; @@ -405,19 +428,19 @@ constexpr auto get_subrules() return complex_seq{}; } -template +template auto read_value(BUF& buf) { using RULE = rule_by_family_t; if constexpr (SUBRULE == SIMPLEX_SUBRULE) { typename RULE::simplex_value_t tag; - buf.read(tag); + rd(buf).read(tag); assert(tag >= rule_simplex_tag_range_v.first); assert(tag <= rule_simplex_tag_range_v.last); [[maybe_unused]] typename RULE::simplex_value_t val = tag - RULE::simplex_tag; - if constexpr (FAMILY == compact::MP_NIL) + if constexpr (FAMILY == MP_NIL) return tnt::empty_value; else if constexpr (RULE::is_bool) return bool(val); @@ -427,25 +450,25 @@ auto read_value(BUF& buf) return val; } else { uint8_t tag; - buf.read(tag); + rd(buf).read(tag); assert(tag == RULE::complex_tag + SUBRULE); using TYPES = typename RULE::complex_types; using V = std::tuple_element_t; under_uint_t u; - buf.read(u); + rd(buf).read(u); V val = bswap(u); return val; } } -template +template auto read_item(BUF& buf, ITEM& item) { using RULE = rule_by_family_t; auto val = read_value(buf); if constexpr (RULE::has_ext) { int8_t ext_type; - buf.read(ext_type); + rd(buf).read(ext_type); item.ext_type = ext_type; } if constexpr (RULE::has_data) { @@ -468,11 +491,11 @@ auto read_item(BUF& buf, ITEM& item) if (size > std::size(item)) size = std::size(item); } - buf.read({std::data(item), size}); + rd(buf).read({std::data(item), size}); if constexpr (tnt::is_limited_v || !tnt::is_resizable_v) { if (size < size_t(val)) - buf.read({size_t(val) - size}); + rd(buf).read({size_t(val) - size}); } } else if constexpr (RULE::has_children) { if constexpr (tnt::is_clearable_v) @@ -481,7 +504,7 @@ auto read_item(BUF& buf, ITEM& item) item.resize(val); } else if constexpr (std::is_enum_v) { item = static_cast(val); - } else if constexpr (tnt::is_optional_v && FAMILY == compact::MP_NIL) { + } else if constexpr (tnt::is_optional_v && FAMILY == MP_NIL) { item.reset(); } else { item = val; @@ -580,7 +603,7 @@ struct Jumps { } }; -template bool jump_common(BUF& buf, T... t); @@ -592,7 +615,7 @@ bool broken_msgpack_jump(BUF&, T...); template struct JumpsBuilder { - template + template static constexpr auto build() { using RULE = rule_by_family_t; @@ -609,13 +632,13 @@ struct JumpsBuilder { } } - template + template static constexpr auto build(tnt::iseq) { return (build() + ...); } - template + template static constexpr auto build(family_sequence s) { if constexpr (s.size() == 0) @@ -665,11 +688,11 @@ struct JumpsBuilder { constexpr bool has_str = (is_str_key>() || ...); if constexpr (has_int && has_str) - return family_sequence{}; + return family_sequence{}; else if constexpr (has_int) - return family_sequence{}; + return family_sequence{}; else if constexpr (has_str) - return family_sequence{}; + return family_sequence{}; else return family_sequence<>{}; } @@ -697,7 +720,7 @@ struct JumpsBuilder { } else if constexpr (path_item_type(LAST) == PIT_DYN_KEY) { using R = decltype(path_resolve(PATH{}, std::declval()...)); - using DST = std::remove_reference_t; + using DST = unwrap_t; static_assert(tnt::is_tuplish_v); constexpr size_t S = tnt::tuple_size_v; if constexpr (tnt::is_tuplish_of_pairish_v) { @@ -743,7 +766,7 @@ decode_jump(BUF& buf, T... t) { static_assert(path_item_type(PATH::last()) != PIT_BAD); static constexpr auto jumps = JumpsBuilder::build(); - uint8_t tag = buf.template get(); + uint8_t tag = rd(buf).template get(); return jumps.data[tag](buf, t...); } @@ -782,8 +805,8 @@ decode_impl(BUF& buf, T... t) static_assert(path_item_type(PATH::last()) != PIT_BAD); if constexpr (path_item_type(PATH::last()) != PIT_DYN_SKIP && path_item_type(PATH::last()) != PIT_RAW) { - auto&& wrapped_dst = path_resolve(PATH{}, t...); - using wrapped_dst_t = std::remove_reference_t; + using wrapped_dst_t = std::remove_reference_t< + decltype(path_resolve(PATH{}, t...))>; if constexpr (is_raw_decoded_v) { return decode_raw(buf, t...); } else { @@ -912,7 +935,7 @@ bool decode_next(BUF& buf, T... t) } } -template bool jump_skip(BUF& buf, T... t) { @@ -922,10 +945,10 @@ bool jump_skip(BUF& buf, T... t) if constexpr (RULE::has_ext) { int8_t ext_type; - buf.read(ext_type); + rd(buf).read(ext_type); } if constexpr (RULE::has_data) { - buf.read({size_t(val)}); + rd(buf).read({size_t(val)}); } if constexpr (RULE::has_children) { auto& arg = std::get(std::tie(t...)); @@ -934,7 +957,7 @@ bool jump_skip(BUF& buf, T... t) return decode_next(buf, t...); } -template bool jump_add(BUF& buf, T... t) { @@ -981,7 +1004,7 @@ constexpr size_t get_next_arr_static_size() return 0; } -template bool jump_read(BUF& buf, T... t) { @@ -994,7 +1017,7 @@ bool jump_read(BUF& buf, T... t) return decode_next(buf, t...); } - if constexpr (FAMILY == compact::MP_ARR) { + if constexpr (FAMILY == MP_ARR) { constexpr auto NT = get_next_arr_item_type(); constexpr size_t NS = get_next_arr_static_size(); using NEXT_PATH = path_push_t; @@ -1028,7 +1051,7 @@ bool jump_read(BUF& buf, T... t) uint64_t arg = val; return decode_impl(buf, t..., arg); } - } else if constexpr (FAMILY == compact::MP_MAP) { + } else if constexpr (FAMILY == MP_MAP) { if constexpr (tnt::is_tuplish_v) { uint64_t arg = val; using NEXT_PATH = path_push_t; @@ -1072,14 +1095,14 @@ constexpr bool signed_compare(K k, V v) } } -template +template constexpr bool compare_key([[maybe_unused]] K k, W&& w, [[maybe_unused]] BUF& buf) { auto&& u = mpp::unwrap(w); using U = mpp::unwrap_t; static_assert(std::is_integral_v); static_assert(!std::is_same_v); - if constexpr (FAMILY == compact::MP_INT) { + if constexpr (FAMILY == MP_INT) { if constexpr (tnt::is_integral_constant_v) { using V = typename U::value_type; constexpr tnt::base_enum_t v = U::value; @@ -1091,7 +1114,7 @@ constexpr bool compare_key([[maybe_unused]] K k, W&& w, [[maybe_unused]] BUF& bu return false; } } else { - static_assert(FAMILY == compact::MP_STR); + static_assert(FAMILY == MP_STR); if constexpr (tnt::is_string_constant_v) { return k == u.size && buf.startsWith({u.data, u.size}); } else if constexpr (tnt::is_char_ptr_v) { @@ -1109,14 +1132,14 @@ constexpr bool compare_key([[maybe_unused]] K k, W&& w, [[maybe_unused]] BUF& bu } } -template bool jump_find_key([[maybe_unused]] K k, tnt::iseq<>, BUF& buf, T... t) { static_assert(path_item_type(PATH::last()) == PIT_DYN_KEY); using NEXT_PATH = path_push_t; - if constexpr (FAMILY == compact::MP_STR) - buf.read({k}); + if constexpr (FAMILY == MP_STR) + rd(buf).read({k}); return decode_impl(buf, t..., size_t(1)); } @@ -1124,12 +1147,12 @@ template auto&& key_path_resolve(DST&& dst) { if constexpr (PAIRS) - return tnt::get<0>(tnt::get(dst)); + return tnt::get<0>(tnt::get(unwrap(dst))); else - return tnt::get(dst); + return tnt::get(unwrap(dst)); } -template bool jump_find_key(K k, tnt::iseq, BUF& buf, T... t) { @@ -1142,8 +1165,8 @@ bool jump_find_key(K k, tnt::iseq, BUF& buf, T... t) using NEXT_PATH = std::conditional_t; if (compare_key(k, key, buf)) { - if constexpr (FAMILY == compact::MP_STR) - buf.read({k}); + if constexpr (FAMILY == MP_STR) + rd(buf).read({k}); return decode_impl(buf, t...); } @@ -1151,13 +1174,13 @@ bool jump_find_key(K k, tnt::iseq, BUF& buf, T... t) return jump_find_key(k, IS, buf, t...); } -template bool jump_read_key(BUF& buf, T... t) { static_assert(path_item_type(PATH::last()) == PIT_DYN_KEY); auto&& dst = path_resolve(PATH{}, t...); - using dst_t = std::remove_reference_t; + using dst_t = unwrap_t; static_assert(tnt::is_tuplish_v); constexpr bool PAIRS = tnt::is_tuplish_of_pairish_v; @@ -1166,24 +1189,24 @@ bool jump_read_key(BUF& buf, T... t) constexpr size_t S = PAIRS ? TS : TS / 2; constexpr auto IS = tnt::make_iseq{}; - static_assert(FAMILY == compact::MP_INT || FAMILY == compact::MP_STR); + static_assert(FAMILY == MP_INT || FAMILY == MP_STR); auto val = read_value(buf); return jump_find_key(val, IS, buf, t...); } -template +template constexpr bool is_object_readable_by_value_v = rule_by_family_t::is_readable_by_value && std::is_assignable_v(std::declval()))>; -template bool jump_read_optional(BUF& buf, T... t) { auto&& dst = unwrap(path_resolve(PATH{}, t...)); using dst_t = std::remove_reference_t; static_assert(tnt::is_optional_v); - if constexpr (FAMILY == compact::MP_NIL) { + if constexpr (FAMILY == MP_NIL) { [[maybe_unused]] auto val = read_value(buf); dst.reset(); return decode_next(buf, t...); @@ -1200,7 +1223,7 @@ bool jump_read_optional(BUF& buf, T... t) } } -template bool jump_read_variant_impl(BUF& buf, T... t) { @@ -1221,7 +1244,7 @@ bool jump_read_variant_impl(BUF& buf, T... t) } } -template bool jump_read_variant(BUF& buf, T... t) { @@ -1236,7 +1259,7 @@ bool jump_read_variant(BUF& buf, T... t) } } -template bool jump_common(BUF& buf, T... t) { @@ -1274,7 +1297,7 @@ bool broken_msgpack_jump(BUF&, T...) template bool -decode(BUF& buf, T&&... t) +decode(BUF&& buf, T&&... t) { // TODO: Guard bool res = decode_details::decode(buf, std::forward(t)...); diff --git a/src/mpp/Enc.hpp b/src/mpp/Enc.hpp index eaf732cbc..846dc3330 100644 --- a/src/mpp/Enc.hpp +++ b/src/mpp/Enc.hpp @@ -38,6 +38,7 @@ #include "BSwap.hpp" #include "ClassRule.hpp" +#include "ContAdapter.hpp" #include "Constants.hpp" #include "Rules.hpp" #include "Spec.hpp" @@ -50,10 +51,6 @@ //TODO : error handling + names //TODO : min + max (detect max for string literal and static capacity) //TODO : avoid reinterpret_cast -//TODO : add raw(N) -//TODO : add std::variant -//TODO : add std::optional -//TODO : universal buffer namespace mpp { @@ -68,7 +65,7 @@ namespace encode_details { struct Nothing {}; template -constexpr compact::Family detectFamily() +constexpr Family detectFamily() { using fixed_t = get_fixed_t; constexpr bool is_non_void_fixed = !std::is_same_v; @@ -77,33 +74,33 @@ constexpr compact::Family detectFamily() if constexpr (is_wrapped_family_v) { return T::family; } else if constexpr (std::is_convertible_v) { - return compact::MP_NIL; + return MP_NIL; } else if constexpr (std::is_same_v) { - return compact::MP_BOOL; + return MP_BOOL; } else if constexpr (tnt::is_integer_v) { - return compact::MP_INT; + return MP_INT; } else if constexpr (std::is_floating_point_v) { - return compact::MP_FLT; + return MP_FLT; } else if constexpr (tnt::is_string_constant_v) { - return compact::MP_STR; + return MP_STR; } else if constexpr (tnt::is_char_ptr_v) { - return compact::MP_STR; + return MP_STR; } else if constexpr (tnt::is_contiguous_char_v) { - return compact::MP_STR; + return MP_STR; } else if constexpr (tnt::is_const_pairs_iterable_v) { - return compact::MP_MAP; + return MP_MAP; } else if constexpr (tnt::is_const_iterable_v) { - return compact::MP_ARR; + return MP_ARR; } else if constexpr (tnt::is_tuplish_of_pairish_v) { if constexpr(tnt::tuple_size_v == 0) - return compact::MP_ARR; + return MP_ARR; else - return compact::MP_MAP; + return MP_MAP; } else if constexpr (tnt::is_tuplish_v) { - return compact::MP_ARR; + return MP_ARR; } else { static_assert(tnt::always_false_v, "Failed to recognise type"); - return compact::MP_END; + return MP_END; } } @@ -146,10 +143,10 @@ auto uniSize32([[maybe_unused]]const U& u) } } -template +template auto getValue([[maybe_unused]] const T& t, [[maybe_unused]] const U& u) { - if constexpr (FAMILY == compact::MP_STR) { + if constexpr (FAMILY == MP_STR) { static_assert(tnt::is_char_ptr_v || tnt::is_string_constant_v || tnt::is_contiguous_char_v); @@ -162,8 +159,8 @@ auto getValue([[maybe_unused]] const T& t, [[maybe_unused]] const U& u) tnt::is_contiguous_char_v); return uniLength32(u); } - } else if constexpr (FAMILY == compact::MP_BIN || - FAMILY == compact::MP_EXT) { + } else if constexpr (FAMILY == MP_BIN || + FAMILY == MP_EXT) { if constexpr(tnt::is_string_constant_v || tnt::is_contiguous_v) { return uniLength32(u); @@ -171,9 +168,9 @@ auto getValue([[maybe_unused]] const T& t, [[maybe_unused]] const U& u) static_assert(std::is_standard_layout_v); return std::integral_constant{}; } - } else if constexpr (FAMILY == compact::MP_ARR) { + } else if constexpr (FAMILY == MP_ARR) { return uniSize32(u); - } else if constexpr (FAMILY == compact::MP_MAP) { + } else if constexpr (FAMILY == MP_MAP) { if constexpr(tnt::is_tuplish_of_pairish_v || tnt::is_const_pairs_iterable_v) { return uniSize32(u); @@ -191,10 +188,10 @@ auto getValue([[maybe_unused]] const T& t, [[maybe_unused]] const U& u) } } -template +template auto getExtType([[maybe_unused]] const T& t, [[maybe_unused]] const U& u) { - if constexpr (FAMILY == compact::MP_EXT) { + if constexpr (FAMILY == MP_EXT) { using E = std::remove_cv_t; if constexpr(tnt::is_integral_constant_v) { using V = std::remove_cv_t; @@ -234,11 +231,11 @@ template struct ChildrenPairs : ChildrenPairsTag { explicit ChildrenPairs(const V& u) : v(u) {} }; -template +template auto getData([[maybe_unused]] const T& t, [[maybe_unused]] const U& u, [[maybe_unused]] const V& value) { - if constexpr (FAMILY == compact::MP_STR) { + if constexpr (FAMILY == MP_STR) { if constexpr (tnt::is_char_ptr_v || tnt::is_bounded_array_v) { using check0_t = decltype(u[0]); @@ -258,8 +255,8 @@ auto getData([[maybe_unused]] const T& t, [[maybe_unused]] const U& u, return std::string_view{std::data(u), std::size(u)}; } - } else if constexpr (FAMILY == compact::MP_BIN || - FAMILY == compact::MP_EXT) { + } else if constexpr (FAMILY == MP_BIN || + FAMILY == MP_EXT) { if constexpr(tnt::is_string_constant_v) { return u; } else if constexpr(tnt::is_contiguous_v) { @@ -270,9 +267,9 @@ auto getData([[maybe_unused]] const T& t, [[maybe_unused]] const U& u, auto p = reinterpret_cast(&u); return std::string_view{p, value}; } - } else if constexpr (FAMILY == compact::MP_ARR) { + } else if constexpr (FAMILY == MP_ARR) { return Children{u}; - } else if constexpr (FAMILY == compact::MP_MAP) { + } else if constexpr (FAMILY == MP_MAP) { if constexpr(tnt::is_tuplish_of_pairish_v || tnt::is_const_pairs_iterable_v) return ChildrenPairs{u}; @@ -283,10 +280,10 @@ auto getData([[maybe_unused]] const T& t, [[maybe_unused]] const U& u, } } -template +template auto getIS([[maybe_unused]] const T& t, [[maybe_unused]] const U& u) { - if constexpr (FAMILY == compact::MP_ARR || FAMILY == compact::MP_MAP) { + if constexpr (FAMILY == MP_ARR || FAMILY == MP_MAP) { if constexpr(tnt::is_tuplish_v) { return tnt::tuple_iseq{}; } else { @@ -357,8 +354,8 @@ constexpr bool can_encode_simple() template constexpr auto getTagValSimple([[maybe_unused]] V value) { - if constexpr(RULE::family == compact::MP_NIL || - RULE::family == compact::MP_IGNR) { + if constexpr(RULE::family == MP_NIL || + RULE::family == MP_IGNR) { // That type is completely independent on value static_assert(!IS_FIXED || std::is_same_v); constexpr char t = static_cast(RULE::simplex_tag); @@ -503,7 +500,7 @@ bool encode(CONT &cont, tnt::CStr prefix, tnt::iseq) { static_assert(sizeof...(I) == 0); - cont.write(prefix); + wr(cont).write(prefix); return true; } @@ -642,19 +639,19 @@ encode(CONT &cont, tnt::CStr prefix, } else if constexpr(tnt::is_string_constant_v) { return encode(cont, prefix.join(u), ais, more...); } else if constexpr(tnt::is_contiguous_v) { - cont.write(prefix); - cont.write({std::data(u), std::size(u)}); + wr(cont).write(prefix); + wr(cont).write({std::data(u), std::size(u)}); return encode(cont, tnt::CStr<>{}, ais, more...); } else { static_assert(std::is_standard_layout_v); - cont.write(prefix); - cont.write(u); + wr(cont).write(prefix); + wr(cont).write(u); return encode(cont, tnt::CStr<>{}, ais, more...); } } else { constexpr bool is_fixed = is_wrapped_fixed_v; using fixed_t = get_fixed_t; - constexpr compact::Family family = detectFamily(); + constexpr Family family = detectFamily(); using rule_t = rule_by_family_t; auto value = getValue(t, u); using V = decltype(value); @@ -677,8 +674,8 @@ encode(CONT &cont, tnt::CStr prefix, size_t soff = find_simplex_offset(value); if (soff < rule_t::simplex_value_range.count) { uint8_t tag = rule_t::simplex_tag + soff; - cont.write(prefix); - cont.write(tag); + wr(cont).write(prefix); + wr(cont).write(tag); return encode(cont, tnt::CStr<>{}, is, as_raw(ext), as_raw(data), more...); @@ -706,7 +703,7 @@ encode(CONT &cont, tnt::CStr prefix, template bool -encode(CONT &cont, const T&... t) +encode(CONT &&cont, const T&... t) { // TODO: Guard tnt::iseq<> is; diff --git a/src/mpp/Rules.hpp b/src/mpp/Rules.hpp index f3a55f601..0db252214 100644 --- a/src/mpp/Rules.hpp +++ b/src/mpp/Rules.hpp @@ -108,55 +108,55 @@ struct RuleRange { count(last - first + 1) {} }; -template +template struct BaseRule { // Widest types that can represent the value. using types = std::tuple; // Msgpack family. - static constexpr compact::Family family = FAMILY; + static constexpr Family family = FAMILY; // The rule stores bool values. - static constexpr bool is_bool = FAMILY == compact::MP_BOOL; + static constexpr bool is_bool = FAMILY == MP_BOOL; // The rule stores floating point values. - static constexpr bool is_floating_point = FAMILY == compact::MP_FLT; + static constexpr bool is_floating_point = FAMILY == MP_FLT; // The rule actually doest not store value, only type matters. - static constexpr bool is_valueless = FAMILY == compact::MP_NIL || - FAMILY == compact::MP_IGNR; + static constexpr bool is_valueless = FAMILY == MP_NIL || + FAMILY == MP_IGNR; // The encoded object has data section (of size equal to value). - static constexpr bool has_data = FAMILY == compact::MP_STR || - FAMILY == compact::MP_BIN || - FAMILY == compact::MP_EXT; + static constexpr bool has_data = FAMILY == MP_STR || + FAMILY == MP_BIN || + FAMILY == MP_EXT; // The encoded object has ext type byte. - static constexpr bool has_ext = FAMILY == compact::MP_EXT; + static constexpr bool has_ext = FAMILY == MP_EXT; // The encoded object has children... - static constexpr bool has_children = FAMILY == compact::MP_ARR || - FAMILY == compact::MP_MAP; + static constexpr bool has_children = FAMILY == MP_ARR || + FAMILY == MP_MAP; // ... and the number of children is value * children_multiplier. static constexpr uint32_t children_multiplier = - FAMILY == compact::MP_ARR ? 1 : - FAMILY == compact::MP_MAP ? 2 : 0; + FAMILY == MP_ARR ? 1 : + FAMILY == MP_MAP ? 2 : 0; // The encoded object can be read by value. static constexpr bool is_readable_by_value = !has_data && !has_ext && !has_children; // The rule has simplex form. - static constexpr bool has_simplex = FAMILY == compact::MP_NIL || - FAMILY == compact::MP_IGNR || - FAMILY == compact::MP_BOOL || - FAMILY == compact::MP_INT || - FAMILY == compact::MP_STR || - FAMILY == compact::MP_ARR || - FAMILY == compact::MP_MAP || - FAMILY == compact::MP_EXT; + static constexpr bool has_simplex = FAMILY == MP_NIL || + FAMILY == MP_IGNR || + FAMILY == MP_BOOL || + FAMILY == MP_INT || + FAMILY == MP_STR || + FAMILY == MP_ARR || + FAMILY == MP_MAP || + FAMILY == MP_EXT; // The rule has simplex form. - static constexpr bool has_complex = FAMILY == compact::MP_INT || - FAMILY == compact::MP_FLT || - FAMILY == compact::MP_STR || - FAMILY == compact::MP_BIN || - FAMILY == compact::MP_ARR || - FAMILY == compact::MP_MAP || - FAMILY == compact::MP_EXT; + static constexpr bool has_complex = FAMILY == MP_INT || + FAMILY == MP_FLT || + FAMILY == MP_STR || + FAMILY == MP_BIN || + FAMILY == MP_ARR || + FAMILY == MP_MAP || + FAMILY == MP_EXT; // The rule has signed simplex range. - static constexpr bool is_simplex_signed = FAMILY == compact::MP_INT; + static constexpr bool is_simplex_signed = FAMILY == MP_INT; // The rule has logarithmic simplex range. - static constexpr bool is_simplex_log_range = FAMILY == compact::MP_EXT; + static constexpr bool is_simplex_log_range = FAMILY == MP_EXT; // The type of simplex value in simplex range. using simplex_value_t = std::conditional_t; @@ -164,22 +164,22 @@ struct BaseRule { using simplex_value_range_t = RuleRange; }; -struct NilRule : BaseRule { +struct NilRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {0, 0}; static constexpr uint8_t simplex_tag = 0xc0; }; -struct IgnrRule : BaseRule { +struct IgnrRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {0, 0}; static constexpr uint8_t simplex_tag = 0xc1; }; -struct BoolRule : BaseRule { +struct BoolRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {0, 1}; static constexpr uint8_t simplex_tag = 0xc2; }; -struct IntRule : BaseRule { +struct IntRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {-32, 127}; static constexpr uint8_t simplex_tag = 0x00; using complex_types = std::tuple { static constexpr uint8_t complex_tag = 0xcc; }; -struct FltRule : BaseRule { +struct FltRule : BaseRule { using complex_types = std::tuple; static constexpr uint8_t complex_tag = 0xca; }; -struct StrRule : BaseRule { +struct StrRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {0, 31}; static constexpr uint8_t simplex_tag = 0xa0; using complex_types = std::tuple; static constexpr uint8_t complex_tag = 0xd9; }; -struct BinRule : BaseRule { +struct BinRule : BaseRule { using complex_types = std::tuple; static constexpr uint8_t complex_tag = 0xc4; }; -struct ArrRule : BaseRule { +struct ArrRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {0, 15}; static constexpr uint8_t simplex_tag = 0x90; using complex_types = std::tuple; static constexpr uint8_t complex_tag = 0xdc; }; -struct MapRule : BaseRule { +struct MapRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {0, 15}; static constexpr uint8_t simplex_tag = 0x80; using complex_types = std::tuple; static constexpr uint8_t complex_tag = 0xde; }; -struct ExtRule : BaseRule { +struct ExtRule : BaseRule { static constexpr simplex_value_range_t simplex_value_range = {0, 4}; static constexpr uint8_t simplex_tag = 0xd4; using complex_types = std::tuple; @@ -229,9 +229,9 @@ using all_rules_t = std::tuple; /** - * Find a rule by compact::Family. + * Find a rule by Family. */ -template +template using rule_by_family_t = std::tuple_element_t; /** @@ -397,7 +397,7 @@ constexpr size_t rule_complex_apply(E eval, F &&f) static_assert(RULE::has_complex); auto val = details::rule_unenum(eval); using V = decltype(val); - constexpr bool is_int = RULE::family == compact::MP_INT; + constexpr bool is_int = RULE::family == MP_INT; constexpr size_t N = rule_complex_count_v; if constexpr (is_int && std::is_signed_v) { static_assert(N == 8); diff --git a/src/mpp/Spec.hpp b/src/mpp/Spec.hpp index 82edf624e..b730c56d2 100644 --- a/src/mpp/Spec.hpp +++ b/src/mpp/Spec.hpp @@ -45,18 +45,18 @@ namespace mpp { * use temporary specificators, like encoder.add(mpp::as_map()). */ -template +template constexpr auto as_family(T&& t); -template constexpr auto as_nil (T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_ignr(T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_bool(T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_int (T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_flt (T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_str (T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_bin (T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_arr (T&& t) { return as_family(std::forward(t)); } -template constexpr auto as_map (T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_nil (T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_ignr(T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_bool(T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_int (T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_flt (T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_str (T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_bin (T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_arr (T&& t) { return as_family(std::forward(t)); } +template constexpr auto as_map (T&& t) { return as_family(std::forward(t)); } template constexpr auto as_ext(EXT_TYPE&& e, T&& t); @@ -97,20 +97,20 @@ struct wrapped : wrapped_tag constexpr explicit wrapped(T&& arg) : object(std::forward(arg)) {} }; -template +template struct wrapped_family : wrapped_family_tag, BASE { - static constexpr compact::Family family = FAMILY; + static constexpr Family family = FAMILY; constexpr explicit wrapped_family(BASE&& arg) : BASE(std::forward(arg)) {} }; -template +template constexpr auto as_family(T&& t) { static_assert(!is_wrapped_family_v, "Family is already set"); static_assert(!is_wrapped_raw_v, "Incompatible with raw"); - static_assert(FAMILY < compact::MP_END, "Invalid family"); - static_assert(FAMILY != compact::MP_EXT, "Please use as_ext"); + static_assert(FAMILY < MP_END, "Invalid family"); + static_assert(FAMILY != MP_EXT, "Please use as_ext"); if constexpr(is_wrapped_v) { using WRAP_T = std::remove_reference_t; @@ -123,7 +123,7 @@ constexpr auto as_family(T&& t) template struct wrapped_ext : wrapped_family_tag, BASE { - static constexpr compact::Family family = compact::MP_EXT; + static constexpr Family family = MP_EXT; EXT_TYPE ext_type; constexpr wrapped_ext(EXT_TYPE e, BASE&& arg) : BASE(std::forward(arg)), ext_type(e) {} diff --git a/test/EncDecGPerfTest.cpp b/test/EncDecGPerfTest.cpp index 09c0db1e4..257d5120c 100644 --- a/test/EncDecGPerfTest.cpp +++ b/test/EncDecGPerfTest.cpp @@ -103,6 +103,10 @@ struct SimpleRData { size_t size; }; +struct SimpleSkip { + size_t size; +}; + struct SimpleReader { const char *pos; @@ -111,19 +115,15 @@ struct SimpleReader { memcpy(data.data, pos, data.size); pos += data.size; } - template - void read(T &t) + void read(SimpleSkip data) { - memcpy(&t, pos, sizeof(t)); - pos += sizeof(t); + pos += data.size; } template - T read() + void read(T &t) { - T t; memcpy(&t, pos, sizeof(t)); pos += sizeof(t); - return t; } template T get() diff --git a/test/EncDecTest.cpp b/test/EncDecTest.cpp index fb9694426..ebbcbb431 100644 --- a/test/EncDecTest.cpp +++ b/test/EncDecTest.cpp @@ -164,20 +164,11 @@ test_type_visual() { TEST_INIT(0); using namespace mpp; - std::cout << compact::MP_ARR << " " - << compact::MP_MAP << " " - << compact::MP_EXT << "\n"; - std::cout << MP_NIL << " " - << MP_BOOL << " " - << (MP_INT) << " " - << (MP_BIN | MP_STR) << " " - << (MP_INT | MP_FLT) << "\n"; + std::cout << MP_ARR << " " << MP_MAP << " " << MP_EXT << "\n"; std::cout << "(" << family_sequence<>{} << ") " - << "(" << family_sequence{} << ") " - << "(" << family_sequence{} << ")\n"; + << "(" << family_sequence{} << ") " + << "(" << family_sequence{} << ")\n"; } enum E1 { @@ -230,6 +221,7 @@ test_basic() mpp::encode(buf, std::integral_constant{}); mpp::encode(buf, std::integral_constant{}); // Strings. + mpp::encode(buf, ""); mpp::encode(buf, "abc"); const char *bbb = "defg"; mpp::encode(buf, bbb); @@ -322,7 +314,9 @@ test_basic() fail_if(bt != true); // Strings. - std::string a; + std::string a = "nothing"; + fail_unless(mpp::decode(run, a)); + fail_if(a != ""); char b_data[10]; size_t b_size = 0; auto b = tnt::make_ref_vector(b_data, b_size); @@ -775,14 +769,16 @@ struct Triplet { c = 3 * i + 3; } + auto tie() const { return std::tie(a, b, c); } + bool operator==(const Triplet& that) const { - return std::tie(a, b, c) == std::tie(that.a, that.b, that.c); + return tie() == that.tie(); } bool operator<(const Triplet& that) const { - return std::tie(a, b, c) < std::tie(that.a, that.b, that.c); + return tie() < that.tie(); } }; @@ -803,26 +799,37 @@ struct Error { descr = std::to_string(code); } - bool operator==(const Error& that) const - { - return std::tie(code, descr) == std::tie(that.code, that.descr); - } - - bool operator<(const Error& that) const - { - return std::tie(code, descr) < std::tie(that.code, that.descr); - } + auto tie() const { return std::tie(code, descr); } + bool operator==(const Error &that) const { return tie() == that.tie(); } + bool operator<(const Error &that) const { return tie() < that.tie(); } static constexpr auto mpp = std::make_tuple( std::make_pair(0, &Error::code), std::make_pair(1, &Error::descr)); }; +struct Legs { + void gen() + { + left = 31; + right = 32; + } + int left; + int right; + + auto tie() const { return std::tie(left, right); } + bool operator==(const Legs &that) const { return tie() == that.tie(); } + bool operator<(const Legs &that) const { return tie() < that.tie(); } + + static constexpr auto mpp = std::make_tuple(&Legs::left, &Legs::right); +}; + struct Body { std::string str; IntegerWrapper num; std::vector triplets; std::vector errors; + Legs legs; void gen() { @@ -834,25 +841,19 @@ struct Body { errors.resize(3); for (size_t i = 0; i < errors.size(); i++) errors[i].gen(i); + legs.gen(); } - bool operator==(const Body& that) const - { - return std::tie(str, num, triplets, errors) == - std::tie(that.str, that.num, that.triplets, that.errors); - } - - bool operator<(const Body& that) const - { - return std::tie(str, num, triplets, errors) < - std::tie(that.str, that.num, that.triplets, that.errors); - } + auto tie() const { return std::tie(str, num, triplets, errors, legs); } + bool operator==(const Body &that) const { return tie() == that.tie(); } + bool operator<(const Body &that) const { return tie() < that.tie(); } static constexpr auto mpp = std::make_tuple( std::make_pair(0, &Body::str), std::make_pair(1, &Body::num), std::make_pair(2, &Body::triplets), - std::make_pair(3, &Body::errors)); + std::make_pair(3, &Body::errors), + std::make_pair(4, &Body::legs)); }; void @@ -1329,6 +1330,39 @@ test_variant() fail_unless(monostate_wr == monostate_rd); } +void +test_cont_adapter() +{ + { + std::vector vec; + mpp::encode(vec, 10, "abc", std::forward_as_tuple(false, 1.)); + int res1 = 0; + std::string res2; + bool res3 = true; + double res4 = 2.; + mpp::decode(vec.data(), res1, res2, std::tie(res3, res4)); + fail_unless(res1 == 10); + fail_unless(res2 == "abc"); + fail_unless(res3 == false); + fail_unless(res4 == 1.); + } + { + char buf[16]; + char *p = buf; + mpp::encode(p, 10, "abc", std::forward_as_tuple(false, 1.)); + int res1 = 0; + std::string res2; + bool res3 = true; + double res4 = 2.; + p = buf; + mpp::decode(p, res1, res2, std::tie(res3, res4)); + fail_unless(res1 == 10); + fail_unless(res2 == "abc"); + fail_unless(res3 == false); + fail_unless(res4 == 1.); + } +} + int main() { test_under_ints(); @@ -1340,4 +1374,5 @@ int main() test_optional(); test_raw(); test_variant(); + test_cont_adapter(); } diff --git a/test/RulesUnitTest.cpp b/test/RulesUnitTest.cpp index 3c0d2aded..db83bf24f 100644 --- a/test/RulesUnitTest.cpp +++ b/test/RulesUnitTest.cpp @@ -37,7 +37,7 @@ #include "Utils/Helpers.hpp" namespace test { -using fis_t = std::make_index_sequence; +using fis_t = std::make_index_sequence; template struct has_simplex : std::false_type {}; @@ -86,7 +86,7 @@ constexpr bool rule_has_tag_v = template constexpr auto rule_by_tag_h(std::index_sequence) { - constexpr mpp::compact::Family family{FAMILY}; + constexpr mpp::Family family{FAMILY}; using rule_t = mpp::rule_by_family_t; if constexpr (rule_has_tag_v) { return rule_t{}; @@ -101,7 +101,7 @@ using rule_by_tag_t = decltype(rule_by_tag_h(test::fis_t{})); } // namespace test -template +template void check_family_rule() { @@ -112,14 +112,14 @@ check_family_rule() if constexpr (Rule_t::has_simplex) static_assert(Rule_t::simplex_value_range.first == 0 || (Rule_t::simplex_value_range.first < 0 && - FAMILY == mpp::compact::MP_INT)); + FAMILY == mpp::MP_INT)); } template void check_family_rule(std::index_sequence) { - (check_family_rule(FAMILY)>(), ...); + (check_family_rule(FAMILY)>(), ...); } void @@ -134,8 +134,8 @@ test_basic() // Some selective checks. static_assert(std::is_same_v, BoolRule::types>); static_assert(std::is_same_v, IntRule::types>); - static_assert(NilRule::family == compact::MP_NIL); - static_assert(BinRule::family == compact::MP_BIN); + static_assert(NilRule::family == MP_NIL); + static_assert(BinRule::family == MP_BIN); static_assert(IgnrRule::has_data == false); static_assert(StrRule::has_data == true); static_assert(BinRule::has_data == true); @@ -281,7 +281,7 @@ template constexpr std::array> tuple_sizes = tuple_sizes_h(std::make_index_sequence>{}); -template +template void collectByType(FullInfo& infos) { using Rule = mpp::rule_by_family_t; @@ -323,7 +323,7 @@ template FullInfo collectByType(std::index_sequence) { FullInfo infos; - (collectByType(FAMILY)>(infos), ...); + (collectByType(FAMILY)>(infos), ...); return infos; }