Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to intx 0.8.0 endian facilities #615

Merged
merged 4 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmake/Hunter/config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ hunter_config(

hunter_config(
intx
VERSION 0.7.1
URL https://github.com/chfast/intx/archive/v0.7.1.tar.gz
SHA1 75f0d5e05df9245c7abc684dae2f4a99764c4ba8
VERSION 0.8.0
URL https://github.com/chfast/intx/archive/v0.8.0.tar.gz
SHA1 612c46d636d9e381a8288d96c70b132190a79ca8
)

hunter_config(
Expand Down
120 changes: 12 additions & 108 deletions core/silkworm/common/endian.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,10 @@
/*
Facilities to deal with byte order/endianness
See https://en.wikipedia.org/wiki/Endianness

The following macros are defined:
SILKWORM_LITTLE_ENDIAN
SILKWORM_BIG_ENDIAN
SILKWORM_BYTE_ORDER

SILKWORM_BYTE_ORDER is equal to SILKWORM_BIG_ENDIAN for big-endian architectures
and to SILKWORM_LITTLE_ENDIAN for little-endian ones (most current architectures).
*/

#include <cstdint>
#include <cstring>
#include <optional>

#ifdef _WIN32

// On Windows assume little endian
#define SILKWORM_LITTLE_ENDIAN 1234
#define SILKWORM_BIG_ENDIAN 4321
#define SILKWORM_BYTE_ORDER SILKWORM_LITTLE_ENDIAN

#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)

// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
#define SILKWORM_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
#define SILKWORM_BIG_ENDIAN __ORDER_BIG_ENDIAN__
#define SILKWORM_BYTE_ORDER __BYTE_ORDER__

#else
#error "endianness detection failure"
#endif

#include <intx/intx.hpp>

Expand All @@ -59,107 +32,38 @@ and to SILKWORM_LITTLE_ENDIAN for little-endian ones (most current architectures

namespace silkworm::endian {

#if SILKWORM_BYTE_ORDER == SILKWORM_LITTLE_ENDIAN
namespace le {
template <class UnsignedInteger>
static inline UnsignedInteger load(UnsignedInteger value) noexcept {
return value;
}
} // namespace le
namespace be {
static inline uint16_t load(uint16_t value) noexcept { return intx::bswap(value); }
static inline uint32_t load(uint32_t value) noexcept { return intx::bswap(value); }
static inline uint64_t load(uint64_t value) noexcept { return intx::bswap(value); }

template <unsigned N>
static inline intx::uint<N> load(const intx::uint<N>& value) noexcept {
return intx::bswap(value);
}
} // namespace be

#elif SILKWORM_BYTE_ORDER == SILKWORM_BIG_ENDIAN
namespace le {
static inline uint16_t load(uint16_t value) noexcept { return intx::bswap(value); }
static inline uint32_t load(uint32_t value) noexcept { return intx::bswap(value); }
static inline uint64_t load(uint64_t value) noexcept { return intx::bswap(value); }
// intx::uint not defined here since its words are little-endian.
// In any case, Silkworm is currently untested on big-endian plaforms.
} // namespace le
namespace be {
static inline uint16_t load(uint16_t value) noexcept { return value; }
static inline uint32_t load(uint32_t value) noexcept { return value; }
static inline uint64_t load(uint64_t value) noexcept { return value; }
} // namespace be

#else
#error "byte order not supported"
#endif

// Similar to boost::endian::load_big_u16
inline uint16_t load_big_u16(const uint8_t* bytes) noexcept {
uint16_t x;
std::memcpy(&x, bytes, sizeof(x));
return be::load(x);
}
const auto load_big_u16 = intx::be::unsafe::load<uint16_t>;

// Similar to boost::endian::load_big_u32
inline uint32_t load_big_u32(const uint8_t* bytes) noexcept {
uint32_t x;
std::memcpy(&x, bytes, sizeof(x));
return be::load(x);
}
const auto load_big_u32 = intx::be::unsafe::load<uint32_t>;

// Similar to boost::endian::load_big_u64
inline uint64_t load_big_u64(const uint8_t* bytes) noexcept {
uint64_t x;
std::memcpy(&x, bytes, sizeof(x));
return be::load(x);
}
const auto load_big_u64 = intx::be::unsafe::load<uint64_t>;

// Similar to boost::endian::load_little_u16
inline uint16_t load_little_u16(const uint8_t* bytes) noexcept {
uint16_t x;
std::memcpy(&x, bytes, sizeof(x));
return le::load(x);
}
const auto load_little_u16 = intx::le::unsafe::load<uint16_t>;

// Similar to boost::endian::load_little_u32
inline uint32_t load_little_u32(const uint8_t* bytes) noexcept {
uint32_t x;
std::memcpy(&x, bytes, sizeof(x));
return le::load(x);
}
const auto load_little_u32 = intx::le::unsafe::load<uint32_t>;

// Similar to boost::endian::load_little_u64
inline uint64_t load_little_u64(const uint8_t* bytes) noexcept {
uint64_t x;
std::memcpy(&x, bytes, sizeof(x));
return le::load(x);
}
const auto load_little_u64 = intx::le::unsafe::load<uint64_t>;

// Similar to boost::endian::store_big_u16
inline void store_big_u16(uint8_t* bytes, const uint16_t value) {
uint16_t x{be::load(value)};
std::memcpy(bytes, &x, sizeof(x));
}
const auto store_big_u16 = intx::be::unsafe::store<uint16_t>;

// Similar to boost::endian::store_big_u32
inline void store_big_u32(uint8_t* bytes, const uint32_t value) {
uint32_t x{be::load(value)};
std::memcpy(bytes, &x, sizeof(x));
}
const auto store_big_u32 = intx::be::unsafe::store<uint32_t>;

// Similar to boost::endian::store_big_u64
inline void store_big_u64(uint8_t* bytes, const uint64_t value) {
uint64_t x{be::load(value)};
std::memcpy(bytes, &x, sizeof(x));
}
const auto store_big_u64 = intx::be::unsafe::store<uint64_t>;

//! \brief Transforms a uint64_t stored in memory with native endianness to it's compacted big endian byte form
//! \param [in] value : the value to be transformed
//! \return A ByteView (std::string_view) into an internal static buffer (thread specific) of the function
//! \remarks each function call overwrites the buffer, therefore invalidating a previously returned result
//! \remarks so each returnd ByteView must be used immediately (before a further call to the same function).
//! \remarks so each returned ByteView must be used immediately (before a further call to the same function).
//! \remarks See Erigon TxIndex value
//! \remarks A "compact" big endian form strips leftmost bytes valued to zero
ByteView to_big_compact(uint64_t value);
Expand All @@ -168,7 +72,7 @@ ByteView to_big_compact(uint64_t value);
//! \param [in] value : the value to be transformed
//! \return A ByteView (std::string_view) into an internal static buffer (thread specific) of the function
//! \remarks each function call overwrites the buffer, therefore invalidating a previously returned result
//! \remarks so each returnd ByteView must be used immediately (before a further call to the same function)
//! \remarks so each returned ByteView must be used immediately (before a further call to the same function)
//! \remarks See Erigon TxIndex value
//! \remarks A "compact" big endian form strips leftmost bytes valued to zero
ByteView to_big_compact(const intx::uint256& value);
Expand Down Expand Up @@ -198,7 +102,7 @@ static DecodingResult from_big_compact(ByteView data, UnsignedInteger& out) {
auto* ptr{reinterpret_cast<uint8_t*>(&out)};
std::memcpy(ptr + (sizeof(UnsignedInteger) - data.length()), &data[0], data.length());

out = be::load(out);
out = intx::to_big_endian(out);
return DecodingResult::kOk;
}

Expand Down
26 changes: 13 additions & 13 deletions core/silkworm/common/endian_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,26 +94,26 @@ TEST_CASE("Block as key and compact form") {
// Check the sequence of bytes in memory
ByteView block_number_view(reinterpret_cast<uint8_t*>(&block_number), sizeof(uint64_t));

#if SILKWORM_BYTE_ORDER == SILKWORM_LITTLE_ENDIAN
// Check we've switched to native endianness
CHECK(to_hex(block_number_view) == block_number_hex_rev);
#else
// Check our hex form matches input form
CHECK(to_hex(block_number_view) == block_number_hex);
#endif
if constexpr (intx::byte_order_is_little_endian) {
// Check we've switched to native endianness
CHECK(to_hex(block_number_view) == block_number_hex_rev);
} else {
// Check our hex form matches input form
CHECK(to_hex(block_number_view) == block_number_hex);
}

alignas(uint64_t) uint8_t block_number_as_key[8];
store_big_u64(&block_number_as_key[0], block_number);

// Check data value is byte swapped if endianness requires
auto block_number_from_key{*reinterpret_cast<uint64_t*>(block_number_as_key)};

#if SILKWORM_BYTE_ORDER == SILKWORM_LITTLE_ENDIAN
CHECK(block_number_from_key != block_number);
#else
CHECK(block_number_from_key == block_number);
#endif
CHECK(be::load(block_number_from_key) == block_number);
if constexpr (intx::byte_order_is_little_endian) {
CHECK(block_number_from_key != block_number);
} else {
CHECK(block_number_from_key == block_number);
}
CHECK(intx::to_big_endian(block_number_from_key) == block_number);
}

SECTION("Block number as compact") {
Expand Down
17 changes: 7 additions & 10 deletions core/silkworm/common/util.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020-2021 The Silkworm Authors
Copyright 2020-2022 The Silkworm Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -23,9 +23,9 @@
#include <vector>

#include <ethash/keccak.hpp>
#include <intx/intx.hpp>

#include <silkworm/common/base.hpp>
#include <silkworm/common/endian.hpp>

namespace silkworm {

Expand Down Expand Up @@ -64,14 +64,11 @@ ByteView zeroless_view(ByteView data);
std::string to_hex(ByteView bytes, bool with_prefix = false);

//! \brief Returns a string representing the hex form of provided integral
template <typename T,
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type* = nullptr>
std::string to_hex(const T& value, bool with_prefix = false) {
Bytes bytes(reinterpret_cast<const uint8_t*>(&value), sizeof(T));
#if SILKWORM_BYTE_ORDER == SILKWORM_LITTLE_ENDIAN
std::reverse(bytes.begin(), bytes.end());
#endif
auto hexed{to_hex(zeroless_view(bytes), with_prefix)};
template <typename T, typename = std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>>>
std::string to_hex(T value, bool with_prefix = false) {
uint8_t bytes[sizeof(T)];
intx::be::store(bytes, value);
std::string hexed{to_hex(zeroless_view(bytes), with_prefix)};
if (hexed.length() == (with_prefix ? 2 : 0)) {
hexed += "00";
}
Expand Down
7 changes: 3 additions & 4 deletions core/silkworm/crypto/snark.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020-2021 The Silkworm Authors
Copyright 2020-2022 The Silkworm Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -20,11 +20,10 @@
#include <cassert>
#include <cstring>

#include <intx/intx.hpp>
#include <libff/algebra/curves/alt_bn128/alt_bn128_pp.hpp>
#include <libff/common/profiling.hpp>

#include <silkworm/common/endian.hpp>

namespace silkworm::snark {

void init_libff() noexcept {
Expand Down Expand Up @@ -132,7 +131,7 @@ Bytes encode_g1_element(libff::alt_bn128_G1 p) noexcept {
auto y{p.Y.as_bigint()};

// Here we convert little-endian data to big-endian output
static_assert(SILKWORM_BYTE_ORDER == SILKWORM_LITTLE_ENDIAN);
static_assert(intx::byte_order_is_little_endian);
static_assert(sizeof(x.data) == 32);

std::memcpy(&out[0], y.data, 32);
Expand Down
4 changes: 2 additions & 2 deletions core/silkworm/execution/precompiled.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020-2021 The Silkworm Authors
Copyright 2020-2022 The Silkworm Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -354,7 +354,7 @@ std::optional<Bytes> blake2_f_run(ByteView input) noexcept {
state.f[0] = std::numeric_limits<uint64_t>::max();
}

static_assert(SILKWORM_BYTE_ORDER == SILKWORM_LITTLE_ENDIAN);
static_assert(intx::byte_order_is_little_endian);
static_assert(sizeof(state.h) == 8 * 8);
std::memcpy(&state.h, input.data() + 4, 8 * 8);

Expand Down
1 change: 1 addition & 0 deletions node/silkworm/db/buffer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <catch2/catch.hpp>
#include <magic_enum.hpp>

#include <silkworm/common/endian.hpp>
#include <silkworm/common/test_context.hpp>
#include <silkworm/db/buffer.hpp>
#include <silkworm/db/tables.hpp>
Expand Down