This commit is contained in:
Eddy Ashton 2020-02-19 09:15:20 +00:00 коммит произвёл GitHub
Родитель 12951f37d3
Коммит 73b090fe6b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 497 добавлений и 458 удалений

Просмотреть файл

@ -39,6 +39,8 @@ namespace eevm
template <typename... Ts>
ByteString encode(Ts&&... ts);
namespace encode_details
{
void prefix_multiple_length(size_t total_length, ByteString& bs);
inline ByteString to_byte_string(const ByteString& bs)
@ -163,7 +165,9 @@ namespace eevm
total_length_as_bytes.insert(
total_length_as_bytes.begin(), 0xf7 + length_of_total_length);
bs.insert(
bs.begin(), total_length_as_bytes.begin(), total_length_as_bytes.end());
bs.begin(),
total_length_as_bytes.begin(),
total_length_as_bytes.end());
}
// RLP-encode a single, non-tuple argument. Convert it to a ByteString,
@ -200,7 +204,8 @@ namespace eevm
auto length_as_bytes = to_byte_string(length);
const uint8_t length_of_length = length_as_bytes.size();
length_as_bytes.insert(length_as_bytes.begin(), 0xb7 + length_of_length);
length_as_bytes.insert(
length_as_bytes.begin(), 0xb7 + length_of_length);
bytes.insert(
bytes.begin(), length_as_bytes.begin(), length_as_bytes.end());
return bytes;
@ -223,7 +228,9 @@ namespace eevm
auto encode_multiple(const std::tuple<Ts...>& tup)
{
return std::apply(
[](auto&&... entry) { return std::make_tuple(encode(entry)...); }, tup);
[](auto&&... entry) { return std::make_tuple(encode(entry)...); },
tup);
}
}
// Main encode function. Either forwards to encode_single, or calls
@ -234,10 +241,11 @@ namespace eevm
{
if constexpr (sizeof...(Ts) == 1 && !is_tuple<std::decay_t<Ts>...>::value)
{
return encode_single(std::forward<Ts>(ts)...);
return encode_details::encode_single(std::forward<Ts>(ts)...);
}
const auto nested_terms = encode_multiple(std::forward<Ts>(ts)...);
const auto nested_terms =
encode_details::encode_multiple(std::forward<Ts>(ts)...);
// Get total length by summing size of each term in tuple
const auto total_length = std::apply(
@ -252,7 +260,7 @@ namespace eevm
},
nested_terms);
prefix_multiple_length(total_length, flattened);
encode_details::prefix_multiple_length(total_length, flattened);
return flattened;
}
@ -260,6 +268,13 @@ namespace eevm
// Decoding
//
class decode_error : public std::logic_error
{
using logic_error::logic_error;
};
namespace decode_details
{
enum class Arity
{
Single,
@ -273,14 +288,9 @@ namespace eevm
size_t length;
};
class decode_error : public std::logic_error
{
using logic_error::logic_error;
};
// Forward declaration to allow recursive calls.
template <typename... Ts>
std::tuple<Ts...> decode(const uint8_t*&, size_t&);
std::tuple<Ts...> decode_impl(const uint8_t*&, size_t&);
inline std::pair<Arity, size_t> decode_length(
const uint8_t*& data, size_t& size);
@ -295,9 +305,9 @@ namespace eevm
{
if (size > 8)
{
throw decode_error(
"Trying to decode number: " + std::to_string(size) +
" is too many bytes for uint64_t");
throw decode_error(fmt::format(
"Trying to decode number: {} is too many bytes for uint64_t",
size));
}
size_t result = 0;
@ -349,10 +359,10 @@ namespace eevm
{
if (size != N)
{
throw decode_error(
"Trying to decode " + std::to_string(N) +
" byte array, but given " + std::to_string(size) +
" bytes to decode");
throw decode_error(fmt::format(
"Trying to decode {} byte array, but given {} bytes to decode",
N,
size));
}
std::array<uint8_t, N> result;
@ -375,7 +385,7 @@ namespace eevm
std::array<T, N> result;
for (auto i = 0u; i < N; ++i)
{
result[i] = std::get<0>(decode<T>(data, size));
result[i] = std::get<0>(decode_impl<T>(data, size));
}
size = 0u;
@ -394,7 +404,8 @@ namespace eevm
std::vector<T> result;
while (contained_length > 0)
{
result.push_back(std::get<0>(decode<T>(data, contained_length)));
result.push_back(
std::get<0>(decode_impl<T>(data, contained_length)));
}
size = 0u;
@ -472,10 +483,11 @@ namespace eevm
if (size < length_of_length)
{
throw decode_error(
"Length of next element should be encoded in " +
std::to_string(length_of_length) + " bytes, but only " +
std::to_string(size) + " remain");
throw decode_error(fmt::format(
"Length of next element should be encoded in {} bytes, but only "
"{} remain",
length_of_length,
size));
}
size -= length_of_length;
@ -497,10 +509,11 @@ namespace eevm
if (size < length_of_length)
{
throw decode_error(
"Length of next list should be encoded in " +
std::to_string(length_of_length) + " bytes, but only " +
std::to_string(size) + " remain");
throw decode_error(fmt::format(
"Length of next list should be encoded in {} bytes, but only {} "
"remain",
length_of_length,
size));
}
size -= length_of_length;
@ -530,15 +543,15 @@ namespace eevm
inline std::tuple<T, Ts...> decode_tuple(
const uint8_t*& data, size_t& size, std::tuple<T, Ts...>)
{
const auto first = decode<T>(data, size);
const auto first = decode_impl<T>(data, size);
return std::tuple_cat(
first, decode_tuple<Ts...>(data, size, std::tuple<Ts...>{}));
}
// Type helper for decoding single item, forwarding to either decode_tuple
// (to unwrap the types contained in a tuple) or directly to the main
// decode function
// (to unwrap the types contained in a tuple) or directly to the top-level
// decode_impl function
template <typename T>
auto decode_item(const uint8_t*& data, size_t& size)
{
@ -548,13 +561,12 @@ namespace eevm
}
else
{
return decode<T>(data, size);
return decode_impl<T>(data, size);
}
}
// Type helper for decoding single item, forwarding to either decode_tuple
// (to unwrap the types contained in a tuple) or directly to the main
// decode function
// Type helper for decoding multiple items, decoding the first and then
// potentially recursing to concatenate the decoded tail
template <typename T, typename... Ts>
std::tuple<T, Ts...> decode_multiple(const uint8_t*& data, size_t& size)
{
@ -570,14 +582,15 @@ namespace eevm
}
}
// Main decode function. Reads initial length, then either converts a single
// item from remaining bytes or forwards to decode_multiple
// Main decode_impl function. Reads initial length, then either converts a
// single item from remaining bytes or forwards to decode_multiple
template <typename... Ts>
std::tuple<Ts...> decode(const uint8_t*& data, size_t& size)
std::tuple<Ts...> decode_impl(const uint8_t*& data, size_t& size)
{
auto [arity, contained_length] = decode_length(data, size);
if constexpr (sizeof...(Ts) == 1 && !is_tuple<std::decay_t<Ts>...>::value)
if constexpr (
sizeof...(Ts) == 1 && !is_tuple<std::decay_t<Ts>...>::value)
{
if (arity != Arity::Single)
{
@ -585,6 +598,7 @@ namespace eevm
}
size -= contained_length;
return std::make_tuple(from_bytes<Ts...>{}(data, contained_length));
}
@ -598,9 +612,9 @@ namespace eevm
{
if (contained_length != 0)
{
throw decode_error(
"Expected empty list, but data contains " +
std::to_string(contained_length) + " remaining bytes");
throw decode_error(fmt::format(
"Expected empty list, but data contains {} remaining bytes",
contained_length));
}
return std::make_tuple();
@ -608,11 +622,29 @@ namespace eevm
else
{
size -= contained_length;
return decode_multiple<Ts...>(data, contained_length);
}
}
}
// Helper. Takes ByteString and forwards to contained data+size to main
// Core helper. Forwards to decode_impl, ensures entire input has been
// consumed
template <typename... Ts>
std::tuple<Ts...> decode(const uint8_t*& data, size_t& size)
{
auto res = decode_details::decode_impl<Ts...>(data, size);
if (size != 0)
{
throw decode_error(fmt::format(
"Expected to decode entire input, but {} bytes remain", size));
}
return res;
}
// Helper. Takes ByteString and forwards contained data+size to main
// decode function
template <typename... Ts>
std::tuple<Ts...> decode(const ByteString& bytes)

Просмотреть файл

@ -20,11 +20,15 @@ const auto large_input_decoded = std::make_tuple(
66000u),
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua"s);
const auto large_input_encoded = rlp::to_byte_string(
const std::string large_input_encoded_s =
"\xf8\xa5\xda\x8bHello world\x8dSaluton "
"Mondo\xcd\xc8\xc1\x01\xc2\x02\x03\xc2\xc1\x04\x83\x01\x01\xd0\xb8zLorem "
"ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua");
"tempor incididunt ut labore et dolore magna aliqua";
const auto large_input_encoded = std::vector<uint8_t>(
large_input_encoded_s.begin(), large_input_encoded_s.end());
TEST_CASE("encode" * doctest::test_suite("rlp"))
{
@ -83,6 +87,7 @@ TEST_CASE("decode" * doctest::test_suite("rlp"))
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x1}) == 0x1);
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x7f}) == 0x7f);
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x81, 0x80}) == 0x80);
CHECK_THROWS(rlp::decode_single<size_t>(rlp::ByteString{0x81, 0x80, 0x00}));
CHECK(rlp::decode<>(rlp::ByteString{0xc0}) == std::make_tuple());
CHECK(rlp::decode<std::string>(rlp::ByteString{0x80}) == std::make_tuple(""));
@ -123,6 +128,8 @@ TEST_CASE("decode" * doctest::test_suite("rlp"))
rlp::decode<std::tuple<std::tuple<size_t>>>(
rlp::ByteString{0xc2, 0xc1, 0x80}) ==
std::make_tuple(std::make_tuple(std::make_tuple(0x0))));
CHECK_THROWS(rlp::decode<std::tuple<std::tuple<size_t>>>(
rlp::ByteString{0xc2, 0xc1, 0x80, 0x00}));
CHECK(
rlp::decode_single<