Skip to content
Open

Views #135

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
29 changes: 29 additions & 0 deletions src/bytes/cast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef BDK_BYTES_CAST_H
#define BDK_BYTES_CAST_H

#include "range.h"

namespace bytes {

/**
* Casts a range of bytes from one type to another.
*
* @param src the input bytes to be cast
* @param dest the destiny bytes container
* @return the destiny bytes container
* @throws invalid argument exception on size incompatibility
*/
template<Range R>
constexpr R cast(const Range auto& src, R dest = R()) {
if (std::ranges::size(src) != std::ranges::size(dest)) {
throw std::invalid_argument("incompatible sizes for casting");
}

std::ranges::copy(src, std::ranges::begin(dest));

return dest;
}

} // namespace bytes

#endif // BDK_BYTES_CAST_H
52 changes: 52 additions & 0 deletions src/bytes/hex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef BDK_BYTES_HEX_H
#define BDK_BYTES_HEX_H

#include <string_view>
#include <stdexcept>
#include "range.h"
#include "initializer.h"
#include "utils/hex.h"

namespace bytes {

/**
* Creates a sized initializer from a hex representation. The initializer will
* fill the container data with by converting the hexadecimal representation
* into binary data.
* @param str the hex string
* @return the sized initializer
*/
constexpr SizedInitializer auto hex(std::string_view str) {
if (str.size() >= 2 && str.starts_with("0x")) {
str = str.substr(2);
}

if (str.size() % 2) {
throw std::invalid_argument("the length of hex string is required to be even number");
}

return makeInitializer(str.size() / 2, [str] (Byte* dest) {
const auto value = [] (char c) -> Byte {
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else
throw std::invalid_argument("character '" + std::to_string(c) + "' is invalid in hex string");
};

auto it = str.begin();

while (it != str.end()) {
const Byte l = value(*it++);
const Byte r = value(*it++);
*dest++ = (l << 4) | r;
}
});
}

} // namespace bytes

#endif // BDK_BYTES_HEX_H
79 changes: 42 additions & 37 deletions src/bytes/join.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,48 @@
#include "utils/dynamicexception.h"

namespace bytes {
namespace detail {
std::size_t joinedSize(const SizedInitializer auto& arg) {
return arg.size();
}

std::size_t joinedSize(const DataRange auto& arg) {
return std::ranges::size(arg);
}

std::size_t joinedSize(const auto& arg, const auto&... args) {
return joinedSize(arg) + joinedSize(args...);
}

Byte* joinImpl(Byte *dest, const SizedInitializer auto& init) {
init.to(dest);
return dest + init.size();
}

Byte* joinImpl(Byte *dest, const DataRange auto& range) {
std::memcpy(dest, std::ranges::data(range), std::ranges::size(range));
return dest + std::ranges::size(range);
}

Byte* joinImpl(Byte *dest, const auto& arg, const auto&... args) {
return joinImpl(joinImpl(dest, arg), args...);
}
} // namespace detail

template<typename... Ts> SizedInitializer auto join(Ts&&... args) {
const size_t size = detail::joinedSize(args...);

auto func = [args_ = std::tuple<Ts...>(std::forward<Ts>(args)...)] (Byte *dest) {
std::apply(detail::joinImpl<Ts...>, std::tuple_cat(std::make_tuple(dest), std::tuple<const Ts&...>(args_)));
};

return makeInitializer(size, std::move(func));
}

namespace detail {

std::size_t joinedSize(const SizedInitializer auto& arg) {
return arg.size();
}

std::size_t joinedSize(const DataRange auto& arg) {
return std::ranges::size(arg);
}

std::size_t joinedSize(const auto& arg, const auto&... args) {
return joinedSize(arg) + joinedSize(args...);
}

Byte* joinImpl(Byte *dest, const SizedInitializer auto& init) {
init.to(dest);
return dest + init.size();
}

Byte* joinImpl(Byte *dest, const DataRange auto& range) {
std::memcpy(dest, std::ranges::data(range), std::ranges::size(range));
return dest + std::ranges::size(range);
}

Byte* joinImpl(Byte *dest, const auto& arg, const auto&... args) {
return joinImpl(joinImpl(dest, arg), args...);
}

} // namespace detail

template<typename... Ts>
SizedInitializer auto join(Ts&&... args) {
const size_t size = detail::joinedSize(args...);

auto func = [args_ = std::tuple<Ts...>(std::forward<Ts>(args)...)] (Byte *dest) {
std::apply(detail::joinImpl<Ts...>, std::tuple_cat(std::make_tuple(dest), std::tuple<const Ts&...>(args_)));
};

return makeInitializer(size, std::move(func));
}

} // namespace bytes

#endif // BYTES_JOIN_H
40 changes: 40 additions & 0 deletions src/bytes/random.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef BDK_BYTES_RANDOM_H
#define BDK_BYTES_RANDOM_H

#include "range.h"
#include "initializer.h"

#include <openssl/rand.h>

namespace bytes {

/**
* Creates an initializer of random bytes.
* The number of generated bytes exactly matches the size of the target bytes range.
*
* Examples:
* Hash hash = bytes::random(); // generates 32 random bytes
* Address addr = bytes::random(); // generates 20 random bytes
*
* @return a random bytes initializer
*/
constexpr Initializer auto random() {
return makeInitializer([] (Span span) {
::RAND_bytes(span.data(), span.size());
});
}

/**
* Creates an random bytes initializer of the given size.
*
* @return a sized initializer of random bytes
*/
constexpr SizedInitializer auto random(size_t size) {
return makeInitializer(size, [size] (Byte* ptr) {
::RAND_bytes(ptr, size);
});
}

} // namespace bytes

#endif // BDK_BYTES_RANDOM_H
15 changes: 13 additions & 2 deletions src/bytes/range.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@

#include <cstdint>
#include <ranges>

using Byte = std::uint8_t;
#include "utils/bytes.h"

namespace bytes {
/**
* Concept of a bytes iterator.
*/
template<typename T>
concept Iterator = std::input_or_output_iterator<T> && std::same_as<std::iter_value_t<T>, Byte>;

/**
* Concept of a bytes contiguous bytes iterator.
*/
template<typename T>
concept DataIterator = Iterator<T> && std::contiguous_iterator<T>;

/**
* The concept of a range of bytes.
*/
Expand Down
17 changes: 4 additions & 13 deletions src/bytes/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@

#include <span>
#include "range.h"
#include "utils/view.h"

namespace bytes {

/// A view over sized contiguous bytes
using View = std::span<const Byte>;

/// A span (i.e. a non-owning array of bytes that allows modification) over sized contiguous bytes
using Span = std::span<Byte>;

/**
Expand All @@ -22,7 +19,7 @@ using Span = std::span<Byte>;
* @return a view object of the bytes
*/
template<BorrowedDataRange R>
constexpr View view(R&& r) { return View(std::forward<R>(r)); }
constexpr View<Bytes> view(R&& r) { return View<Bytes>(std::forward<R>(r)); }

/**
* Creates a span from the given data range. It needs to be Borrowed
Expand All @@ -43,8 +40,8 @@ constexpr Span span(R&& r) { return Span(std::forward<R>(r)); }
* @param str the target string
* @return a bytes view of the string bytes
*/
inline View view(std::string_view str) {
return View(reinterpret_cast<const Byte*>(str.data()), str.size());
inline View<Bytes> view(std::string_view str) {
return View<Bytes>(reinterpret_cast<const Byte*>(str.data()), str.size());
}

/**
Expand All @@ -57,12 +54,6 @@ inline Span span(std::string& str) {
return Span(reinterpret_cast<Byte*>(str.data()), str.size());
}

/// The concept of a type that can be viewed as a sized range of contiguous bytes.
template<typename T>
concept Viewable = requires(const T& a) {
{ view(a) } -> std::convertible_to<View>;
};

} // namespace bytes

#endif // BYTES_VIEW_H
4 changes: 2 additions & 2 deletions src/contract/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ Bytes ABI::Encoder::encodeInt(const int256_t& num) {
return ret;
}

uint256_t ABI::Decoder::decodeUint(const bytes::View &bytes, uint64_t &index) {
uint256_t ABI::Decoder::decodeUint(const View<Bytes> &bytes, uint64_t &index) {
if (index + 32 > bytes.size()) throw std::length_error("Data too short for uint256");
uint256_t result = Utils::bytesToUint256(bytes.subspan(index, 32));
index += 32;
return result;
}

int256_t ABI::Decoder::decodeInt(const bytes::View& bytes, uint64_t& index) {
int256_t ABI::Decoder::decodeInt(const View<Bytes>& bytes, uint64_t& index) {
if (index + 32 > bytes.size()) throw std::length_error("Data too short for int256");
int256_t result = Utils::bytesToInt256(bytes.subspan(index, 32));
index += 32;
Expand Down
Loading
Loading