mirror of https://github.com/oxen-io/lokinet
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
9.1 KiB
C++
318 lines
9.1 KiB
C++
#pragma once
|
|
|
|
#include "formattable.hpp"
|
|
#include "logging.hpp"
|
|
|
|
#include <oxenc/bt.h>
|
|
#include <oxenc/hex.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <numeric>
|
|
#include <type_traits>
|
|
|
|
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 <size_t sz>
|
|
// 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(); }
|
|
|
|
AlignedBuffer(const uint8_t* data) { *this = data; }
|
|
|
|
explicit AlignedBuffer(const std::array<uint8_t, SIZE>& buf) { _data = buf; }
|
|
|
|
AlignedBuffer& operator=(const uint8_t* data)
|
|
{
|
|
std::memcpy(_data.data(), data, sz);
|
|
return *this;
|
|
}
|
|
|
|
/// bitwise NOT
|
|
AlignedBuffer<sz> operator~() const
|
|
{
|
|
AlignedBuffer<sz> ret;
|
|
std::transform(begin(), end(), ret.begin(), [](uint8_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<sz> 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;
|
|
}
|
|
|
|
uint8_t& operator[](size_t idx)
|
|
{
|
|
assert(idx < SIZE);
|
|
return _data[idx];
|
|
}
|
|
|
|
const uint8_t& operator[](size_t idx) const
|
|
{
|
|
assert(idx < SIZE);
|
|
return _data[idx];
|
|
}
|
|
|
|
static constexpr size_t size() { return sz; }
|
|
|
|
void Fill(uint8_t f) { _data.fill(f); }
|
|
|
|
std::array<uint8_t, SIZE>& as_array() { return _data; }
|
|
|
|
const std::array<uint8_t, SIZE>& as_array() const { return _data; }
|
|
|
|
uint8_t* data() { return _data.data(); }
|
|
|
|
const uint8_t* data() const { return _data.data(); }
|
|
|
|
bool is_zero() const
|
|
{
|
|
const uint64_t* ptr = reinterpret_cast<const uint64_t*>(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<uint8_t, SIZE>::iterator begin() { return _data.begin(); }
|
|
|
|
typename std::array<uint8_t, SIZE>::iterator end() { return _data.end(); }
|
|
|
|
typename std::array<uint8_t, SIZE>::const_iterator begin() const { return _data.cbegin(); }
|
|
|
|
typename std::array<uint8_t, SIZE>::const_iterator end() const { return _data.cend(); }
|
|
|
|
bool from_string(std::string_view b)
|
|
{
|
|
if (b.size() != sz)
|
|
{
|
|
// log::error(logcat, "Error: buffer size mismatch in aligned buffer!");
|
|
return false;
|
|
}
|
|
|
|
std::memcpy(_data.data(), b.data(), b.size());
|
|
return true;
|
|
}
|
|
|
|
std::string bt_encode() const
|
|
{
|
|
return oxenc::bt_serialize(_data);
|
|
// return {reinterpret_cast<const char*>(data()), sz};
|
|
}
|
|
|
|
bool bt_decode(std::string buf)
|
|
{
|
|
oxenc::bt_deserialize(buf, _data);
|
|
return true;
|
|
}
|
|
|
|
std::string_view to_view() const { return {reinterpret_cast<const char*>(data()), sz}; }
|
|
|
|
std::string to_string() const { return ToHex(); }
|
|
|
|
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;
|
|
}
|
|
|
|
static constexpr bool to_string_formattable = true;
|
|
|
|
private:
|
|
std::array<uint8_t, SIZE> _data;
|
|
};
|
|
|
|
template <typename T>
|
|
concept bt_type = std::is_base_of_v<oxenc::bt_list_consumer, T>;
|
|
|
|
template <bt_type T>
|
|
struct bt_printer
|
|
{
|
|
log::CategoryLogger logcat = log::Cat("bt-printer");
|
|
|
|
T _bt;
|
|
|
|
bool _read(std::string& entry, T& btc)
|
|
{
|
|
if (btc.is_string())
|
|
{
|
|
entry += "string, value='{}']\n"_format(btc.consume_string_view());
|
|
}
|
|
else if (btc.is_unsigned_integer())
|
|
{
|
|
entry += "uint, value='{}']\n"_format(btc.template consume_integer<uint64_t>());
|
|
}
|
|
else if (btc.is_negative_integer())
|
|
{
|
|
entry += "int, value='-{}']\n"_format(btc.template consume_integer<int64_t>());
|
|
}
|
|
else if (btc.is_integer())
|
|
{
|
|
entry += "int, value='{}']\n"_format(btc.template consume_integer<int64_t>());
|
|
}
|
|
else if (btc.is_list())
|
|
{
|
|
{
|
|
auto sublist = btc.consume_list_consumer();
|
|
entry += "bt-list, contents=[\n";
|
|
_read_list(entry, sublist);
|
|
}
|
|
}
|
|
else if (btc.is_dict())
|
|
{
|
|
{
|
|
auto subdict = btc.consume_dict_consumer();
|
|
entry += "dict, contents=[ ...\n";
|
|
_read_dict(entry, subdict);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
entry += "UNKNOWN... early end to btc contents ]\n";
|
|
btc.finish();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void _read_list(std::string& entry, oxenc::bt_list_consumer& btlc)
|
|
{
|
|
while (not btlc.is_finished())
|
|
{
|
|
entry += "\tentry: [type="s;
|
|
if (not _read(entry, btlc))
|
|
return;
|
|
}
|
|
}
|
|
|
|
void _read_dict(std::string& entry, oxenc::bt_dict_consumer& btdc)
|
|
{
|
|
while (not btdc.is_finished())
|
|
{
|
|
entry += "\tkey: {}, entry: [type="_format(btdc.key());
|
|
if (not _read(entry, btdc))
|
|
return;
|
|
}
|
|
|
|
entry += "\t...end of dict contents ]\n";
|
|
}
|
|
|
|
public:
|
|
bt_printer(std::string_view data)
|
|
{
|
|
try
|
|
{
|
|
if (data.starts_with('l'))
|
|
_bt = oxenc::bt_list_consumer{data};
|
|
else if (data.starts_with('d'))
|
|
_bt = oxenc::bt_dict_consumer{data};
|
|
else
|
|
throw std::invalid_argument{"bt_printer must be given bt list or dict!"};
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
log::critical(logcat, "bt_printer exception: {}", e.what());
|
|
}
|
|
}
|
|
|
|
std::string to_string() const
|
|
{
|
|
std::string ret{};
|
|
|
|
if constexpr (std::is_same_v<T, oxenc::bt_list_consumer>)
|
|
{
|
|
ret += "\nbt-list contents[ \n";
|
|
_read_list(_bt);
|
|
}
|
|
else
|
|
{
|
|
ret += "\nbt-dict contents[ \n";
|
|
_read_dict(_bt);
|
|
}
|
|
ret += "] ";
|
|
return ret;
|
|
}
|
|
static constexpr bool to_string_formattable = true;
|
|
};
|
|
|
|
// auto bt_printer::log_cat = log::Cat("bt_printer");
|
|
|
|
} // namespace llarp
|
|
|
|
namespace std
|
|
{
|
|
template <size_t sz>
|
|
struct hash<llarp::AlignedBuffer<sz>>
|
|
{
|
|
std::size_t operator()(const llarp::AlignedBuffer<sz>& buf) const noexcept
|
|
{
|
|
std::size_t h = 0;
|
|
std::memcpy(&h, buf.data(), sizeof(std::size_t));
|
|
return h;
|
|
}
|
|
};
|
|
} // namespace std
|