use serde::{Deserialize, Serialize};
use velo_ext::WorkerId;
const WORKER_SHIFT: u32 = 64;
pub(crate) const MPSC_STREAM_BIT: u64 = 1 << 63;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum AnchorKind {
Spsc,
Mpsc,
}
impl std::fmt::Display for AnchorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AnchorKind::Spsc => f.write_str("spsc"),
AnchorKind::Mpsc => f.write_str("mpsc"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct StreamAnchorHandle(u128);
#[derive(Serialize, Deserialize)]
struct StreamAnchorHandleWire {
hi: u64,
lo: u64,
}
impl StreamAnchorHandle {
pub fn pack(worker_id: WorkerId, local_id: u64) -> Self {
debug_assert!(
local_id & MPSC_STREAM_BIT == 0,
"pack() local_id must fit in 63 bits; MPSC_STREAM_BIT is reserved (use pack_mpsc)"
);
let raw = ((worker_id.as_u64() as u128) << WORKER_SHIFT) | (local_id as u128);
Self(raw)
}
pub fn pack_mpsc(worker_id: WorkerId, local_id: u64) -> Self {
debug_assert!(
local_id & MPSC_STREAM_BIT == 0,
"pack_mpsc() local_id must fit in 63 bits; bit is applied internally"
);
let tagged = local_id | MPSC_STREAM_BIT;
let raw = ((worker_id.as_u64() as u128) << WORKER_SHIFT) | (tagged as u128);
Self(raw)
}
pub fn unpack(&self) -> (WorkerId, u64) {
let worker_id = WorkerId::from_u64((self.0 >> WORKER_SHIFT) as u64);
let local_id = self.0 as u64;
(worker_id, local_id)
}
pub fn as_u128(&self) -> u128 {
self.0
}
pub fn from_u128(raw: u128) -> Self {
Self(raw)
}
pub fn kind(&self) -> AnchorKind {
if self.is_mpsc_stream() {
AnchorKind::Mpsc
} else {
AnchorKind::Spsc
}
}
pub fn is_mpsc_stream(&self) -> bool {
(self.0 as u64) & MPSC_STREAM_BIT != 0
}
pub fn is_spsc_stream(&self) -> bool {
!self.is_mpsc_stream()
}
}
impl Serialize for StreamAnchorHandle {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
StreamAnchorHandleWire {
hi: (self.0 >> 64) as u64,
lo: self.0 as u64,
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for StreamAnchorHandle {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let wire = StreamAnchorHandleWire::deserialize(deserializer)?;
Ok(Self(((wire.hi as u128) << 64) | (wire.lo as u128)))
}
}
impl std::fmt::Display for StreamAnchorHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (worker_id, local_id) = self.unpack();
write!(
f,
"StreamAnchorHandle(worker={}, local={}, kind={})",
worker_id.as_u64(),
local_id & !MPSC_STREAM_BIT,
self.kind()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use velo_ext::WorkerId;
#[test]
fn test_pack_unpack_roundtrip() {
let worker_id = WorkerId::from_u64(0xDEAD_BEEF_1234_5678);
let local_id: u64 = 42;
let handle = StreamAnchorHandle::pack(worker_id, local_id);
let (recovered_worker, recovered_local) = handle.unpack();
assert_eq!(recovered_worker, worker_id);
assert_eq!(recovered_local, local_id);
}
#[test]
fn test_rmp_serde_roundtrip() {
let worker_id = WorkerId::from_u64(0xDEAD_BEEF_1234_5678);
let local_id: u64 = 99;
let handle = StreamAnchorHandle::pack(worker_id, local_id);
let encoded = rmp_serde::to_vec(&handle).expect("serialize");
let decoded: StreamAnchorHandle = rmp_serde::from_slice(&encoded).expect("deserialize");
assert_eq!(handle, decoded);
let (w, l) = decoded.unpack();
assert_eq!(w, worker_id);
assert_eq!(l, local_id);
}
#[test]
fn test_zero_local_id_encodes() {
let worker_id = WorkerId::from_u64(1);
let handle = StreamAnchorHandle::pack(worker_id, 0);
let (w, l) = handle.unpack();
assert_eq!(w, worker_id);
assert_eq!(l, 0);
}
#[test]
fn test_max_spsc_values_roundtrip() {
let worker_id = WorkerId::from_u64(u64::MAX);
let local_id = (1u64 << 63) - 1;
let handle = StreamAnchorHandle::pack(worker_id, local_id);
assert!(handle.is_spsc_stream());
assert!(!handle.is_mpsc_stream());
let encoded = rmp_serde::to_vec(&handle).unwrap();
let decoded: StreamAnchorHandle = rmp_serde::from_slice(&encoded).unwrap();
assert_eq!(handle, decoded);
let (w, l) = decoded.unpack();
assert_eq!(w.as_u64(), u64::MAX);
assert_eq!(l, (1u64 << 63) - 1);
}
#[test]
fn test_max_mpsc_values_roundtrip() {
let worker_id = WorkerId::from_u64(u64::MAX);
let raw_local = (1u64 << 63) - 1;
let handle = StreamAnchorHandle::pack_mpsc(worker_id, raw_local);
assert!(handle.is_mpsc_stream());
assert!(!handle.is_spsc_stream());
let encoded = rmp_serde::to_vec(&handle).unwrap();
let decoded: StreamAnchorHandle = rmp_serde::from_slice(&encoded).unwrap();
assert_eq!(handle, decoded);
let (w, l) = decoded.unpack();
assert_eq!(w.as_u64(), u64::MAX);
assert_eq!(l, u64::MAX, "packed local_id is raw | MPSC_STREAM_BIT");
}
#[test]
fn test_kind_discriminator() {
let wid = WorkerId::from_u64(1);
let spsc = StreamAnchorHandle::pack(wid, 7);
let mpsc = StreamAnchorHandle::pack_mpsc(wid, 7);
assert!(spsc.is_spsc_stream());
assert!(!spsc.is_mpsc_stream());
assert_eq!(spsc.kind(), AnchorKind::Spsc);
assert!(mpsc.is_mpsc_stream());
assert!(!mpsc.is_spsc_stream());
assert_eq!(mpsc.kind(), AnchorKind::Mpsc);
assert_ne!(spsc, mpsc);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "MPSC_STREAM_BIT is reserved")]
fn test_pack_rejects_mpsc_bit_in_debug() {
let wid = WorkerId::from_u64(1);
let _ = StreamAnchorHandle::pack(wid, MPSC_STREAM_BIT | 1);
}
}