Skip to content

Commit

Permalink
Merge pull request #107 from chfast/be_api2
Browse files Browse the repository at this point in the history
Rework and extend endian-specific conversion API
  • Loading branch information
chfast authored Aug 20, 2019
2 parents 9312a4c + 5c5982e commit 96afab1
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 24 deletions.
29 changes: 23 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,34 @@ The format is based on [Keep a Changelog],
and this project adheres to [Semantic Versioning].


## [0.4.0] - unreleased

### Added

- Added the `as_bytes()` casting helper.
[[#106](https://github.com/chfast/intx/pull/106)]

### Changed

- The endian-specific API for converting intx types to/from bytes has been reworked.
[[#107](https://github.com/chfast/intx/pull/107)]


## [0.3.0] - 2019-06-20

### Added
- New `addmod()` and `mulmod()` procedures have been added for the `uint256` type
([#101](https://github.com/chfast/intx/pull/101)).
- New `addmod()` and `mulmod()` procedures have been added for the `uint256` type.
[[#101](https://github.com/chfast/intx/pull/101)]

### Changed
- Pedantic compiler warnings have been fixed
([#98](https://github.com/chfast/intx/pull/98)).
- Pedantic compiler warnings have been fixed.
[[#98](https://github.com/chfast/intx/pull/98)]
- Performance of the division algorithm increased up to 40%
when dividing 256-bit values by 128-bit and 64-bit ones
([#99](https://github.com/chfast/intx/pull/99)).
when dividing 256-bit values by 128-bit and 64-bit ones.
[[#99](https://github.com/chfast/intx/pull/99)]


[0.4.0]: https://github.com/chfast/intx/compare/v0.3.0...master
[0.3.0]: https://github.com/chfast/intx/releases/v0.2.0

[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
Expand Down
2 changes: 2 additions & 0 deletions include/intx/int128.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ struct uint;
template <>
struct uint<128>
{
static constexpr unsigned num_bits = 128;

uint64_t lo = 0;
uint64_t hi = 0;

Expand Down
84 changes: 73 additions & 11 deletions include/intx/intx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,18 +969,18 @@ constexpr uint512 operator"" _u512(const char* s) noexcept

namespace le // Conversions to/from LE bytes.
{
template <unsigned N>
inline intx::uint<N> uint(const uint8_t bytes[sizeof(intx::uint<N>)]) noexcept
template <typename IntT, unsigned M>
inline IntT load(const uint8_t (&bytes)[M]) noexcept
{
auto x = intx::uint<N>{};
static_assert(M == IntT::num_bits / 8,
"the size of source bytes must match the size of the destination uint");
auto x = IntT{};
std::memcpy(&x, bytes, sizeof(x));
return x;
}
constexpr auto uint256 = uint<256>;
constexpr auto uint512 = uint<512>;

template <unsigned N>
inline void store(uint8_t* dst, const intx::uint<N>& x) noexcept
inline void store(uint8_t (&dst)[N / 8], const intx::uint<N>& x) noexcept
{
std::memcpy(dst, &x, sizeof(x));
}
Expand All @@ -990,22 +990,84 @@ inline void store(uint8_t* dst, const intx::uint<N>& x) noexcept

namespace be // Conversions to/from BE bytes.
{
/// Loads an uint value from bytes of big-endian order.
/// If the size of bytes is smaller than the result uint, the value is zero-extended.
template <typename IntT, unsigned M>
inline IntT load(const uint8_t (&bytes)[M]) noexcept
{
static_assert(M <= IntT::num_bits / 8,
"the size of source bytes must not exceed the size of the destination uint");
auto x = IntT{};
std::memcpy(&as_bytes(x)[IntT::num_bits / 8 - M], bytes, M);
return bswap(x);
}

template <typename IntT, typename T>
inline IntT load(const T& t) noexcept
{
return load<IntT>(t.bytes);
}

/// Stores an uint value in a bytes array in big-endian order.
template <unsigned N>
inline intx::uint<N> uint(const uint8_t bytes[sizeof(intx::uint<N>)]) noexcept
inline void store(uint8_t (&dst)[N / 8], const intx::uint<N>& x) noexcept
{
const auto d = bswap(x);
std::memcpy(dst, &d, sizeof(d));
}

/// Stores an uint value in .bytes field of type T. The .bytes must be an array of uint8_t
/// of the size matching the size of uint.
template <typename T, unsigned N>
inline T store(const intx::uint<N>& x) noexcept
{
T r{};
store(r.bytes, x);
return r;
}

/// Stores the truncated value of an uint in a bytes array.
/// Only the least significant bytes from big-endian representation of the uint
/// are stored in the result bytes array up to array's size.
template <unsigned M, unsigned N>
inline void trunc(uint8_t (&dst)[M], const intx::uint<N>& x) noexcept
{
static_assert(M < N / 8, "destination must be smaller than the source value");
const auto d = bswap(x);
const auto b = as_bytes(d);
std::memcpy(dst, &b[sizeof(d) - M], M);
}

/// Stores the truncated value of an uint in the .bytes field of an object of type T.
template <typename T, unsigned N>
inline T trunc(const intx::uint<N>& x) noexcept
{
T r{};
trunc(r.bytes, x);
return r;
}

namespace unsafe
{
/// Loads an uint value from a buffer. The user must make sure
/// that the provided buffer is big enough. Therefore marked "unsafe".
template <typename IntT>
inline IntT load(const uint8_t* bytes) noexcept
{
auto x = intx::uint<N>{};
auto x = IntT{};
std::memcpy(&x, bytes, sizeof(x));
return bswap(x);
}
constexpr auto uint256 = uint<256>;
constexpr auto uint512 = uint<512>;

/// Stores an uint value at the provided pointer in big-endian order. The user must make sure
/// that the provided buffer is big enough to fit the value. Therefore marked "unsafe".
template <unsigned N>
inline void store(uint8_t* dst, const intx::uint<N>& x) noexcept
{
auto d = bswap(x);
const auto d = bswap(x);
std::memcpy(dst, &d, sizeof(d));
}
} // namespace unsafe

} // namespace be

Expand Down
84 changes: 82 additions & 2 deletions test/unittests/test_intx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,14 +442,94 @@ TYPED_TEST(uint_test, endianness)
le::store(data, x);
EXPECT_EQ(data[0], 1);
EXPECT_EQ(data[s - 1], 0);
EXPECT_EQ(le::uint<s * 8>(data), x);
EXPECT_EQ(le::load<TypeParam>(data), x);

be::store(data, x);
EXPECT_EQ(data[0], 0);
EXPECT_EQ(data[s - 1], 1);
EXPECT_EQ(be::uint<s * 8>(data), x);
EXPECT_EQ(be::load<TypeParam>(data), x);

be::unsafe::store(data, x);
EXPECT_EQ(data[0], 0);
EXPECT_EQ(data[s - 1], 1);
EXPECT_EQ(be::unsafe::load<TypeParam>(data), x);
}

TYPED_TEST(uint_test, be_zext)
{
uint8_t data[] = {0x01, 0x02, 0x03};
const auto x = be::load<TypeParam>(data);
EXPECT_EQ(x, 0x010203);
}

TYPED_TEST(uint_test, be_load)
{
constexpr auto size = sizeof(TypeParam);
uint8_t data[size]{};
data[0] = 0x80;
data[size - 1] = 1;
const auto x = be::load<TypeParam>(data);
EXPECT_EQ(x, (TypeParam{1} << (TypeParam::num_bits - 1)) | 1);
}

TYPED_TEST(uint_test, be_store)
{
const auto x = TypeParam{0x0201};
uint8_t data[sizeof(x)];
be::store(data, x);
EXPECT_EQ(data[sizeof(x) - 1], 1);
EXPECT_EQ(data[sizeof(x) - 2], 2);
EXPECT_EQ(data[sizeof(x) - 3], 0);
EXPECT_EQ(data[0], 0);
}

TYPED_TEST(uint_test, be_trunc)
{
constexpr auto x = TypeParam{0xee48656c6c6f20536f6c617269732121_u128};
uint8_t out[15];
be::trunc(out, x);
const auto str = std::string{reinterpret_cast<char*>(out), sizeof(out)};
EXPECT_EQ(str, "Hello Solaris!!");
}

template <unsigned M>
struct storage
{
uint8_t bytes[M];
};

TYPED_TEST(uint_test, typed_store)
{
const auto x = TypeParam{2};
const auto s = be::store<storage<sizeof(x)>>(x);
EXPECT_EQ(s.bytes[sizeof(x) - 1], 2);
}

TYPED_TEST(uint_test, typed_trunc)
{
const auto x = TypeParam{0xaabb};
const auto s = be::trunc<storage<9>>(x);
EXPECT_EQ(s.bytes[8], 0xbb);
EXPECT_EQ(s.bytes[7], 0xaa);
EXPECT_EQ(s.bytes[6], 0);
EXPECT_EQ(s.bytes[0], 0);
}

TYPED_TEST(uint_test, typed_load_zext)
{
const auto s = storage<1>({0xed});
const auto x = be::load<TypeParam>(s);
EXPECT_EQ(x, 0xed);
}

TYPED_TEST(uint_test, typed_load)
{
const auto s = storage<sizeof(TypeParam)>({0x88});
const auto x = be::load<TypeParam>(s);
EXPECT_EQ(x, TypeParam{0x88} << (TypeParam::num_bits - 8));
}


TYPED_TEST(uint_test, convert_to_bool)
{
EXPECT_TRUE((TypeParam{1, 0}));
Expand Down
5 changes: 0 additions & 5 deletions test/unittests/test_intx_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@

using namespace intx;

static_assert(&be::uint<256> == be::uint256, "wrong alias: be::uint256");
static_assert(&be::uint<512> == be::uint512, "wrong alias: be::uint512");
static_assert(&le::uint<256> == le::uint256, "wrong alias: le::uint256");
static_assert(&le::uint<512> == le::uint512, "wrong alias: le::uint512");

static_assert(uint128{2} + uint128{2} == 4, "");
static_assert(uint256{2} + uint256{2} == 4, "");
static_assert(uint512{2} + uint512{2} == 4, "");
Expand Down

0 comments on commit 96afab1

Please sign in to comment.