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>
149 lines
4.8 KiB
C++
149 lines
4.8 KiB
C++
/// @file dpf/net/round_lane.hpp
|
|
/// @brief Round→lane mapping and wire framing when rounds share lanes.
|
|
/// @details A fixed pool of duplex streams can carry any number of exchange
|
|
/// rounds: lane = `round % n_lanes`. A framed lane prefixes each write
|
|
/// with a little-endian `{u16 round, u32 nbytes}` header, and the
|
|
/// receiver appends those bytes to that round's inbox. Unframed lanes
|
|
/// carry one round each (round == lane) with no header.
|
|
#ifndef LIBDPF_INCLUDE_DPF_NET_ROUND_LANE_HPP__
|
|
#define LIBDPF_INCLUDE_DPF_NET_ROUND_LANE_HPP__
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "dpf/net/policy.hpp"
|
|
|
|
namespace dpf
|
|
{
|
|
namespace net
|
|
{
|
|
|
|
/// @brief On-wire header for a framed round payload on a shared lane.
|
|
struct round_lane_hdr
|
|
{
|
|
static constexpr std::size_t size = 6;
|
|
|
|
std::uint16_t round = 0;
|
|
std::uint32_t nbytes = 0;
|
|
|
|
void pack(std::uint8_t * dst) const noexcept
|
|
{
|
|
dst[0] = static_cast<std::uint8_t>(round & 0xffu);
|
|
dst[1] = static_cast<std::uint8_t>((round >> 8) & 0xffu);
|
|
dst[2] = static_cast<std::uint8_t>(nbytes & 0xffu);
|
|
dst[3] = static_cast<std::uint8_t>((nbytes >> 8) & 0xffu);
|
|
dst[4] = static_cast<std::uint8_t>((nbytes >> 16) & 0xffu);
|
|
dst[5] = static_cast<std::uint8_t>((nbytes >> 24) & 0xffu);
|
|
}
|
|
|
|
static round_lane_hdr unpack(const std::uint8_t * src) noexcept
|
|
{
|
|
round_lane_hdr h;
|
|
h.round = static_cast<std::uint16_t>(src[0] | (src[1] << 8));
|
|
h.nbytes = static_cast<std::uint32_t>(src[2] | (src[3] << 8) |
|
|
(src[4] << 16) | (src[5] << 24));
|
|
return h;
|
|
}
|
|
};
|
|
|
|
/// @brief Lane count used when a caller passes `0`.
|
|
inline constexpr std::size_t default_lanes = 8;
|
|
/// @brief Request one stream per round.
|
|
inline constexpr std::size_t lanes_one_per_round = static_cast<std::size_t>(-1);
|
|
/// @brief Round ids travel as `u16`.
|
|
inline constexpr std::size_t max_rounds = 0xffffu;
|
|
|
|
/// @brief Streams to open for `n_rounds`. `requested` 0 → `default_lanes`.
|
|
inline std::size_t lane_count_for_rounds(std::size_t n_rounds,
|
|
std::size_t requested = 0)
|
|
{
|
|
if (n_rounds == 0)
|
|
return 1;
|
|
if (requested == lanes_one_per_round)
|
|
return n_rounds;
|
|
std::size_t want = requested == 0 ? default_lanes : requested;
|
|
if (want > n_rounds)
|
|
want = n_rounds;
|
|
return want == 0 ? 1 : want;
|
|
}
|
|
|
|
/// @brief Lane index and whether the wire uses round headers.
|
|
struct round_lane_map
|
|
{
|
|
std::size_t n_lanes = 1;
|
|
std::size_t n_rounds = 0;
|
|
bool framed = false;
|
|
|
|
round_lane_map() = default;
|
|
|
|
round_lane_map(std::size_t streams, std::size_t rounds,
|
|
framing_mode mode = framing_mode::automatic)
|
|
: n_lanes(streams == 0 ? 1 : streams),
|
|
n_rounds(rounds)
|
|
{
|
|
if (rounds > max_rounds)
|
|
throw std::invalid_argument("round_lane_map: "
|
|
+ std::to_string(rounds) + " rounds exceed the u16 round id");
|
|
switch (mode)
|
|
{
|
|
case framing_mode::automatic:
|
|
framed = rounds > n_lanes;
|
|
break;
|
|
case framing_mode::always:
|
|
framed = true;
|
|
break;
|
|
case framing_mode::never:
|
|
if (rounds > n_lanes)
|
|
throw std::invalid_argument("round_lane_map: framing never needs "
|
|
"one lane per round (" + std::to_string(rounds)
|
|
+ " rounds, " + std::to_string(n_lanes) + " lanes)");
|
|
framed = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::size_t lane(std::uint16_t round) const
|
|
{
|
|
if (static_cast<std::size_t>(round) >= n_rounds && n_rounds != 0)
|
|
throw std::out_of_range("round_lane_map round");
|
|
if (!framed)
|
|
return static_cast<std::size_t>(round);
|
|
return static_cast<std::size_t>(round) % n_lanes;
|
|
}
|
|
};
|
|
|
|
/// @brief Build `hdr || payload` for a framed write.
|
|
inline std::vector<std::uint8_t> pack_round_frame(std::uint16_t round,
|
|
const std::uint8_t * payload, std::size_t n)
|
|
{
|
|
std::vector<std::uint8_t> out(round_lane_hdr::size + n);
|
|
round_lane_hdr{round, static_cast<std::uint32_t>(n)}.pack(out.data());
|
|
if (n != 0)
|
|
std::memcpy(out.data() + round_lane_hdr::size, payload, n);
|
|
return out;
|
|
}
|
|
|
|
/// @brief FNV-1a over a slot-width vector (plan shape fingerprint).
|
|
inline std::uint64_t slots_fingerprint(const std::vector<std::size_t> & slots)
|
|
{
|
|
std::uint64_t h = 14695981039346656037ull;
|
|
for (auto s : slots)
|
|
{
|
|
std::uint64_t v = static_cast<std::uint64_t>(s);
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
h ^= (v >> (8 * i)) & 0xffu;
|
|
h *= 1099511628211ull;
|
|
}
|
|
}
|
|
return h;
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace dpf
|
|
|
|
#endif // LIBDPF_INCLUDE_DPF_NET_ROUND_LANE_HPP__
|