Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
221 lines
6.9 KiB
C++
221 lines
6.9 KiB
C++
/// @file dpf/net/dealer_cursor.hpp
|
|
/// @brief Per-(round, index) correction words for a batch session.
|
|
#ifndef LIBDPF_INCLUDE_DPF_NET_DEALER_CURSOR_HPP__
|
|
#define LIBDPF_INCLUDE_DPF_NET_DEALER_CURSOR_HPP__
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "dpf/net/stream_array.hpp"
|
|
|
|
namespace dpf
|
|
{
|
|
namespace net
|
|
{
|
|
|
|
/// @brief Empty correction / blind marker. Protocols that need no dealer
|
|
/// material specialize on this type and skip the cursor.
|
|
struct empty_pad
|
|
{
|
|
};
|
|
|
|
/// @brief Seekable table of dealer corrections, one lane per round.
|
|
/// @details Size is fixed at construction: `count` instances and one slot
|
|
/// width per round. Live dealers and inbox tapes both fill this
|
|
/// table before the online session runs, or fill it lazily through
|
|
/// `put`. Sessions read with `at(round, index)`.
|
|
class dealer_cursor
|
|
{
|
|
public:
|
|
dealer_cursor() = default;
|
|
|
|
dealer_cursor(std::size_t count, std::vector<std::size_t> slot_bytes)
|
|
: count_(count),
|
|
slot_bytes_(std::move(slot_bytes)),
|
|
offsets_(slot_bytes_.size() + 1, 0)
|
|
{
|
|
for (std::size_t r = 0; r < slot_bytes_.size(); ++r)
|
|
offsets_[r + 1] = offsets_[r] + count_ * slot_bytes_[r];
|
|
store_.assign(offsets_.back(), 0);
|
|
ready_.assign(slot_bytes_.size() * count_, false);
|
|
}
|
|
|
|
std::size_t count() const noexcept { return count_; }
|
|
std::size_t rounds() const noexcept { return slot_bytes_.size(); }
|
|
|
|
std::size_t slot_bytes(std::uint16_t round) const
|
|
{
|
|
if (round >= slot_bytes_.size())
|
|
throw std::out_of_range("dealer_cursor round");
|
|
return slot_bytes_[round];
|
|
}
|
|
|
|
void put(std::uint16_t round, std::size_t index, const std::uint8_t * bytes,
|
|
std::size_t n)
|
|
{
|
|
check(round, index, n);
|
|
std::memcpy(ptr(round, index), bytes, n);
|
|
ready_[ready_index(round, index)] = true;
|
|
}
|
|
|
|
/// @brief Fill every index of `round` from a contiguous dealer tape.
|
|
void put_round(std::uint16_t round, const std::uint8_t * bytes,
|
|
std::size_t nbytes)
|
|
{
|
|
if (round >= slot_bytes_.size())
|
|
throw std::out_of_range("dealer_cursor round");
|
|
const std::size_t need = count_ * slot_bytes_[round];
|
|
if (nbytes != need)
|
|
throw std::invalid_argument("dealer_cursor put_round size");
|
|
if (need != 0)
|
|
std::memcpy(store_.data() + offsets_[round], bytes, need);
|
|
for (std::size_t i = 0; i < count_; ++i)
|
|
ready_[ready_index(round, i)] = true;
|
|
}
|
|
|
|
bool ready(std::uint16_t round, std::size_t index) const
|
|
{
|
|
if (round >= slot_bytes_.size() || index >= count_)
|
|
return false;
|
|
if (slot_bytes_[round] == 0)
|
|
return true;
|
|
return ready_[ready_index(round, index)];
|
|
}
|
|
|
|
void at(std::uint16_t round, std::size_t index, std::uint8_t * out,
|
|
std::size_t n) const
|
|
{
|
|
check(round, index, n);
|
|
if (n == 0)
|
|
return;
|
|
if (!ready_[ready_index(round, index)])
|
|
throw std::logic_error("dealer_cursor not ready");
|
|
std::memcpy(out, ptr(round, index), n);
|
|
}
|
|
|
|
template <typename T>
|
|
T at(std::uint16_t round, std::size_t index) const
|
|
{
|
|
static_assert(std::is_trivially_copyable_v<T>,
|
|
"dealer_cursor::at requires a trivially copyable type");
|
|
if constexpr (std::is_same_v<T, empty_pad>)
|
|
{
|
|
(void)round;
|
|
(void)index;
|
|
return empty_pad{};
|
|
}
|
|
else
|
|
{
|
|
T out{};
|
|
at(round, index, reinterpret_cast<std::uint8_t *>(&out), sizeof(T));
|
|
return out;
|
|
}
|
|
}
|
|
|
|
/// @brief Load each round from consecutive stream indexes (one tape per round).
|
|
static dealer_cursor from_streams(stream_array & src, std::size_t count,
|
|
std::vector<std::size_t> slot_bytes, std::size_t first_stream = 0)
|
|
{
|
|
dealer_cursor out(count, std::move(slot_bytes));
|
|
for (std::uint16_t r = 0; r < out.rounds(); ++r)
|
|
{
|
|
const std::size_t need = count * out.slot_bytes(r);
|
|
if (need == 0)
|
|
continue;
|
|
std::vector<std::uint8_t> buf(need);
|
|
src.read(first_stream + r, buf.data(), need);
|
|
out.put_round(r, buf.data(), need);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
private:
|
|
void check(std::uint16_t round, std::size_t index, std::size_t n) const
|
|
{
|
|
if (round >= slot_bytes_.size() || index >= count_)
|
|
throw std::out_of_range("dealer_cursor index");
|
|
if (n != slot_bytes_[round])
|
|
throw std::invalid_argument("dealer_cursor size");
|
|
}
|
|
|
|
std::size_t ready_index(std::uint16_t round, std::size_t index) const noexcept
|
|
{
|
|
return static_cast<std::size_t>(round) * count_ + index;
|
|
}
|
|
|
|
std::uint8_t * ptr(std::uint16_t round, std::size_t index) noexcept
|
|
{
|
|
return store_.data() + offsets_[round] + index * slot_bytes_[round];
|
|
}
|
|
|
|
const std::uint8_t * ptr(std::uint16_t round, std::size_t index) const noexcept
|
|
{
|
|
return store_.data() + offsets_[round] + index * slot_bytes_[round];
|
|
}
|
|
|
|
std::size_t count_ = 0;
|
|
std::vector<std::size_t> slot_bytes_;
|
|
std::vector<std::size_t> offsets_;
|
|
std::vector<std::uint8_t> store_;
|
|
std::vector<bool> ready_;
|
|
};
|
|
|
|
/// @brief Prefetch a full dealer table from `stream_array` tapes (see `from_streams`).
|
|
class stream_dealer_cursor
|
|
{
|
|
public:
|
|
stream_dealer_cursor(stream_array & src, std::size_t count,
|
|
std::vector<std::size_t> slot_bytes, std::size_t first_stream = 0)
|
|
: inner_(dealer_cursor::from_streams(src, count, std::move(slot_bytes),
|
|
first_stream))
|
|
{
|
|
}
|
|
|
|
std::size_t count() const noexcept { return inner_.count(); }
|
|
std::size_t rounds() const noexcept { return inner_.rounds(); }
|
|
|
|
std::size_t slot_bytes(std::uint16_t round) const
|
|
{
|
|
return inner_.slot_bytes(round);
|
|
}
|
|
|
|
bool ready(std::uint16_t round, std::size_t index) const
|
|
{
|
|
return inner_.ready(round, index);
|
|
}
|
|
|
|
void at(std::uint16_t round, std::size_t index, std::uint8_t * out,
|
|
std::size_t n) const
|
|
{
|
|
inner_.at(round, index, out, n);
|
|
}
|
|
|
|
template <typename T>
|
|
T at(std::uint16_t round, std::size_t index) const
|
|
{
|
|
return inner_.at<T>(round, index);
|
|
}
|
|
|
|
const dealer_cursor & cursor() const noexcept { return inner_; }
|
|
dealer_cursor & cursor() noexcept { return inner_; }
|
|
|
|
private:
|
|
dealer_cursor inner_;
|
|
};
|
|
|
|
/// @brief One-round table: `nbytes` must equal `count * slot_bytes[0]`.
|
|
inline dealer_cursor dealer_cursor_from_stream(stream_array & a,
|
|
std::size_t stream_index, std::size_t count, std::size_t slot_nbytes)
|
|
{
|
|
return dealer_cursor::from_streams(a, count, {slot_nbytes}, stream_index);
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace dpf
|
|
|
|
#endif // LIBDPF_INCLUDE_DPF_NET_DEALER_CURSOR_HPP__
|