libdpf/include/dpf/net/round_lane.hpp

150 lines
4.8 KiB
C++
Raw Normal View History

/// @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__