grafos_locator/
locator.rs

1//! Typed locator structs for fabric resources.
2//!
3//! Each locator describes the location of a specific resource type within a
4//! fabric lease. All locators carry a `version: u8` field (currently `1`) for
5//! forward-compatible deserialization.
6
7extern crate alloc;
8use alloc::vec::Vec;
9
10use serde::{Deserialize, Serialize};
11
12/// Locator for a byte range within a memory lease.
13///
14/// Points to `len` bytes starting at `offset` within the lease identified
15/// by `lease_id`.
16#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
17pub struct MemRegionLocator {
18    /// Schema version for this locator type.
19    pub version: u8,
20    /// Fabric lease identifier that owns the memory region.
21    pub lease_id: u128,
22    /// Byte offset within the lease.
23    pub offset: u64,
24    /// Length in bytes.
25    pub len: u64,
26}
27
28impl MemRegionLocator {
29    /// Create a new v1 memory region locator.
30    pub fn new(lease_id: u128, offset: u64, len: u64) -> Self {
31        Self {
32            version: 1,
33            lease_id,
34            offset,
35            len,
36        }
37    }
38}
39
40/// Locator for an LBA range within a block lease.
41///
42/// Points to `blocks` blocks starting at `lba` within the lease identified
43/// by `lease_id`.
44#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
45pub struct BlockRegionLocator {
46    /// Schema version for this locator type.
47    pub version: u8,
48    /// Fabric lease identifier that owns the block region.
49    pub lease_id: u128,
50    /// Starting logical block address.
51    pub lba: u64,
52    /// Number of blocks in the region.
53    pub blocks: u32,
54}
55
56impl BlockRegionLocator {
57    /// Create a new v1 block region locator.
58    pub fn new(lease_id: u128, lba: u64, blocks: u32) -> Self {
59        Self {
60            version: 1,
61            lease_id,
62            lba,
63            blocks,
64        }
65    }
66}
67
68/// Locator for a ring buffer (queue) in a memory lease.
69///
70/// Points to a queue structure at `base_offset` within the lease identified
71/// by `lease_id`. The `generation` field tracks queue resets — consumers
72/// must re-sync when the generation changes.
73#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
74pub struct QueueLocator {
75    /// Schema version for this locator type.
76    pub version: u8,
77    /// Fabric lease identifier that backs the queue.
78    pub lease_id: u128,
79    /// Base byte offset for queue metadata/payload.
80    pub base_offset: u64,
81    /// Queue generation used for stale-consumer detection.
82    pub generation: u64,
83}
84
85impl QueueLocator {
86    /// Create a new v1 queue locator.
87    pub fn new(lease_id: u128, base_offset: u64, generation: u64) -> Self {
88        Self {
89            version: 1,
90            lease_id,
91            base_offset,
92            generation,
93        }
94    }
95}
96
97/// Locator for a request/response RPC arena in a memory lease.
98///
99/// Points to a pair of memory regions (request arena at `request_offset`,
100/// response arena at `response_offset`) within the lease identified by
101/// `lease_id`. The `generation` field tracks arena resets.
102#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
103pub struct RpcArenaLocator {
104    /// Schema version for this locator type.
105    pub version: u8,
106    /// Fabric lease identifier that backs the arena.
107    pub lease_id: u128,
108    /// Request buffer offset.
109    pub request_offset: u64,
110    /// Response buffer offset.
111    pub response_offset: u64,
112    /// Arena generation used for rebinding/fencing.
113    pub generation: u64,
114}
115
116impl RpcArenaLocator {
117    /// Create a new v1 RPC arena locator.
118    pub fn new(lease_id: u128, request_offset: u64, response_offset: u64, generation: u64) -> Self {
119        Self {
120            version: 1,
121            lease_id,
122            request_offset,
123            response_offset,
124            generation,
125        }
126    }
127}
128
129/// Locator for a replicated set of memory regions.
130///
131/// Contains a list of [`MemRegionLocator`]s that point to identical copies
132/// of the same data. The `generation` field increments when the replica set
133/// membership changes.
134#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
135pub struct ReplicaSetLocator {
136    /// Schema version for this locator type.
137    pub version: u8,
138    /// Replica set generation.
139    pub generation: u64,
140    /// Replica locations carrying equivalent content.
141    pub replicas: Vec<MemRegionLocator>,
142}
143
144impl ReplicaSetLocator {
145    /// Create a new v1 replica set locator.
146    pub fn new(generation: u64, replicas: Vec<MemRegionLocator>) -> Self {
147        Self {
148            version: 1,
149            generation,
150            replicas,
151        }
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn mem_region_roundtrip() {
161        let loc = MemRegionLocator::new(0xDEAD_BEEF, 1024, 4096);
162        let bytes = postcard::to_allocvec(&loc).expect("serialize");
163        let decoded: MemRegionLocator = postcard::from_bytes(&bytes).expect("deserialize");
164        assert_eq!(loc, decoded);
165        assert_eq!(decoded.version, 1);
166    }
167
168    #[test]
169    fn block_region_roundtrip() {
170        let loc = BlockRegionLocator::new(0xCAFE_BABE, 0, 256);
171        let bytes = postcard::to_allocvec(&loc).expect("serialize");
172        let decoded: BlockRegionLocator = postcard::from_bytes(&bytes).expect("deserialize");
173        assert_eq!(loc, decoded);
174        assert_eq!(decoded.version, 1);
175    }
176
177    #[test]
178    fn queue_roundtrip() {
179        let loc = QueueLocator::new(0x1234, 512, 7);
180        let bytes = postcard::to_allocvec(&loc).expect("serialize");
181        let decoded: QueueLocator = postcard::from_bytes(&bytes).expect("deserialize");
182        assert_eq!(loc, decoded);
183        assert_eq!(decoded.version, 1);
184    }
185
186    #[test]
187    fn rpc_arena_roundtrip() {
188        let loc = RpcArenaLocator::new(0xABCD, 0, 8192, 3);
189        let bytes = postcard::to_allocvec(&loc).expect("serialize");
190        let decoded: RpcArenaLocator = postcard::from_bytes(&bytes).expect("deserialize");
191        assert_eq!(loc, decoded);
192        assert_eq!(decoded.version, 1);
193    }
194
195    #[test]
196    fn replica_set_roundtrip() {
197        let replicas = vec![
198            MemRegionLocator::new(1, 0, 1024),
199            MemRegionLocator::new(2, 0, 1024),
200            MemRegionLocator::new(3, 0, 1024),
201        ];
202        let loc = ReplicaSetLocator::new(5, replicas);
203        let bytes = postcard::to_allocvec(&loc).expect("serialize");
204        let decoded: ReplicaSetLocator = postcard::from_bytes(&bytes).expect("deserialize");
205        assert_eq!(loc, decoded);
206        assert_eq!(decoded.version, 1);
207        assert_eq!(decoded.replicas.len(), 3);
208    }
209
210    #[test]
211    fn replica_set_empty() {
212        let loc = ReplicaSetLocator::new(0, vec![]);
213        let bytes = postcard::to_allocvec(&loc).expect("serialize");
214        let decoded: ReplicaSetLocator = postcard::from_bytes(&bytes).expect("deserialize");
215        assert_eq!(loc, decoded);
216        assert!(decoded.replicas.is_empty());
217    }
218
219    #[test]
220    fn version_preserved() {
221        // Verify version field survives serialization for all types
222        let m = MemRegionLocator::new(1, 0, 0);
223        assert_eq!(m.version, 1);
224
225        let b = BlockRegionLocator::new(1, 0, 0);
226        assert_eq!(b.version, 1);
227
228        let q = QueueLocator::new(1, 0, 0);
229        assert_eq!(q.version, 1);
230
231        let r = RpcArenaLocator::new(1, 0, 0, 0);
232        assert_eq!(r.version, 1);
233
234        let s = ReplicaSetLocator::new(0, vec![]);
235        assert_eq!(s.version, 1);
236    }
237}