use std::fmt;
pub(crate) const RPC_ADDR_LEN: usize = 32;
pub const RPC_SESSION_ID_NEW: i32 = -1;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct RpcAddress {
bytes: [u8; RPC_ADDR_LEN],
}
impl RpcAddress {
pub fn zero() -> Self {
RpcAddress {
bytes: [0u8; RPC_ADDR_LEN],
}
}
pub fn is_zero(&self) -> bool {
self.bytes == [0u8; RPC_ADDR_LEN]
}
pub fn unique(counter: &mut u64, space: AddressSpace) -> Self {
*counter = counter.wrapping_add(1);
let mut bytes = [0u8; RPC_ADDR_LEN];
bytes[..8].copy_from_slice(&counter.to_le_bytes());
bytes[8] = space.tag();
RpcAddress { bytes }
}
pub(crate) fn as_wire_bytes(&self) -> &[u8; RPC_ADDR_LEN] {
&self.bytes
}
pub(crate) fn from_wire_bytes(bytes: [u8; RPC_ADDR_LEN]) -> Self {
RpcAddress { bytes }
}
}
impl fmt::Debug for RpcAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
return write!(f, "RpcAddress(zero)");
}
write!(
f,
"RpcAddress({:02x}{:02x}{:02x}{:02x}…)",
self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressSpace {
Initiator,
Acceptor,
}
impl AddressSpace {
fn tag(self) -> u8 {
match self {
AddressSpace::Initiator => 1,
AddressSpace::Acceptor => 2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum SpecialTransaction {
GetRoot = 0,
GetMaxThreads = 1,
GetSessionId = 2,
GetFdMode = 3,
}
impl SpecialTransaction {
pub fn code(self) -> u32 {
self as u32
}
pub fn from_code(code: u32) -> Option<Self> {
match code {
0 => Some(SpecialTransaction::GetRoot),
1 => Some(SpecialTransaction::GetMaxThreads),
2 => Some(SpecialTransaction::GetSessionId),
3 => Some(SpecialTransaction::GetFdMode),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_is_distinct_and_stable() {
assert!(RpcAddress::zero().is_zero());
assert_eq!(RpcAddress::zero(), RpcAddress::zero());
assert_eq!(RpcAddress::zero().as_wire_bytes(), &[0u8; RPC_ADDR_LEN]);
}
#[test]
fn unique_is_collision_free_and_nonzero() {
let mut ctr = 0u64;
let mut seen = std::collections::HashSet::new();
let zero = RpcAddress::zero();
for _ in 0..1_000_000 {
let a = RpcAddress::unique(&mut ctr, AddressSpace::Initiator);
assert!(!a.is_zero(), "unique() must never be zero");
assert_ne!(a, zero);
assert!(seen.insert(a), "unique() collision");
}
assert_eq!(seen.len(), 1_000_000);
}
#[test]
fn debug_is_fingerprint_only() {
let mut ctr = 0u64;
let a = RpcAddress::unique(&mut ctr, AddressSpace::Initiator);
let s = format!("{a:?}");
assert!(s.starts_with("RpcAddress("));
assert!(s.contains('…'), "Debug must be a truncated fingerprint");
assert_eq!(format!("{:?}", RpcAddress::zero()), "RpcAddress(zero)");
}
#[test]
fn wire_bytes_roundtrip() {
let mut ctr = 41u64;
let a = RpcAddress::unique(&mut ctr, AddressSpace::Initiator);
let b = RpcAddress::from_wire_bytes(*a.as_wire_bytes());
assert_eq!(a, b);
}
#[test]
fn special_transaction_codes_match_r34() {
assert_eq!(SpecialTransaction::GetRoot.code(), 0);
assert_eq!(SpecialTransaction::GetMaxThreads.code(), 1);
assert_eq!(SpecialTransaction::GetSessionId.code(), 2);
assert_eq!(
SpecialTransaction::from_code(2),
Some(SpecialTransaction::GetSessionId)
);
assert_eq!(
SpecialTransaction::from_code(3),
Some(SpecialTransaction::GetFdMode)
);
assert_eq!(SpecialTransaction::from_code(4), None);
}
}