libdpf/include/dpf/net/dealer_cursor.hpp
Ryan Henry 0d22946a0e Checkpoint the party/runtime stack before share-program and malicious-mode work.
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>
2026-09-28 05:59:19 -06:00

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__