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.
lokinet/llarp/util/aligned.hpp

367 lines
6.9 KiB
C++

#pragma once
#include "bencode.h"
#include <llarp/util/logging.hpp>
#include <llarp/util/formattable.hpp>
#include <oxenc/hex.h>
#include <array>
#include <cstddef>
6 years ago
#include <iomanip>
#include <iostream>
#include <memory>
#include <numeric>
#include <type_traits>
#include <algorithm>
6 years ago
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);
6 years ago
}
namespace llarp
{
/// aligned buffer that is sz bytes long and aligns to the nearest Alignment
template <size_t sz>
4 years ago
// 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
4 years ago
#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;
6 years ago
AlignedBuffer()
{
Zero();
}
explicit AlignedBuffer(const byte_t* data)
{
*this = data;
}
explicit AlignedBuffer(const std::array<byte_t, SIZE>& buf)
{
_data = buf;
}
AlignedBuffer&
operator=(const byte_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(), [](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<sz> ret;
Replace libuv with uvw & related refactoring - removes all the llarp_ev_* functions, replacing with methods/classes/functions in the llarp namespace. - banish ev/ev.h to the void - Passes various things by const lvalue ref, especially shared_ptr's that don't need to be copied (to avoid an atomic refcount increment/decrement). - Add a llarp::UDPHandle abstract class for UDP handling - Removes the UDP tick handler; code that needs tick can just do a separate handler on the event loop outside the UDP socket. - Adds an "OwnedBuffer" which owns its own memory but is implicitly convertible to a llarp_buffer_t. This is mostly needed to take over ownership of buffers from uvw without copying them as, currently, uvw does its own allocation (pending some open upstream issues/PRs). - Logic: - add `make_caller`/`call_forever`/`call_every` utility functions to abstract Call wrapping and dependent timed tasks. - Add inLogicThread() so that code can tell its inside the logic thread (typically for debugging assertions). - get rid of janky integer returns and dealing with cancellations on call_later: the other methods added here and the event loop code remove the need for them. - Event loop: - redo everything with uvw instead of libuv - rename EventLoopWakeup::Wakeup to EventLoopWakeup::Trigger to better reflect what it does. - add EventLoopRepeater for repeated events, and replace the code that reschedules itself every time it is called with a repeater. - Split up `EventLoop::run()` into a non-virtual base method and abstract `run_loop()` methods; the base method does a couple extra setup/teardown things that don't need to be in the derived class. - udp_listen is replaced with ev->udp(...) which returns a new UDPHandle object rather that needing gross C-style-but-not-actually-C-compatible structs. - Remove unused register_poll_fd_(un)readable - Use shared_ptr for EventLoopWakeup rather than returning a raw pointer; uvw lets us not have to worry about having the event loop class maintain ownership of it. - Add factory EventLoop::create() function to create a default (uvw-based) event loop (previously this was one of the llarp_ev_blahblah unnamespaced functions). - ev_libuv: this is mostly rewritten; all of the glue code/structs, in particular, are gone as they are no longer needed with uvw. - DNS: - Rename DnsHandler to DnsInterceptor to better describe what it does (this is the code that intercepts all DNS to the tun IP range for Android). - endpoint: - remove unused "isolated network" code - remove distinct (but actually always the same) variables for router/endpoint logic objects - llarp_buffer_t - make constructors type-safe against being called with points to non-size-1 values - tun packet reading: - read all available packets off the device/file descriptor; previously we were reading one packet at a time then returning to the event loop to poll again. - ReadNextPacket() now returns a 0-size packet if the read would block (so that we can implement the previous point). - ReadNextPacket() now throws on I/O error - Miscellaneous code cleanups/simplifications
4 years ago
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<byte_t, SIZE>&
as_array()
{
return _data;
}
const std::array<byte_t, SIZE>&
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<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<byte_t, SIZE>::iterator
begin()
{
return _data.begin();
}
typename std::array<byte_t, SIZE>::iterator
end()
{
return _data.end();
}
typename std::array<byte_t, SIZE>::const_iterator
begin() const
{
return _data.cbegin();
}
typename std::array<byte_t, SIZE>::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<const char*>(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<const char*>(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<byte_t, SIZE> _data;
};
namespace detail
{
template <size_t Sz>
static std::true_type
is_aligned_buffer_impl(AlignedBuffer<Sz>*);
static std::false_type
is_aligned_buffer_impl(...);
} // namespace detail
// True if T is or is derived from AlignedBuffer<N> for any N
template <typename T>
constexpr inline bool is_aligned_buffer =
decltype(detail::is_aligned_buffer_impl(static_cast<T*>(nullptr)))::value;
} // namespace llarp
namespace fmt
{
// Any AlignedBuffer<N> (or subclass) gets hex formatted when output:
template <typename T>
struct formatter<
T,
char,
std::enable_if_t<llarp::is_aligned_buffer<T> && !llarp::IsToStringFormattable<T>>>
: formatter<std::string_view>
{
template <typename FormatContext>
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 <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