#pragma once #include "bencode.h" #include #include #include #include #include #include #include #include #include #include #include extern "C" { extern void randombytes(unsigned char* const ptr, unsigned long long sz); extern int sodium_is_zero(const unsigned char* n, const size_t nlen); } namespace llarp { /// aligned buffer that is sz bytes long and aligns to the nearest Alignment template // Microsoft C malloc(3C) cannot return pointers aligned wider than 8 ffs #ifdef _WIN32 struct alignas(uint64_t) AlignedBuffer #else struct alignas(std::max_align_t) AlignedBuffer #endif { static_assert(alignof(std::max_align_t) <= 16, "insane alignment"); static_assert( sz >= 8, "AlignedBuffer cannot be used with buffers smaller than 8 " "bytes"); static constexpr size_t SIZE = sz; virtual ~AlignedBuffer() = default; AlignedBuffer() { Zero(); } explicit AlignedBuffer(const byte_t* data) { *this = data; } explicit AlignedBuffer(const std::array& buf) { _data = buf; } AlignedBuffer& operator=(const byte_t* data) { std::memcpy(_data.data(), data, sz); return *this; } /// bitwise NOT AlignedBuffer operator~() const { AlignedBuffer ret; std::transform(begin(), end(), ret.begin(), [](byte_t a) { return ~a; }); return ret; } bool operator==(const AlignedBuffer& other) const { return _data == other._data; } bool operator!=(const AlignedBuffer& other) const { return _data != other._data; } bool operator<(const AlignedBuffer& other) const { return _data < other._data; } bool operator>(const AlignedBuffer& other) const { return _data > other._data; } bool operator<=(const AlignedBuffer& other) const { return _data <= other._data; } bool operator>=(const AlignedBuffer& other) const { return _data >= other._data; } AlignedBuffer operator^(const AlignedBuffer& other) const { AlignedBuffer ret; std::transform(begin(), end(), other.begin(), ret.begin(), std::bit_xor<>()); return ret; } AlignedBuffer& operator^=(const AlignedBuffer& other) { // Mutate in place instead. for (size_t i = 0; i < sz; ++i) { _data[i] ^= other._data[i]; } return *this; } byte_t& operator[](size_t idx) { assert(idx < SIZE); return _data[idx]; } const byte_t& operator[](size_t idx) const { assert(idx < SIZE); return _data[idx]; } static constexpr size_t size() { return sz; } void Fill(byte_t f) { _data.fill(f); } std::array& as_array() { return _data; } const std::array& as_array() const { return _data; } byte_t* data() { return _data.data(); } const byte_t* data() const { return _data.data(); } bool IsZero() const { const uint64_t* ptr = reinterpret_cast(data()); for (size_t idx = 0; idx < SIZE / sizeof(uint64_t); idx++) { if (ptr[idx]) return false; } return true; } void Zero() { _data.fill(0); } virtual void Randomize() { randombytes(data(), SIZE); } typename std::array::iterator begin() { return _data.begin(); } typename std::array::iterator end() { return _data.end(); } typename std::array::const_iterator begin() const { return _data.cbegin(); } typename std::array::const_iterator end() const { return _data.cend(); } bool FromBytestring(llarp_buffer_t* buf) { if (buf->sz != sz) { llarp::LogError("bdecode buffer size mismatch ", buf->sz, "!=", sz); return false; } memcpy(data(), buf->base, sz); return true; } bool from_string_view(std::string_view b) { if (b.size() != sz) { log::error(util_cat, "Error: buffer size mismatch in aligned buffer!"); return false; } std::memcpy(_data.data(), b.data(), b.size()); return true; } bool from_string(std::string b) { return from_string_view(b); } bool bt_encode(llarp_buffer_t* buf) const { return bencode_write_bytestring(buf, data(), sz); } std::string bt_encode() const { return {reinterpret_cast(data()), sz}; } bool BDecode(llarp_buffer_t* buf) { llarp_buffer_t strbuf; if (!bencode_read_string(buf, &strbuf)) { return false; } return FromBytestring(&strbuf); } std::string_view ToView() const { return {reinterpret_cast(data()), sz}; } std::string ToHex() const { return oxenc::to_hex(begin(), end()); } std::string ShortHex() const { return oxenc::to_hex(begin(), begin() + 4); } bool FromHex(std::string_view str) { if (str.size() != 2 * size() || !oxenc::is_hex(str)) return false; oxenc::from_hex(str.begin(), str.end(), begin()); return true; } private: std::array _data; }; namespace detail { template static std::true_type is_aligned_buffer_impl(AlignedBuffer*); static std::false_type is_aligned_buffer_impl(...); } // namespace detail // True if T is or is derived from AlignedBuffer for any N template constexpr inline bool is_aligned_buffer = decltype(detail::is_aligned_buffer_impl(static_cast(nullptr)))::value; } // namespace llarp namespace fmt { // Any AlignedBuffer (or subclass) gets hex formatted when output: template struct formatter< T, char, std::enable_if_t && !llarp::IsToStringFormattable>> : formatter { template auto format(const T& val, FormatContext& ctx) { auto it = oxenc::hex_encoder{val.begin(), val.end()}; return std::copy(it, it.end(), ctx.out()); } }; } // namespace fmt namespace std { template struct hash> { std::size_t operator()(const llarp::AlignedBuffer& buf) const noexcept { std::size_t h = 0; std::memcpy(&h, buf.data(), sizeof(std::size_t)); return h; } }; } // namespace std