diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index c72f8fe..6977152 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -772,6 +772,132 @@ struct AbiNestedWriterMgrListConstLen : }; // struct AbiNestedWriterMgrListConstLen +/** + * @brief This is one of the classes used to manage the nested writers for a + * type that has nested types; + * for example, T[] types. + * And this class is used to manage the nested writers for + * (T1, T2, ..., Tn) types, which have multiple + * nested type (T1, T2, ..., Tn). + * + * @tparam _ItemWriters A series of nested writers for the nested types + */ +template +struct AbiNestedWriterMgrTuple; + + +template<> +struct AbiNestedWriterMgrTuple<> +{ + AbiNestedWriterMgrTuple() : + m_size(0), + m_isDynamic(false), // tuple with no nested types is static type + m_totalNumHeadChunks(0) + {} + + ~AbiNestedWriterMgrTuple() = default; + + bool IsDynamicType() const + { + return m_isDynamic; + } + + size_t GetTotalNumHeadChunks() const + { + return m_totalNumHeadChunks; + } + + template + void IterateVals(_Func, _ValIt begin, _ValIt end) const + { + if (begin != end) + { + throw Exception( + "ABI writer - too many given data than declared" + ); + } + } + + template + void ApplyVals(_Func&&) const + {} + + size_t m_size; + bool m_isDynamic; + size_t m_totalNumHeadChunks; + +}; // struct AbiNestedWriterMgrTuple<> + + +template +struct AbiNestedWriterMgrTuple<_ItemWriter, _ItemWriters...> +{ + using ItemWriter = _ItemWriter; + + AbiNestedWriterMgrTuple(ItemWriter&& itemWriter, _ItemWriters&&... itemWriters) : + m_size(1 + sizeof...(_ItemWriters)), + m_itemWriter(std::forward(itemWriter)), + m_next(std::forward<_ItemWriters>(itemWriters)...), + m_isDynamic(m_itemWriter.IsDynamicType() || m_next.IsDynamicType()), + m_totalNumHeadChunks( + m_itemWriter.GetNumHeadChunks() + m_next.GetTotalNumHeadChunks() + ) + {} + + ~AbiNestedWriterMgrTuple() = default; + + bool IsDynamicType() const + { + return m_isDynamic; + } + + /** + * @brief Get the total number of head chunks of nested types, + * NO MATTER IF THIS TYPE IS DYNAMIC OR STATIC + * + * @return The total number of head chunks + */ + size_t GetTotalNumHeadChunks() const + { + return m_totalNumHeadChunks; + } + + template + void IterateVals(_Func func, _ValIt begin, _ValIt end) const + { + if (begin == end) + { + throw Exception( + "ABI writer - the number of given data is less than declared" + ); + } + func(m_itemWriter, *begin); + m_next.IterateVals(func, std::next(begin), end); + } + + template + void ApplyVals(_Func&& func, _ValT&& val, _ValTs&&... vals) const + { + static_assert( + sizeof...(_ValTs) == sizeof...(_ItemWriters), + "ABI writer - the number of given data does not match the declared" + ); + func(m_itemWriter, std::forward<_ValT>(val)); + m_next.ApplyVals( + std::forward<_Func>(func), + std::forward<_ValTs>(vals)... + ); + } + + size_t m_size; + ItemWriter m_itemWriter; + AbiNestedWriterMgrTuple<_ItemWriters...> m_next; + bool m_isDynamic; + size_t m_totalNumHeadChunks; + +}; // struct AbiNestedWriterMgrTuple<_ItemWriter, _ItemWriters...> + + template struct AbiWriterNestedTypeDynLenImpl { @@ -875,8 +1001,11 @@ struct AbiWriterNestedTypeDynLenImpl template void operator()(const _Writer& writer, const _ValType& val) { - std::tie(m_destIt, m_dataOffset) = - writer.WriteHead(m_destIt, val, m_dataOffset); + std::tie(m_destIt, m_dataOffset) = writer.WriteHead( + m_destIt, + val, + m_dataOffset + ); } _DestIt& m_destIt; @@ -1015,29 +1144,120 @@ struct AbiWriterNestedTypeConstLenImpl : }; // struct AbiWriterNestedTypeConstLenImpl -template -struct AbiWriterNestedListConstLenImpl : +template +struct AbiWriterNestedTypeTupleImpl : public AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr> { - using NestedWriterMgr = _NestedWriterMgr; using Base = AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr>; + using Self = AbiWriterNestedTypeTupleImpl<_NestedWriterMgr>; + + using Base::Base; + + using NumTailChunksFunctor = typename Base::NumTailChunksFunctor; + template + using WriteHeadsFunctor = typename Base::template WriteHeadsFunctor<_DestIt>; + template + using WriteTailsFunctor = typename Base::template WriteTailsFunctor<_DestIt>; + + template + size_t GetNumTailChunksTuple(_ValTs&& ...vals) const + { + if (!Base::IsDynamicType()) + { + // static type - no tail chunks + return 0; + } + else + { + // dynamic type - the tail is + // sub_head_1, sub_head_2, ..., sub_head_n, + // sub_tail_1, sub_tail_2, ..., sub_tail_n + size_t numTailChunks = + (Base::m_nestedWriterMgr).GetTotalNumHeadChunks(); + + (Base::m_nestedWriterMgr).ApplyVals( + NumTailChunksFunctor(numTailChunks), + std::forward<_ValTs>(vals)... + ); + + return numTailChunks; + } + } + + template + _DestIt WriteTuple(_DestIt destIt, _ValTs&& ...vals) const + { + // calculate the offset of the data area + size_t dataOffset = + (Base::m_nestedWriterMgr).GetTotalNumHeadChunks() * + AbiCodecConst::sk_chunkSize(); + + + // write all head chunks + (Base::m_nestedWriterMgr).ApplyVals( + WriteHeadsFunctor<_DestIt>(destIt, dataOffset), + std::forward<_ValTs>(vals)... + ); + + // write all tail chunks + (Base::m_nestedWriterMgr).ApplyVals( + WriteTailsFunctor<_DestIt>(destIt), + std::forward<_ValTs>(vals)... + ); + + return destIt; + } +}; // struct AbiWriterNestedTypeTupleImpl + + +template class _BaseCls, class _NestedWriterMgr> +struct AbiWriterNestedConstLenImpl : + public _BaseCls<_NestedWriterMgr> +{ + using NestedWriterMgr = _NestedWriterMgr; + using Base = _BaseCls<_NestedWriterMgr>; using Base::Base; - ~AbiWriterNestedListConstLenImpl() = default; + ~AbiWriterNestedConstLenImpl() = default; + + size_t GetNumTailChunks(const Internal::Obj::ListBaseObj& val) const + { + return Base::GetNumTailChunks(val.begin(), val.end()); + } size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::GetNumTailChunks(list.begin(), list.end()); + return GetNumTailChunks(val.AsList()); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::ListBaseObj& val) const + { + return Base::Write(destIt, val.begin(), val.end()); } template _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::Write(destIt, list.begin(), list.end()); + return WriteObj(destIt, val.AsList()); } +}; // struct AbiWriterNestedConstLenImpl + + +template +struct AbiWriterNestedListConstLenImpl : + public AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeConstLenImpl, + _NestedWriterMgr + > +{ + using Base = AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeConstLenImpl, + _NestedWriterMgr + >; + + using Base::Base; }; // struct AbiWriterNestedListConstLenImpl @@ -1052,29 +1272,97 @@ struct AbiWriterNestedListDynLenImpl : ~AbiWriterNestedListDynLenImpl() = default; + size_t GetNumTailChunks(const Internal::Obj::ListBaseObj& val) const + { + return Base::GetNumTailChunks(val.size(), val.begin(), val.end()); + } + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::GetNumTailChunks(list.size(), list.begin(), list.end()); + return GetNumTailChunks(val.AsList()); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::ListBaseObj& val) const + { + return Base::Write(destIt, val.size(), val.begin(), val.end()); } template _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::Write(destIt, list.size(), list.begin(), list.end()); + return WriteObj(destIt, val.AsList()); } }; // struct AbiWriterNestedListDynLenImpl -// ========== -// AbiWriterImpl for (T1, T2, Tn) types, where T is static type -// ========== +template +struct AbiWriterNestedTupleImpl : + public AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeTupleImpl, + _NestedWriterMgr + > +{ + using NestedWriterMgr = _NestedWriterMgr; + using Base = AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeTupleImpl, + _NestedWriterMgr + >; + using Base::Base; -// ========== -// AbiWriterImpl for (T1, T2, Tn) types, where T is dynamic type -// ========== + ~AbiWriterNestedTupleImpl() = default; + + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const + { + return Base::GetNumTailChunks(val.AsList()); + } + + template + size_t GetNumTailChunks( + const _TupleType& vals, + Internal::Obj::SizeSeq<_Idx...> + ) const + { + return Base::GetNumTailChunksTuple(std::get<_Idx - 1>(vals)...); + } + + template + size_t GetNumTailChunks(const std::tuple<_ValTs...>& vals) const + { + return GetNumTailChunks( + vals, + Internal::Obj::IncrSizeSeq() + ); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + return Base::WriteObj(destIt, val.AsList()); + } + + template + _DestIt Write( + _DestIt destIt, + const _TupleType& vals, + Internal::Obj::SizeSeq<_Idx...> + ) const + { + return Base::WriteTuple(destIt, std::get<_Idx - 1>(vals)...); + } + + template + _DestIt Write(_DestIt destIt, const std::tuple<_ValTs...>& vals) const + { + return Write( + destIt, + vals, + Internal::Obj::IncrSizeSeq() + ); + } + +}; // struct AbiWriterNestedTupleImpl } // namespace EthInternal @@ -1145,17 +1433,17 @@ class AbiWriterHeadOnlyBase : public AbiWriterBase virtual ~AbiWriterHeadOnlyBase() = default; - bool IsDynamicType() const override + virtual bool IsDynamicType() const override { return m_impl.IsDynamicType(); } - size_t GetNumHeadChunks() const override + virtual size_t GetNumHeadChunks() const override { return m_impl.GetNumHeadChunks(); } - size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const override + virtual size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const override { return 0; } @@ -1229,17 +1517,17 @@ class AbiWriterHeadTailBase : public AbiWriterBase virtual ~AbiWriterHeadTailBase() = default; - bool IsDynamicType() const override + virtual bool IsDynamicType() const override { return m_impl.IsDynamicType(); } - size_t GetNumHeadChunks() const override + virtual size_t GetNumHeadChunks() const override { return m_impl.GetNumHeadChunks(); } - size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const override + virtual size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const override { return m_impl.GetNumTailChunks(val); } @@ -1546,7 +1834,7 @@ struct AbiWriter< virtual ~AbiWriter() = default; // LCOV_EXCL_STOP -}; // struct AbiParser +}; // struct AbiWriter template @@ -1626,8 +1914,166 @@ struct AbiWriter< virtual ~AbiWriter() = default; // LCOV_EXCL_STOP -}; // struct AbiParser +}; // struct AbiWriter + + +template +struct AbiWriterListDynLen : + public AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::true_type + > +{ + using Base = AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::true_type + >; + using Self = AbiWriterListDynLen<_ItemWriter>; + + AbiWriterListDynLen() : + Base(_ItemWriter()) + {} +}; // struct AbiWriterListDynLen + + +// ========== +// AbiWriter for (T1, T2, ..., Tn) types +// ========== + + +template +struct AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, // Are inner types specified at runtime? - false + _ItemWriters... +> : + public AbiWriterHeadTailBase< + EthInternal::AbiWriterNestedTupleImpl< + EthInternal::AbiNestedWriterMgrTuple<_ItemWriters...> + > + > +{ + using NestedWritersMgr = + EthInternal::AbiNestedWriterMgrTuple<_ItemWriters...>; + using WriterImpl = EthInternal::AbiWriterNestedTupleImpl< + NestedWritersMgr + >; + using Base = AbiWriterHeadTailBase; + using Self = AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, + _ItemWriters... + >; + + using WriteIterator = typename Base::WriteIterator; + AbiWriter(_ItemWriters&& ...itemWriters) : + Base( + WriterImpl( + NestedWritersMgr(std::forward<_ItemWriters>(itemWriters)...) + ) + ) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + + using Base::GetNumTailChunks; + + template + size_t GetNumTailChunks(const std::tuple<_ValTs...>& vals) const + { + return (Base::m_impl).GetNumTailChunks(vals); + } + + using Base::WriteHead; + + template + std::tuple< + WriteIterator, + size_t + > + WriteHead( + WriteIterator destIt, + const std::tuple<_ValTs...>& vals, + size_t currDataOffset + ) const + { + if (!Base::IsDynamicType()) + { + // static type - head only + return std::make_tuple( + (Base::m_impl).Write(destIt, vals), + currDataOffset + ); + } + else + { + // dynamic type - head is a uint representing the data offset + auto headIt = (Base::m_headWriter).Write( + destIt, + static_cast(currDataOffset) + ); + + // calculate the new data offset + currDataOffset += ( + (Base::m_impl).GetNumTailChunks(vals) * + AbiCodecConst::sk_chunkSize() + ); + + return std::make_tuple(headIt, currDataOffset); + } + } + + using Base::WriteTail; + + template + WriteIterator WriteTail( + WriteIterator destIt, + const std::tuple<_ValTs...>& vals + ) const + { + if (!Base::IsDynamicType()) + { + // static type - no tail chunks + return destIt; + } + else + { + // dynamic type - write the tail chunks + return (Base::m_impl).Write(destIt, vals); + } + } + +}; // struct AbiWriter + + +template +struct AbiWriterStaticTuple : + public AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, + _ItemWriters... + > +{ + using Base = AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, + _ItemWriters... + >; + using Self = AbiWriterStaticTuple<_ItemWriters...>; + + AbiWriterStaticTuple() : + Base(_ItemWriters()...) + {} + + // LCOV_EXCL_START + virtual ~AbiWriterStaticTuple() = default; + // LCOV_EXCL_STOP +}; // struct AbiWriterStaticTuple } // namespace Eth diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index b5ba5f5..7e3f75d 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -78,10 +78,10 @@ static const std::vector gsk_testObjWriterHead = { }; -template +template static void TestObjWriter( const _Writer& writer, - const SimpleObjects::BaseObj& obj, + const _ObjT& obj, const std::vector& expHeadOutput, const std::vector& expTailOutput, size_t expDataOffsetIncr = 0 @@ -967,3 +967,452 @@ GTEST_TEST(TestEthAbiWriter, WriteDynListDynLen) } } + +GTEST_TEST(TestEthAbiWriter, WriteStaticTuple) +{ + { + using AbiTupleWriter = AbiWriter< + SimpleObjects::ObjCategory::StaticDict, + std::false_type, + AbiWriter, + AbiWriter > + >; + + AbiTupleWriter writer( + ( AbiWriter() ), + ( AbiWriter >() ) + ); + + EXPECT_EQ(writer.IsDynamicType(), false); + EXPECT_EQ(writer.GetNumHeadChunks(), 2); + + { + std::vector expOutput = { + // item 1 + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x12U, 0x34U, 0x56U, 0x78U, 0x90U, 0xabU, 0xcdU, 0xefU, + // item 2 + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + SimpleObjects::UInt64(0x1234567890ABCDEFULL), + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}) + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 0); + TestObjWriter( + writer, + inVal, + expOutput, + {} + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); + auto inValRef = std::tie(inVal1Ref, inVal2Ref); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); + TestObjWriter( + writer, + inValRef, + expOutput, + {} + ); + } + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteDynamicTuple) +{ + { + using AbiTupleWriter = AbiWriter< + SimpleObjects::ObjCategory::StaticDict, + std::false_type, + AbiWriter, + AbiWriter + >; + + AbiTupleWriter writer( + ( AbiWriter() ), + ( AbiWriter() ) + ); + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + { + std::vector expOutput = { + // item 1 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x80U, + // data area + // item 1 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 1 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // item 2 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x09U, + // item 2 - bytes + 0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + SimpleObjects::Bytes({0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}) + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 6); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); + auto inValRef = std::tie(inVal1Ref, inVal2Ref); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + } + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) +{ + SimpleObjects::UInt64 val1(12345); + SimpleObjects::UInt64 val2(54321); + SimpleObjects::UInt64 val3(67890); + SimpleObjects::Bytes val4({ 0x01U, 0x02U, 0x03U, 0x04U, 0x05U }); + + // ('uint64', 'uint64[]', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterListDynLen< + AbiWriter + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x60U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // data area + // list length + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U, + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, + }; + auto inVal = std::make_tuple( + val1, + SimpleObjects::ListT({val2, val3}), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 6); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inValRef = std::tie(inVal1Ref, inVal2Ref, inVal3Ref); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + } + + // ('uint64', '(uint64,uint64)', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), false); + EXPECT_EQ(writer.GetNumHeadChunks(), 4); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + val1, + std::make_tuple(val2, val3), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 0); + TestObjWriter( + writer, + inVal, + expOutput, + {} + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); + auto inValRef = std::tie( + inVal1Ref, + inVal2Ref, + inVal3Ref + ); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); + TestObjWriter( + writer, + inValRef, + expOutput, + {} + ); + } + + // ('uint64', '(uint64,bytes)', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x60U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // data area + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2.2 - length of bytes + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 2.2 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + val1, + std::make_tuple(val2, val4), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 7); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 7 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); + auto inValRef = std::tie( + inVal1Ref, + inVal2Ref, + inVal3Ref + ); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 7); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 7 * AbiCodecConst::sk_chunkSize() + ); + } + + // ('uint64', '(uint64,(uint64,bytes))', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + > + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x60U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // data area 1 + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // data area 2.2 + // item 2.2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, + // item 2.2.2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2.2.2 - length of bytes + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 2.2.2 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + val1, + std::make_tuple( + val2, + std::make_tuple(val3, val4) + ), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 9); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 9 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal221Ref = std::get<0>(std::get<1>(std::get<1>(inVal))); + const SimpleObjects::BaseObj& inVal222Ref = std::get<1>(std::get<1>(std::get<1>(inVal))); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inVal22Ref = std::tie(inVal221Ref, inVal222Ref); + auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); + auto inValRef = std::tie( + inVal1Ref, + inVal2Ref, + inVal3Ref + ); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 9); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 9 * AbiCodecConst::sk_chunkSize() + ); + } +} +