use super::projection::{Projection, ProjectionId};
pub const MAX_NEIGHBORS: usize = 8;
const GRAPH_BYTES: usize = MAX_NEIGHBORS * 2 + 4 + 4 + 8;
#[derive(Debug, Clone, Copy, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PadicAddr {
pub base: u32,
pub coeff0: u16,
pub coeff1: u16,
}
impl PadicAddr {
pub fn to_string(&self) -> String {
format!("{}.{}.{}", self.base, self.coeff0, self.coeff1)
}
}
#[derive(Debug, Clone)]
pub struct GraphProjection {
pub neighbors: [u16; MAX_NEIGHBORS],
pub layer: u8,
pub address: PadicAddr,
}
impl Default for GraphProjection {
fn default() -> Self {
Self {
neighbors: [u16::MAX; MAX_NEIGHBORS],
layer: 0,
address: PadicAddr::default(),
}
}
}
impl Projection for GraphProjection {
fn byte_size() -> usize {
GRAPH_BYTES
}
fn id() -> ProjectionId {
ProjectionId::Graph
}
fn read(buf: &[u8]) -> Self {
assert!(buf.len() >= GRAPH_BYTES);
let mut neighbors = [u16::MAX; MAX_NEIGHBORS];
for i in 0..MAX_NEIGHBORS {
let off = i * 2;
neighbors[i] = u16::from_le_bytes([buf[off], buf[off + 1]]);
}
let layer_off = MAX_NEIGHBORS * 2;
let layer = buf[layer_off];
let addr_off = layer_off + 4; let base = u32::from_le_bytes([
buf[addr_off],
buf[addr_off + 1],
buf[addr_off + 2],
buf[addr_off + 3],
]);
let coeff0 = u16::from_le_bytes([buf[addr_off + 4], buf[addr_off + 5]]);
let coeff1 = u16::from_le_bytes([buf[addr_off + 6], buf[addr_off + 7]]);
Self {
neighbors,
layer,
address: PadicAddr {
base,
coeff0,
coeff1,
},
}
}
fn write(&self, buf: &mut [u8]) {
assert!(buf.len() >= GRAPH_BYTES);
for i in 0..MAX_NEIGHBORS {
let off = i * 2;
buf[off..off + 2].copy_from_slice(&self.neighbors[i].to_le_bytes());
}
let layer_off = MAX_NEIGHBORS * 2;
buf[layer_off] = self.layer;
buf[layer_off + 1] = 0; buf[layer_off + 2] = 0;
buf[layer_off + 3] = 0;
let addr_off = layer_off + 4;
buf[addr_off..addr_off + 4].copy_from_slice(&self.address.base.to_le_bytes());
buf[addr_off + 4..addr_off + 6].copy_from_slice(&self.address.coeff0.to_le_bytes());
buf[addr_off + 6..addr_off + 8].copy_from_slice(&self.address.coeff1.to_le_bytes());
}
fn shape_hash_contribution(&self) -> u32 {
let mut hash = 0x811c_9dc5u32;
for &n in &self.neighbors {
for byte in n.to_le_bytes() {
hash ^= byte as u32;
hash = hash.wrapping_mul(0x0100_0193);
}
}
hash
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_graph_byte_size() {
assert_eq!(GraphProjection::byte_size(), 32);
}
#[test]
fn test_graph_roundtrip() {
let mut proj = GraphProjection::default();
proj.neighbors[0] = 42;
proj.neighbors[3] = 100;
proj.layer = 2;
proj.address = PadicAddr {
base: 5,
coeff0: 1,
coeff1: 77,
};
let mut buf = vec![0u8; GraphProjection::byte_size()];
proj.write(&mut buf);
let restored = GraphProjection::read(&buf);
assert_eq!(restored.neighbors[0], 42);
assert_eq!(restored.neighbors[3], 100);
assert_eq!(restored.neighbors[7], u16::MAX); assert_eq!(restored.layer, 2);
assert_eq!(restored.address.base, 5);
assert_eq!(restored.address.coeff0, 1);
assert_eq!(restored.address.coeff1, 77);
}
#[test]
fn test_padic_to_string() {
let addr = PadicAddr {
base: 3,
coeff0: 0,
coeff1: 42,
};
assert_eq!(addr.to_string(), "3.0.42");
}
}