1use bytemuck::{Pod, Zeroable};
4use std::mem::{align_of, size_of};
5
6pub fn cast_slice<T: Pod>(v: &[T]) -> &[u8] {
7 bytemuck::cast_slice(v)
8}
9
10pub fn cast_slice_mut<T: Pod>(v: &mut [T]) -> &mut [u8] {
11 bytemuck::cast_slice_mut(v)
12}
13
14pub fn cast_slice_from<T: Pod>(v: &[u8]) -> &[T] {
15 bytemuck::cast_slice(v)
16}
17
18pub fn cast_slice_from_mut<T: Pod>(v: &mut [u8]) -> &mut [T] {
19 bytemuck::cast_slice_mut(v)
20}
21
22pub fn expect_exact_len(actual: usize, expected: usize) -> Result<(), String> {
23 if actual == expected {
24 Ok(())
25 } else {
26 Err(format!("expected {expected} bytes, got {actual}"))
27 }
28}
29
30#[repr(transparent)]
31#[derive(Copy, Clone, Pod, Zeroable)]
32pub struct WireCountU32(pub u32);
33
34pub trait WirePoint: Copy {
35 fn to_wire(self) -> u64;
36 fn from_wire(w: u64) -> Self;
37}
38
39pub const WIRE_VERSION: u16 = 2;
41
42#[repr(C)]
48#[derive(Copy, Clone, Pod, Zeroable)]
49pub struct WireHdr {
50 pub version_le: u16, pub kind_le: u16, pub reserved_le: u32, }
54
55impl WireHdr {
56 pub fn new(kind: u16) -> Self {
57 Self {
58 version_le: WIRE_VERSION.to_le(),
59 kind_le: kind.to_le(),
60 reserved_le: 0,
61 }
62 }
63 pub fn kind(&self) -> u16 {
64 u16::from_le(self.kind_le)
65 }
66 pub fn version(&self) -> u16 {
67 u16::from_le(self.version_le)
68 }
69}
70
71#[repr(C)]
72#[derive(Copy, Clone, Pod, Zeroable)]
73pub struct WireCount {
74 pub n_le: u32, }
76impl WireCount {
77 pub fn new(n: usize) -> Self {
78 Self {
79 n_le: (n as u32).to_le(),
80 }
81 }
82 pub fn get(&self) -> usize {
83 u32::from_le(self.n_le) as usize
84 }
85}
86
87#[repr(C)]
89#[derive(Copy, Clone, Pod, Zeroable)]
90pub struct WirePointRepr {
91 pub id_le: u64,
92}
93impl WirePointRepr {
94 pub fn of(id: u64) -> Self {
95 Self { id_le: id.to_le() }
96 }
97 pub fn get(&self) -> u64 {
98 u64::from_le(self.id_le)
99 }
100}
101
102#[repr(C)]
104#[derive(Copy, Clone, Pod, Zeroable)]
105pub struct WireAdj {
106 pub src_le: u64,
107 pub dst_le: u64,
108}
109impl WireAdj {
110 pub fn new(src: u64, dst: u64) -> Self {
111 Self {
112 src_le: src.to_le(),
113 dst_le: dst.to_le(),
114 }
115 }
116 pub fn src(&self) -> u64 {
117 u64::from_le(self.src_le)
118 }
119 pub fn dst(&self) -> u64 {
120 u64::from_le(self.dst_le)
121 }
122}
123
124#[repr(C)]
128#[derive(Copy, Clone, Pod, Zeroable)]
129pub struct WireArrow {
130 pub src_le: u64,
131 pub dst_le: u64,
132}
133impl WireArrow {
134 pub fn new(src: u64, dst: u64) -> Self {
135 Self {
136 src_le: src.to_le(),
137 dst_le: dst.to_le(),
138 }
139 }
140 pub fn src(&self) -> u64 {
141 u64::from_le(self.src_le)
142 }
143 pub fn dst(&self) -> u64 {
144 u64::from_le(self.dst_le)
145 }
146}
147
148#[repr(C)]
152#[derive(Copy, Clone, Zeroable)]
153pub struct WireOrientedArrow<Pay, Orient>
154where
155 Pay: Copy + Pod + Zeroable,
156 Orient: Copy + Pod + Zeroable,
157{
158 pub src_le: u64,
159 pub dst_le: u64,
160 pub payload: Pay,
161 pub orientation: Orient,
162}
163
164impl<Pay, Orient> WireOrientedArrow<Pay, Orient>
165where
166 Pay: Copy + Pod + Zeroable,
167 Orient: Copy + Pod + Zeroable,
168{
169 pub fn new(src: u64, dst: u64, payload: Pay, orientation: Orient) -> Self {
170 Self {
171 src_le: src.to_le(),
172 dst_le: dst.to_le(),
173 payload,
174 orientation,
175 }
176 }
177 pub fn src(&self) -> u64 {
178 u64::from_le(self.src_le)
179 }
180 pub fn dst(&self) -> u64 {
181 u64::from_le(self.dst_le)
182 }
183}
184
185unsafe impl<Pay, Orient> Pod for WireOrientedArrow<Pay, Orient>
186where
187 Pay: Copy + Pod + Zeroable,
188 Orient: Copy + Pod + Zeroable,
189{
190}
191
192#[repr(C)]
195#[derive(Copy, Clone, Pod, Zeroable)]
196pub struct WireArrowTriple {
197 pub src_le: u64,
198 pub dst_le: u64,
199 pub remote_point_le: u64,
200 pub rank_le: u32, pub _pad: u32, }
203impl WireArrowTriple {
204 pub const SIZE: usize = 32; pub fn new(src: u64, dst: u64, remote: u64, rank: u32) -> Self {
206 Self {
207 src_le: src.to_le(),
208 dst_le: dst.to_le(),
209 remote_point_le: remote.to_le(),
210 rank_le: rank.to_le(),
211 _pad: 0,
212 }
213 }
214 pub fn decode(&self) -> (u64, u64, u64, u32) {
215 (
216 u64::from_le(self.src_le),
217 u64::from_le(self.dst_le),
218 u64::from_le(self.remote_point_le),
219 u32::from_le(self.rank_le),
220 )
221 }
222}
223
224pub const WIRE_PAYLOAD_MAX: usize = 16; #[repr(C)]
231#[derive(Copy, Clone, Pod, Zeroable)]
232pub struct WireStackTriple {
233 pub base_le: u64,
234 pub cap_le: u64,
235 pub pay: [u8; WIRE_PAYLOAD_MAX],
237}
238impl WireStackTriple {
239 pub fn new(base: u64, cap: u64, pay: &[u8]) -> Self {
240 let mut buf = [0u8; WIRE_PAYLOAD_MAX];
241 let n = pay.len().min(WIRE_PAYLOAD_MAX);
242 buf[..n].copy_from_slice(&pay[..n]);
243 Self {
244 base_le: base.to_le(),
245 cap_le: cap.to_le(),
246 pay: buf,
247 }
248 }
249 pub fn base(&self) -> u64 {
250 u64::from_le(self.base_le)
251 }
252 pub fn cap(&self) -> u64 {
253 u64::from_le(self.cap_le)
254 }
255}
256
257const _: () = {
260 assert!(size_of::<WireHdr>() == 8);
262 assert!(size_of::<WireCount>() == 4);
263 assert!(size_of::<WirePointRepr>() == 8);
264 assert!(size_of::<WireAdj>() == 16);
265 assert!(size_of::<WireArrow>() == 16);
266 assert!(size_of::<WireArrowTriple>() == WireArrowTriple::SIZE);
267 assert!(align_of::<WireArrowTriple>() == 8);
268};
269
270impl WirePoint for crate::topology::point::PointId {
271 #[inline]
272 fn to_wire(self) -> u64 {
273 self.get()
274 }
275 #[inline]
276 fn from_wire(w: u64) -> Self {
277 crate::topology::point::PointId::new(w).expect("invalid PointId on wire")
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use bytemuck::{cast_slice, cast_slice_mut};
285
286 #[test]
287 fn roundtrip_adj() {
288 let v = vec![WireAdj::new(1, 2), WireAdj::new(3, 4)];
289 let bytes: Vec<u8> = cast_slice(&v).to_vec();
290 let mut out = vec![WireAdj::zeroed(); v.len()];
291 cast_slice_mut(&mut out).copy_from_slice(&bytes);
292 assert_eq!(out[0].src(), 1);
293 assert_eq!(out[1].dst(), 4);
294 }
295
296 #[test]
297 fn roundtrip_wire_arrow() {
298 let v = vec![WireArrow::new(10, 20), WireArrow::new(30, 40)];
299 let bytes: Vec<u8> = cast_slice(&v).to_vec();
300 let mut out = vec![WireArrow::zeroed(); v.len()];
301 cast_slice_mut(&mut out).copy_from_slice(&bytes);
302 assert_eq!(out[0].src(), 10);
303 assert_eq!(out[1].dst(), 40);
304 }
305
306 #[test]
307 fn roundtrip_arrow() {
308 let t = WireArrowTriple::new(1, 2, 3, 4);
309 let bytes: Vec<u8> = cast_slice(&[t]).to_vec();
310 let mut out = vec![WireArrowTriple::zeroed(); 1];
311 cast_slice_mut(&mut out).copy_from_slice(&bytes);
312 assert_eq!(out[0].decode(), (1, 2, 3, 4));
313 assert_eq!(
314 WireArrowTriple::SIZE,
315 std::mem::size_of::<WireArrowTriple>()
316 );
317 }
318
319 #[test]
320 fn roundtrip_stack() {
321 let pay = [1u8, 2, 3, 4];
322 let t = WireStackTriple::new(10, 20, &pay);
323 let bytes: Vec<u8> = cast_slice(&[t]).to_vec();
324 let mut out = vec![WireStackTriple::zeroed(); 1];
325 cast_slice_mut(&mut out).copy_from_slice(&bytes);
326 assert_eq!(out[0].base(), 10);
327 assert_eq!(out[0].cap(), 20);
328 assert_eq!(&out[0].pay[..4], &pay);
329 }
330
331 #[test]
332 fn version_guard() {
333 let hdr = WireHdr::new(1);
334 assert_eq!(hdr.version(), WIRE_VERSION);
335 }
336}