grafos_collections/
vec.rs

1//! Growable arrays backed by leased fabric memory.
2//!
3//! This module provides two array types:
4//!
5//! - [`FabricVec<T>`] stores elements in fixed-size slots within a single
6//!   memory lease. When the lease is full, [`grow()`](FabricVec::grow)
7//!   acquires additional leases and chains them together. The
8//!   [`push()`](FabricVec::push) method calls `grow()` automatically.
9//!
10//! - [`VarFabricVec<T>`] stores variable-size elements using an index+heap
11//!   layout. The index region holds fixed-size `(offset, len)` entries that
12//!   point into a heap region where the actual serialized data lives. This
13//!   avoids wasting space when element sizes vary widely.
14//!
15//! # FabricVec memory layout
16//!
17//! ```text
18//! Offset 0:  [header]  len: u64 (8 bytes) | capacity: u64 (8 bytes) | stride: u64 (8 bytes)
19//! Offset 24: [data]    element 0 (stride bytes) | element 1 | ...
20//! ```
21//!
22//! Each element slot is `stride` bytes wide, determined at construction time.
23//! Within each slot, the first 4 bytes store the serialized element length
24//! as a little-endian `u32`, followed by the postcard-serialized bytes.
25//! Variable-size types must fit within `stride - 4` serialized bytes;
26//! exceeding it returns [`FabricError::CapacityExceeded`].
27//!
28//! When a `FabricVec` grows, additional leases are acquired and stored in
29//! an overflow chain. Elements in overflow regions are addressed by
30//! computing which region contains the target index.
31//!
32//! # VarFabricVec memory layout
33//!
34//! ```text
35//! Offset 0:  [header]       len: u64 (8) | index_cap: u64 (8) | heap_size: u64 (8) | heap_used: u64 (8)
36//! Offset 32: [index region] entry 0 (12 bytes: offset u64 + len u32) | entry 1 | ...
37//! After index: [heap region] variable-length serialized data packed contiguously
38//! ```
39//!
40//! # Examples
41//!
42//! ```rust
43//! use grafos_collections::vec::FabricVec;
44//! use grafos_std::mem::MemBuilder;
45//!
46//! # grafos_std::host::reset_mock();
47//! # grafos_std::host::mock_set_fbmu_arena_size(65536);
48//! let lease = MemBuilder::new().min_bytes(4096).acquire()?;
49//! let mut v: FabricVec<u32> = FabricVec::new(lease, 16)?;
50//!
51//! v.push(&10)?;
52//! v.push(&20)?;
53//! assert_eq!(v.get(0)?, 10);
54//! assert_eq!(v.pop()?, Some(20));
55//! # Ok::<(), grafos_std::FabricError>(())
56//! ```
57
58extern crate alloc;
59use alloc::vec;
60use alloc::vec::Vec;
61
62use grafos_std::error::{FabricError, Result};
63use grafos_std::mem::{MemBuilder, MemLease};
64
65use serde::{de::DeserializeOwned, Serialize};
66
67const HEADER_SIZE: u64 = 24; // len(8) + capacity(8) + stride(8)
68
69/// A growable array stored in leased fabric memory.
70///
71/// Elements are serialized via postcard into fixed-size slots within the
72/// arena. The collection owns the underlying [`MemLease`] and releases it
73/// when dropped.
74///
75/// # Type parameters
76///
77/// `T` must implement `Serialize + DeserializeOwned`. The serialized form
78/// of each element must fit within `stride - 4` bytes (4 bytes are used
79/// for the length prefix).
80///
81/// # Example
82///
83/// ```rust
84/// use grafos_collections::vec::FabricVec;
85/// use grafos_std::mem::MemBuilder;
86///
87/// # grafos_std::host::reset_mock();
88/// # grafos_std::host::mock_set_fbmu_arena_size(65536);
89/// let mut v: FabricVec<u32> = FabricVec::with_capacity(100, 16)?;
90/// v.push(&42)?;
91/// v.push(&99)?;
92///
93/// for item in v.iter() {
94///     println!("{}", item?);
95/// }
96/// # Ok::<(), grafos_std::FabricError>(())
97/// ```
98pub struct FabricVec<T> {
99    lease: MemLease,
100    len: u64,
101    capacity: u64,
102    stride: u64,
103    overflow: Vec<OverflowRegion>,
104    _marker: core::marker::PhantomData<T>,
105}
106
107/// An additional memory region chained after the primary lease is full.
108struct OverflowRegion {
109    lease: MemLease,
110    capacity: u64,
111}
112
113impl<T: Serialize + DeserializeOwned> FabricVec<T> {
114    /// Create a new `FabricVec` that takes ownership of the given lease.
115    ///
116    /// `stride` is the slot width in bytes. Each slot stores a 4-byte
117    /// length prefix plus the postcard-serialized element, so the maximum
118    /// serialized element size is `stride - 4`.
119    ///
120    /// Capacity is computed as `(arena_size - 24) / stride`.
121    ///
122    /// # Errors
123    ///
124    /// Returns [`FabricError::CapacityExceeded`] if the arena is too small
125    /// to hold even the 24-byte header.
126    pub fn new(lease: MemLease, stride: usize) -> Result<Self> {
127        let arena = lease.mem().arena_size()?;
128        if arena < HEADER_SIZE {
129            return Err(FabricError::CapacityExceeded);
130        }
131        let stride = stride as u64;
132        let capacity = (arena - HEADER_SIZE) / stride;
133        let v = FabricVec {
134            lease,
135            len: 0,
136            capacity,
137            stride,
138            overflow: Vec::new(),
139            _marker: core::marker::PhantomData,
140        };
141        v.write_header()?;
142        Ok(v)
143    }
144
145    /// Create a new `FabricVec` by acquiring a lease sized for `cap` elements
146    /// of `stride` bytes each.
147    ///
148    /// This is a convenience constructor that calls `MemBuilder::new().min_bytes(...).acquire()`
149    /// and then `FabricVec::new()`.
150    ///
151    /// # Errors
152    ///
153    /// Returns [`FabricError::CapacityExceeded`] if the host cannot provide
154    /// an arena large enough for `cap * stride + 24` bytes.
155    pub fn with_capacity(cap: usize, stride: usize) -> Result<Self> {
156        let needed = HEADER_SIZE + (cap as u64) * (stride as u64);
157        let lease = MemBuilder::new().min_bytes(needed).acquire()?;
158        Self::new(lease, stride)
159    }
160
161    /// Push an element onto the end of the vector.
162    ///
163    /// If the vector is full, `grow()` is called to acquire a new memory
164    /// lease and chain it. Returns [`FabricError::CapacityExceeded`] only
165    /// if `grow()` fails (the host cannot allocate more memory).
166    pub fn push(&mut self, item: &T) -> Result<()> {
167        if self.len >= self.capacity {
168            self.grow()?;
169        }
170        let bytes = postcard::to_allocvec(item).map_err(|_| FabricError::IoError(-1))?;
171        if bytes.len() as u64 > self.stride {
172            return Err(FabricError::CapacityExceeded);
173        }
174        let (mem, offset) = self.resolve_slot(self.len);
175        let mut slot = vec![0u8; self.stride as usize];
176        let len_bytes = (bytes.len() as u32).to_le_bytes();
177        slot[..4].copy_from_slice(&len_bytes);
178        slot[4..4 + bytes.len()].copy_from_slice(&bytes);
179        mem.write(offset, &slot)?;
180        self.len += 1;
181        self.write_header()?;
182        Ok(())
183    }
184
185    /// Acquire a new memory lease and chain it to this vector, increasing
186    /// total capacity.
187    ///
188    /// The new region has the same stride and approximately the same
189    /// capacity as the primary region.
190    ///
191    /// # Errors
192    ///
193    /// Returns [`FabricError::CapacityExceeded`] if the host cannot
194    /// allocate more memory.
195    pub fn grow(&mut self) -> Result<()> {
196        let primary_cap = (self.lease.mem().arena_size()? - HEADER_SIZE) / self.stride;
197        let needed = HEADER_SIZE + primary_cap * self.stride;
198        let new_lease = MemBuilder::new().min_bytes(needed).acquire()?;
199        let arena = new_lease.mem().arena_size()?;
200        let new_cap = (arena - HEADER_SIZE) / self.stride;
201        self.capacity += new_cap;
202        self.overflow.push(OverflowRegion {
203            lease: new_lease,
204            capacity: new_cap,
205        });
206        self.write_header()?;
207        Ok(())
208    }
209
210    /// Remove and return the last element, or `None` if the vector is empty.
211    pub fn pop(&mut self) -> Result<Option<T>> {
212        if self.len == 0 {
213            return Ok(None);
214        }
215        let item = self.get(self.len as usize - 1)?;
216        self.len -= 1;
217        self.write_header()?;
218        Ok(Some(item))
219    }
220
221    /// Read the element at `index`.
222    ///
223    /// # Errors
224    ///
225    /// Returns [`FabricError::CapacityExceeded`] if `index >= len`.
226    pub fn get(&self, index: usize) -> Result<T> {
227        if index as u64 >= self.len {
228            return Err(FabricError::CapacityExceeded);
229        }
230        let (mem, offset) = self.resolve_slot(index as u64);
231        let raw = mem.read(offset, self.stride as u32)?;
232        let ser_len = u32::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]) as usize;
233        postcard::from_bytes(&raw[4..4 + ser_len]).map_err(|_| FabricError::IoError(-1))
234    }
235
236    /// Overwrite the element at `index`.
237    ///
238    /// # Errors
239    ///
240    /// Returns [`FabricError::CapacityExceeded`] if `index >= len`.
241    pub fn set(&mut self, index: usize, item: &T) -> Result<()> {
242        if index as u64 >= self.len {
243            return Err(FabricError::CapacityExceeded);
244        }
245        let bytes = postcard::to_allocvec(item).map_err(|_| FabricError::IoError(-1))?;
246        if bytes.len() as u64 > self.stride {
247            return Err(FabricError::CapacityExceeded);
248        }
249        let (mem, offset) = self.resolve_slot(index as u64);
250        let mut slot = vec![0u8; self.stride as usize];
251        let len_bytes = (bytes.len() as u32).to_le_bytes();
252        slot[..4].copy_from_slice(&len_bytes);
253        slot[4..4 + bytes.len()].copy_from_slice(&bytes);
254        mem.write(offset, &slot)?;
255        Ok(())
256    }
257
258    /// Returns the number of elements in the vector.
259    pub fn len(&self) -> usize {
260        self.len as usize
261    }
262
263    /// Returns `true` if the vector contains no elements.
264    pub fn is_empty(&self) -> bool {
265        self.len == 0
266    }
267
268    /// Returns the maximum number of elements the vector can hold.
269    pub fn capacity(&self) -> usize {
270        self.capacity as usize
271    }
272
273    /// Clear the vector, setting len to 0.
274    pub fn clear(&mut self) -> Result<()> {
275        self.len = 0;
276        self.write_header()
277    }
278
279    /// Returns an iterator over the elements.
280    pub fn iter(&self) -> FabricVecIter<'_, T> {
281        FabricVecIter {
282            vec: self,
283            index: 0,
284        }
285    }
286
287    /// Returns the lease ID of the primary memory lease for external
288    /// renewal management (e.g. via [`grafos_leasekit::RenewalManager`]).
289    pub fn lease_id(&self) -> u128 {
290        self.lease.lease_id()
291    }
292
293    /// Returns the expiry time (unix seconds) of the primary memory lease
294    /// for external renewal management.
295    pub fn expires_at_unix_secs(&self) -> u64 {
296        self.lease.expires_at_unix_secs()
297    }
298
299    /// Resolve an element index to the (FabricMem, byte-offset) pair for
300    /// the slot, traversing into overflow regions when necessary.
301    fn resolve_slot(&self, index: u64) -> (&grafos_std::mem::FabricMem, u64) {
302        let primary_cap = self.primary_capacity();
303        if index < primary_cap {
304            return (self.lease.mem(), HEADER_SIZE + index * self.stride);
305        }
306        let mut remaining = index - primary_cap;
307        for region in &self.overflow {
308            if remaining < region.capacity {
309                let offset = HEADER_SIZE + remaining * self.stride;
310                return (region.lease.mem(), offset);
311            }
312            remaining -= region.capacity;
313        }
314        // Should never reach here if len/capacity invariants hold.
315        // Return primary lease at offset 0 as a fallback -- the subsequent
316        // read/write will likely return an error.
317        (self.lease.mem(), 0)
318    }
319
320    /// Capacity of just the primary lease (excluding overflow).
321    fn primary_capacity(&self) -> u64 {
322        let arena = self.lease.mem().arena_size().unwrap_or(HEADER_SIZE);
323        (arena - HEADER_SIZE) / self.stride
324    }
325
326    fn write_header(&self) -> Result<()> {
327        let mut hdr = [0u8; HEADER_SIZE as usize];
328        hdr[0..8].copy_from_slice(&self.len.to_le_bytes());
329        hdr[8..16].copy_from_slice(&self.capacity.to_le_bytes());
330        hdr[16..24].copy_from_slice(&self.stride.to_le_bytes());
331        self.lease.mem().write(0, &hdr)
332    }
333}
334
335/// Iterator over elements of a [`FabricVec`].
336///
337/// Each call to `next()` performs a remote read to fetch and deserialize
338/// one element. There is no prefetching or batching.
339///
340/// Yields `Result<T>` -- a read or deserialization failure produces an
341/// `Err` item rather than panicking.
342pub struct FabricVecIter<'a, T> {
343    vec: &'a FabricVec<T>,
344    index: usize,
345}
346
347impl<'a, T: Serialize + DeserializeOwned> Iterator for FabricVecIter<'a, T> {
348    type Item = Result<T>;
349
350    fn next(&mut self) -> Option<Self::Item> {
351        if self.index >= self.vec.len() {
352            return None;
353        }
354        let result = self.vec.get(self.index);
355        self.index += 1;
356        Some(result)
357    }
358
359    fn size_hint(&self) -> (usize, Option<usize>) {
360        let remaining = self.vec.len() - self.index;
361        (remaining, Some(remaining))
362    }
363}
364
365// ---------------------------------------------------------------------------
366// VarFabricVec — variable-size element vector
367// ---------------------------------------------------------------------------
368
369const VAR_HEADER_SIZE: u64 = 32; // len(8) + index_cap(8) + heap_size(8) + heap_used(8)
370const INDEX_ENTRY_SIZE: u64 = 12; // offset(8) + len(4)
371
372/// A vector that stores variable-size elements in leased fabric memory.
373///
374/// Unlike [`FabricVec`] which uses fixed-stride slots, `VarFabricVec` uses
375/// an index+heap layout. The index region holds fixed-size 12-byte entries
376/// `(offset: u64, len: u32)` that point into a heap region where the actual
377/// postcard-serialized data is packed contiguously. This avoids wasting
378/// space when serialized element sizes vary widely (e.g. `String`,
379/// `Vec<u8>`).
380///
381/// # Trade-offs
382///
383/// - Better space efficiency for variable-size types.
384/// - Two remote reads per `get()` (index entry + heap data) instead of one.
385/// - No in-place `set()` if the new element is larger than the old one
386///   (the old heap space is not reclaimed; a compacting GC is not provided).
387///
388/// # Example
389///
390/// ```rust
391/// use grafos_collections::vec::VarFabricVec;
392/// use grafos_std::mem::MemBuilder;
393///
394/// # grafos_std::host::reset_mock();
395/// # grafos_std::host::mock_set_fbmu_arena_size(65536);
396/// let lease = MemBuilder::new().min_bytes(8192).acquire()?;
397/// let mut v: VarFabricVec<String> = VarFabricVec::new(lease, 128)?;
398///
399/// v.push(&"hello".into())?;
400/// v.push(&"world, this is a longer string".into())?;
401/// assert_eq!(v.get(0)?, "hello".to_string());
402/// assert_eq!(v.len(), 2);
403/// # Ok::<(), grafos_std::FabricError>(())
404/// ```
405pub struct VarFabricVec<T> {
406    lease: MemLease,
407    len: u64,
408    index_cap: u64,
409    heap_start: u64,
410    heap_size: u64,
411    heap_used: u64,
412    _marker: core::marker::PhantomData<T>,
413}
414
415impl<T: Serialize + DeserializeOwned> VarFabricVec<T> {
416    /// Create a new `VarFabricVec` over the given lease.
417    ///
418    /// `max_elements` determines the number of index entries reserved.
419    /// The remaining arena space after the header and index region is
420    /// used as the heap for element data.
421    ///
422    /// # Errors
423    ///
424    /// Returns [`FabricError::CapacityExceeded`] if the arena is too small
425    /// for the header, index region, and at least 1 byte of heap.
426    pub fn new(lease: MemLease, max_elements: usize) -> Result<Self> {
427        let arena = lease.mem().arena_size()?;
428        let index_cap = max_elements as u64;
429        let heap_start = VAR_HEADER_SIZE + index_cap * INDEX_ENTRY_SIZE;
430        if arena <= heap_start {
431            return Err(FabricError::CapacityExceeded);
432        }
433        let heap_size = arena - heap_start;
434        let v = VarFabricVec {
435            lease,
436            len: 0,
437            index_cap,
438            heap_start,
439            heap_size,
440            heap_used: 0,
441            _marker: core::marker::PhantomData,
442        };
443        v.write_header()?;
444        Ok(v)
445    }
446
447    /// Create a new `VarFabricVec` by acquiring a lease.
448    ///
449    /// `max_elements` is the index capacity. `avg_element_bytes` is a hint
450    /// for sizing the heap region. The actual arena requested is:
451    /// `header + index + max_elements * avg_element_bytes`.
452    pub fn with_capacity(max_elements: usize, avg_element_bytes: usize) -> Result<Self> {
453        let needed = VAR_HEADER_SIZE
454            + (max_elements as u64) * INDEX_ENTRY_SIZE
455            + (max_elements as u64) * (avg_element_bytes as u64);
456        let lease = MemBuilder::new().min_bytes(needed).acquire()?;
457        Self::new(lease, max_elements)
458    }
459
460    /// Push an element onto the end of the vector.
461    ///
462    /// Serializes the element and appends it to the heap region.
463    ///
464    /// # Errors
465    ///
466    /// - [`FabricError::CapacityExceeded`] if the index region is full or
467    ///   the heap cannot fit the serialized element.
468    pub fn push(&mut self, item: &T) -> Result<()> {
469        if self.len >= self.index_cap {
470            return Err(FabricError::CapacityExceeded);
471        }
472        let bytes = postcard::to_allocvec(item).map_err(|_| FabricError::IoError(-1))?;
473        if self.heap_used + bytes.len() as u64 > self.heap_size {
474            return Err(FabricError::CapacityExceeded);
475        }
476        // Write data to heap
477        let data_offset = self.heap_start + self.heap_used;
478        self.lease.mem().write(data_offset, &bytes)?;
479        // Write index entry
480        let idx_offset = VAR_HEADER_SIZE + self.len * INDEX_ENTRY_SIZE;
481        let mut entry = [0u8; INDEX_ENTRY_SIZE as usize];
482        entry[0..8].copy_from_slice(&self.heap_used.to_le_bytes());
483        entry[8..12].copy_from_slice(&(bytes.len() as u32).to_le_bytes());
484        self.lease.mem().write(idx_offset, &entry)?;
485
486        self.heap_used += bytes.len() as u64;
487        self.len += 1;
488        self.write_header()?;
489        Ok(())
490    }
491
492    /// Remove and return the last element, or `None` if empty.
493    ///
494    /// The heap space used by the popped element is reclaimed only if it
495    /// was the last item appended (heap_used is rewound).
496    pub fn pop(&mut self) -> Result<Option<T>> {
497        if self.len == 0 {
498            return Ok(None);
499        }
500        let item = self.get(self.len as usize - 1)?;
501        // Read the index entry to reclaim heap space
502        let idx_offset = VAR_HEADER_SIZE + (self.len - 1) * INDEX_ENTRY_SIZE;
503        let entry = self.lease.mem().read(idx_offset, INDEX_ENTRY_SIZE as u32)?;
504        let heap_offset = u64::from_le_bytes([
505            entry[0], entry[1], entry[2], entry[3], entry[4], entry[5], entry[6], entry[7],
506        ]);
507        let data_len = u32::from_le_bytes([entry[8], entry[9], entry[10], entry[11]]) as u64;
508        // Reclaim if this was at the end of the heap
509        if heap_offset + data_len == self.heap_used {
510            self.heap_used = heap_offset;
511        }
512        self.len -= 1;
513        self.write_header()?;
514        Ok(Some(item))
515    }
516
517    /// Read the element at `index`.
518    ///
519    /// Performs two remote reads: one for the index entry and one for the
520    /// heap data.
521    pub fn get(&self, index: usize) -> Result<T> {
522        if index as u64 >= self.len {
523            return Err(FabricError::CapacityExceeded);
524        }
525        let idx_offset = VAR_HEADER_SIZE + (index as u64) * INDEX_ENTRY_SIZE;
526        let entry = self.lease.mem().read(idx_offset, INDEX_ENTRY_SIZE as u32)?;
527        let heap_offset = u64::from_le_bytes([
528            entry[0], entry[1], entry[2], entry[3], entry[4], entry[5], entry[6], entry[7],
529        ]);
530        let data_len = u32::from_le_bytes([entry[8], entry[9], entry[10], entry[11]]);
531        let data = self
532            .lease
533            .mem()
534            .read(self.heap_start + heap_offset, data_len)?;
535        postcard::from_bytes(&data).map_err(|_| FabricError::IoError(-1))
536    }
537
538    /// Returns the number of elements.
539    pub fn len(&self) -> usize {
540        self.len as usize
541    }
542
543    /// Returns `true` if the vector is empty.
544    pub fn is_empty(&self) -> bool {
545        self.len == 0
546    }
547
548    /// Returns the maximum number of index entries.
549    pub fn index_capacity(&self) -> usize {
550        self.index_cap as usize
551    }
552
553    /// Returns the total heap space in bytes.
554    pub fn heap_size(&self) -> u64 {
555        self.heap_size
556    }
557
558    /// Returns the heap space used so far in bytes.
559    pub fn heap_used(&self) -> u64 {
560        self.heap_used
561    }
562
563    /// Clear the vector, resetting len and heap_used to 0.
564    pub fn clear(&mut self) -> Result<()> {
565        self.len = 0;
566        self.heap_used = 0;
567        self.write_header()
568    }
569
570    /// Returns the lease ID of the memory lease for external renewal
571    /// management (e.g. via [`grafos_leasekit::RenewalManager`]).
572    pub fn lease_id(&self) -> u128 {
573        self.lease.lease_id()
574    }
575
576    /// Returns the expiry time (unix seconds) of the memory lease for
577    /// external renewal management.
578    pub fn expires_at_unix_secs(&self) -> u64 {
579        self.lease.expires_at_unix_secs()
580    }
581
582    /// Returns an iterator over the elements.
583    pub fn iter(&self) -> VarFabricVecIter<'_, T> {
584        VarFabricVecIter {
585            vec: self,
586            index: 0,
587        }
588    }
589
590    fn write_header(&self) -> Result<()> {
591        let mut hdr = [0u8; VAR_HEADER_SIZE as usize];
592        hdr[0..8].copy_from_slice(&self.len.to_le_bytes());
593        hdr[8..16].copy_from_slice(&self.index_cap.to_le_bytes());
594        hdr[16..24].copy_from_slice(&self.heap_size.to_le_bytes());
595        hdr[24..32].copy_from_slice(&self.heap_used.to_le_bytes());
596        self.lease.mem().write(0, &hdr)
597    }
598}
599
600/// Iterator over elements of a [`VarFabricVec`].
601pub struct VarFabricVecIter<'a, T> {
602    vec: &'a VarFabricVec<T>,
603    index: usize,
604}
605
606impl<'a, T: Serialize + DeserializeOwned> Iterator for VarFabricVecIter<'a, T> {
607    type Item = Result<T>;
608
609    fn next(&mut self) -> Option<Self::Item> {
610        if self.index >= self.vec.len() {
611            return None;
612        }
613        let result = self.vec.get(self.index);
614        self.index += 1;
615        Some(result)
616    }
617
618    fn size_hint(&self) -> (usize, Option<usize>) {
619        let remaining = self.vec.len() - self.index;
620        (remaining, Some(remaining))
621    }
622}
623
624#[cfg(test)]
625mod tests {
626    use super::*;
627    use grafos_std::host;
628    use serde::{Deserialize, Serialize};
629
630    fn setup(arena_size: u64) -> MemLease {
631        host::reset_mock();
632        host::mock_set_fbmu_arena_size(arena_size);
633        MemBuilder::new().acquire().expect("acquire")
634    }
635
636    #[test]
637    fn push_pop_roundtrip() {
638        let lease = setup(4096);
639        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
640        assert!(v.is_empty());
641
642        v.push(&10).expect("push 10");
643        v.push(&20).expect("push 20");
644        v.push(&30).expect("push 30");
645        assert_eq!(v.len(), 3);
646
647        assert_eq!(v.pop().expect("pop"), Some(30));
648        assert_eq!(v.pop().expect("pop"), Some(20));
649        assert_eq!(v.pop().expect("pop"), Some(10));
650        assert_eq!(v.pop().expect("pop"), None);
651        assert!(v.is_empty());
652    }
653
654    #[test]
655    fn get_set() {
656        let lease = setup(4096);
657        let mut v: FabricVec<u64> = FabricVec::new(lease, 16).expect("new");
658
659        v.push(&100).expect("push");
660        v.push(&200).expect("push");
661        v.push(&300).expect("push");
662
663        assert_eq!(v.get(0).expect("get"), 100);
664        assert_eq!(v.get(1).expect("get"), 200);
665        assert_eq!(v.get(2).expect("get"), 300);
666
667        v.set(1, &999).expect("set");
668        assert_eq!(v.get(1).expect("get"), 999);
669    }
670
671    #[test]
672    fn get_out_of_bounds() {
673        let lease = setup(4096);
674        let v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
675        assert_eq!(v.get(0).unwrap_err(), FabricError::CapacityExceeded);
676    }
677
678    #[test]
679    fn iter_elements() {
680        let lease = setup(4096);
681        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
682        for i in 0..5u32 {
683            v.push(&i).expect("push");
684        }
685        let collected: alloc::vec::Vec<u32> = v.iter().map(|r| r.expect("iter")).collect();
686        assert_eq!(collected, alloc::vec![0, 1, 2, 3, 4]);
687    }
688
689    #[test]
690    fn vec_lease_accessors() {
691        let lease = setup(4096);
692        let v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
693        assert_ne!(v.lease_id(), 0);
694        assert!(v.expires_at_unix_secs() > 0);
695    }
696
697    #[test]
698    fn clear_resets_len() {
699        let lease = setup(4096);
700        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
701        v.push(&1).expect("push");
702        v.push(&2).expect("push");
703        assert_eq!(v.len(), 2);
704        v.clear().expect("clear");
705        assert_eq!(v.len(), 0);
706        assert!(v.is_empty());
707    }
708
709    #[test]
710    fn capacity_exceeded_when_grow_fails() {
711        // Arena = header(24) + 2 * stride(16) = 56 bytes
712        let lease = setup(56);
713        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
714        assert_eq!(v.capacity(), 2);
715
716        v.push(&1).expect("push 1");
717        v.push(&2).expect("push 2");
718        // Shrink mock arena so grow() cannot allocate
719        host::mock_set_fbmu_arena_size(0);
720        assert_eq!(v.push(&3).unwrap_err(), FabricError::CapacityExceeded);
721    }
722
723    #[test]
724    fn with_capacity_creates_appropriately_sized_vec() {
725        host::reset_mock();
726        host::mock_set_fbmu_arena_size(65536);
727
728        let v: FabricVec<u64> = FabricVec::with_capacity(100, 16).expect("with_capacity");
729        assert!(v.capacity() >= 100);
730    }
731
732    #[derive(Debug, PartialEq, Serialize, Deserialize)]
733    struct TestStruct {
734        name: alloc::string::String,
735        value: u64,
736    }
737
738    #[test]
739    fn variable_size_elements() {
740        let lease = setup(8192);
741        let mut v: FabricVec<TestStruct> = FabricVec::new(lease, 64).expect("new");
742
743        let a = TestStruct {
744            name: alloc::string::String::from("alpha"),
745            value: 42,
746        };
747        let b = TestStruct {
748            name: alloc::string::String::from("beta"),
749            value: 99,
750        };
751
752        v.push(&a).expect("push a");
753        v.push(&b).expect("push b");
754
755        assert_eq!(v.get(0).expect("get 0"), a);
756        assert_eq!(v.get(1).expect("get 1"), b);
757    }
758
759    #[test]
760    fn variable_size_elements_different_serialized_lengths() {
761        // Verify that elements with very different serialized sizes coexist
762        // correctly in the same FabricVec, as long as each fits in stride.
763        let lease = setup(8192);
764        let mut v: FabricVec<TestStruct> = FabricVec::new(lease, 128).expect("new");
765
766        let short = TestStruct {
767            name: alloc::string::String::from("x"),
768            value: 1,
769        };
770        let long = TestStruct {
771            name: alloc::string::String::from("a]relatively_long_name_for_testing_purposes"),
772            value: u64::MAX,
773        };
774        let empty = TestStruct {
775            name: alloc::string::String::new(),
776            value: 0,
777        };
778
779        v.push(&short).expect("push short");
780        v.push(&long).expect("push long");
781        v.push(&empty).expect("push empty");
782
783        assert_eq!(v.get(0).expect("get 0"), short);
784        assert_eq!(v.get(1).expect("get 1"), long);
785        assert_eq!(v.get(2).expect("get 2"), empty);
786
787        // Overwrite a short element with a longer one at the same index
788        v.set(0, &long).expect("set 0 to long");
789        assert_eq!(v.get(0).expect("get 0 after set"), long);
790    }
791
792    #[test]
793    fn variable_size_element_exceeds_stride_returns_error() {
794        // stride=16, length prefix uses 4 bytes, so max serialized size is
795        // stride (the check is bytes.len() > stride). A string whose postcard
796        // serialization exceeds stride should be rejected.
797        let lease = setup(4096);
798        let mut v: FabricVec<alloc::string::String> = FabricVec::new(lease, 16).expect("new");
799
800        // A 20-byte string will serialize to >16 bytes (1-byte varint length + 20 chars)
801        let big = alloc::string::String::from("12345678901234567890");
802        assert_eq!(v.push(&big).unwrap_err(), FabricError::CapacityExceeded);
803        assert!(v.is_empty());
804    }
805
806    #[test]
807    fn capacity_boundary_fill_then_pop_then_refill() {
808        // Arena exactly fits header(24) + 4 * stride(16) = 88 bytes → capacity 4.
809        // Fill completely, pop all, then refill to verify reuse.
810        let lease = setup(88);
811        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
812        assert_eq!(v.capacity(), 4);
813
814        // Fill to capacity
815        for i in 0..4u32 {
816            v.push(&(i * 10)).expect("push");
817        }
818        assert_eq!(v.len(), 4);
819        // Prevent grow so push fails
820        host::mock_set_fbmu_arena_size(0);
821        assert_eq!(v.push(&99).unwrap_err(), FabricError::CapacityExceeded);
822        // Restore arena for later allocations
823        host::mock_set_fbmu_arena_size(88);
824
825        // Drain fully
826        for i in (0..4u32).rev() {
827            assert_eq!(v.pop().expect("pop"), Some(i * 10));
828        }
829        assert!(v.is_empty());
830
831        // Refill with different values (len was reset, so primary region is reused)
832        for i in 100..104u32 {
833            v.push(&i).expect("re-push");
834        }
835        assert_eq!(v.len(), 4);
836        for (idx, expected) in (100..104u32).enumerate() {
837            assert_eq!(v.get(idx).expect("get"), expected);
838        }
839    }
840
841    #[test]
842    fn arena_too_small_for_header() {
843        // Arena smaller than HEADER_SIZE (24 bytes) should fail construction.
844        let lease = setup(16);
845        let result: core::result::Result<FabricVec<u32>, _> = FabricVec::new(lease, 16);
846        match result {
847            Err(FabricError::CapacityExceeded) => {}
848            other => panic!("expected CapacityExceeded, got {:?}", other.err()),
849        }
850    }
851
852    #[test]
853    fn set_out_of_bounds() {
854        let lease = setup(4096);
855        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
856        v.push(&42).expect("push");
857        // index 1 is out of bounds (len == 1)
858        assert_eq!(v.set(1, &99).unwrap_err(), FabricError::CapacityExceeded);
859        // index 0 is still valid
860        assert_eq!(v.get(0).expect("get"), 42);
861    }
862
863    #[test]
864    fn iter_size_hint() {
865        let lease = setup(4096);
866        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
867        for i in 0..5u32 {
868            v.push(&i).expect("push");
869        }
870        let mut iter = v.iter();
871        assert_eq!(iter.size_hint(), (5, Some(5)));
872        iter.next();
873        assert_eq!(iter.size_hint(), (4, Some(4)));
874    }
875
876    // --- FabricVec capacity growth tests ---
877
878    #[test]
879    fn grow_chains_new_lease() {
880        // Create a tiny arena that can hold 2 elements, then grow.
881        host::reset_mock();
882        host::mock_set_fbmu_arena_size(56); // header(24) + 2*stride(16) = 56
883        let lease = MemBuilder::new().acquire().expect("acquire");
884        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
885        assert_eq!(v.capacity(), 2);
886
887        v.push(&10).expect("push 10");
888        v.push(&20).expect("push 20");
889
890        // Now bump the mock arena so the next alloc succeeds
891        host::mock_set_fbmu_arena_size(56);
892        v.push(&30).expect("push 30 (triggers grow)");
893        assert!(v.capacity() >= 3);
894        assert_eq!(v.len(), 3);
895
896        assert_eq!(v.get(0).expect("get 0"), 10);
897        assert_eq!(v.get(1).expect("get 1"), 20);
898        assert_eq!(v.get(2).expect("get 2"), 30);
899    }
900
901    #[test]
902    fn grow_multiple_overflow_regions() {
903        host::reset_mock();
904        // Each region holds 2 elements (header=24 + 2*16=56)
905        host::mock_set_fbmu_arena_size(56);
906        let lease = MemBuilder::new().acquire().expect("acquire");
907        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
908        assert_eq!(v.capacity(), 2);
909
910        // Push 6 elements across 3 regions
911        for i in 0..6u32 {
912            v.push(&(i * 10)).expect("push");
913        }
914        assert_eq!(v.len(), 6);
915        assert!(v.capacity() >= 6);
916
917        // Verify all elements readable
918        for i in 0..6u32 {
919            assert_eq!(v.get(i as usize).expect("get"), i * 10);
920        }
921
922        // Pop all in reverse
923        for i in (0..6u32).rev() {
924            assert_eq!(v.pop().expect("pop"), Some(i * 10));
925        }
926        assert!(v.is_empty());
927    }
928
929    #[test]
930    fn grow_iter_spans_regions() {
931        host::reset_mock();
932        host::mock_set_fbmu_arena_size(56); // 2 elements per region
933        let lease = MemBuilder::new().acquire().expect("acquire");
934        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
935
936        for i in 0..5u32 {
937            v.push(&i).expect("push");
938        }
939        let collected: alloc::vec::Vec<u32> = v.iter().map(|r| r.expect("iter")).collect();
940        assert_eq!(collected, alloc::vec![0, 1, 2, 3, 4]);
941    }
942
943    #[test]
944    fn grow_set_in_overflow_region() {
945        host::reset_mock();
946        host::mock_set_fbmu_arena_size(56); // 2 per region
947        let lease = MemBuilder::new().acquire().expect("acquire");
948        let mut v: FabricVec<u32> = FabricVec::new(lease, 16).expect("new");
949
950        v.push(&1).expect("push");
951        v.push(&2).expect("push");
952        v.push(&3).expect("push"); // in overflow region
953
954        v.set(2, &99).expect("set in overflow");
955        assert_eq!(v.get(2).expect("get"), 99);
956    }
957
958    // --- FabricVec with String and Vec<u8> elements ---
959
960    #[test]
961    fn fabric_vec_string_elements() {
962        let lease = setup(8192);
963        let mut v: FabricVec<alloc::string::String> = FabricVec::new(lease, 64).expect("new");
964
965        let strings = alloc::vec![
966            alloc::string::String::from("hello"),
967            alloc::string::String::from("world"),
968            alloc::string::String::from(""),
969            alloc::string::String::from("a longer string with more characters"),
970        ];
971        for s in &strings {
972            v.push(s).expect("push");
973        }
974        for (i, s) in strings.iter().enumerate() {
975            assert_eq!(&v.get(i).expect("get"), s);
976        }
977    }
978
979    #[test]
980    fn fabric_vec_vec_u8_elements() {
981        let lease = setup(8192);
982        let mut v: FabricVec<alloc::vec::Vec<u8>> = FabricVec::new(lease, 64).expect("new");
983
984        let items: alloc::vec::Vec<alloc::vec::Vec<u8>> =
985            alloc::vec![alloc::vec![1, 2, 3], alloc::vec![], alloc::vec![255; 50],];
986        for item in &items {
987            v.push(item).expect("push");
988        }
989        for (i, item) in items.iter().enumerate() {
990            assert_eq!(&v.get(i).expect("get"), item);
991        }
992    }
993
994    // --- VarFabricVec tests ---
995
996    #[test]
997    fn var_vec_push_pop_roundtrip() {
998        let lease = setup(8192);
999        let mut v: VarFabricVec<alloc::string::String> =
1000            VarFabricVec::new(lease, 100).expect("new");
1001
1002        v.push(&alloc::string::String::from("short")).expect("push");
1003        v.push(&alloc::string::String::from(
1004            "a much longer string that takes more heap space",
1005        ))
1006        .expect("push");
1007        v.push(&alloc::string::String::from("")).expect("push");
1008        assert_eq!(v.len(), 3);
1009
1010        assert_eq!(
1011            v.get(0).expect("get 0"),
1012            alloc::string::String::from("short")
1013        );
1014        assert_eq!(
1015            v.get(1).expect("get 1"),
1016            alloc::string::String::from("a much longer string that takes more heap space")
1017        );
1018        assert_eq!(v.get(2).expect("get 2"), alloc::string::String::from(""));
1019
1020        assert_eq!(v.pop().expect("pop"), Some(alloc::string::String::from("")));
1021        assert_eq!(v.len(), 2);
1022    }
1023
1024    #[test]
1025    fn var_vec_mixed_sizes_efficient() {
1026        let lease = setup(8192);
1027        let mut v: VarFabricVec<alloc::vec::Vec<u8>> = VarFabricVec::new(lease, 100).expect("new");
1028
1029        // Push items of very different sizes
1030        v.push(&alloc::vec![42u8]).expect("push tiny");
1031        v.push(&alloc::vec![0u8; 100]).expect("push medium");
1032        v.push(&alloc::vec![255u8; 500]).expect("push large");
1033
1034        assert_eq!(v.len(), 3);
1035        assert_eq!(v.get(0).expect("get 0"), alloc::vec![42u8]);
1036        assert_eq!(v.get(1).expect("get 1"), alloc::vec![0u8; 100]);
1037        assert_eq!(v.get(2).expect("get 2"), alloc::vec![255u8; 500]);
1038
1039        // Heap usage should be roughly the sum of serialized sizes, not
1040        // 3 * max_stride.
1041        assert!(v.heap_used() < 1000);
1042    }
1043
1044    #[test]
1045    fn var_vec_with_capacity() {
1046        host::reset_mock();
1047        host::mock_set_fbmu_arena_size(65536);
1048        let v: VarFabricVec<alloc::string::String> =
1049            VarFabricVec::with_capacity(50, 32).expect("with_capacity");
1050        assert_eq!(v.index_capacity(), 50);
1051        assert!(v.heap_size() > 0);
1052    }
1053
1054    #[test]
1055    fn var_vec_clear() {
1056        let lease = setup(8192);
1057        let mut v: VarFabricVec<u32> = VarFabricVec::new(lease, 100).expect("new");
1058        v.push(&1).expect("push");
1059        v.push(&2).expect("push");
1060        v.clear().expect("clear");
1061        assert!(v.is_empty());
1062        assert_eq!(v.heap_used(), 0);
1063    }
1064
1065    #[test]
1066    fn var_vec_iter() {
1067        let lease = setup(8192);
1068        let mut v: VarFabricVec<u32> = VarFabricVec::new(lease, 100).expect("new");
1069        for i in 0..5u32 {
1070            v.push(&(i * 10)).expect("push");
1071        }
1072        let collected: alloc::vec::Vec<u32> = v.iter().map(|r| r.expect("iter")).collect();
1073        assert_eq!(collected, alloc::vec![0, 10, 20, 30, 40]);
1074    }
1075
1076    #[test]
1077    fn var_vec_heap_full_returns_error() {
1078        // Create a small arena where the heap fills up before the index.
1079        // header(32) + 10 * index_entry(12) = 152 bytes for index
1080        // With a 256-byte arena, heap = 256 - 152 = 104 bytes
1081        let lease = setup(256);
1082        let mut v: VarFabricVec<alloc::vec::Vec<u8>> = VarFabricVec::new(lease, 10).expect("new");
1083        // Each Vec<u8> of 60 bytes serializes to ~62 bytes (varint len prefix + data)
1084        v.push(&alloc::vec![0u8; 60]).expect("push 1");
1085        // Second push should fail — not enough heap
1086        let result = v.push(&alloc::vec![0u8; 60]);
1087        assert_eq!(result.unwrap_err(), FabricError::CapacityExceeded);
1088        assert_eq!(v.len(), 1);
1089    }
1090
1091    #[test]
1092    fn var_vec_index_full_returns_error() {
1093        // header(32) + 2 * index_entry(12) = 56, plus heap. With a small
1094        // index_cap=2, the third push should fail.
1095        let lease = setup(4096);
1096        let mut v: VarFabricVec<u32> = VarFabricVec::new(lease, 2).expect("new");
1097        v.push(&1).expect("push 1");
1098        v.push(&2).expect("push 2");
1099        assert_eq!(v.push(&3).unwrap_err(), FabricError::CapacityExceeded);
1100    }
1101
1102    #[test]
1103    fn var_vec_pop_reclaims_heap() {
1104        let lease = setup(8192);
1105        let mut v: VarFabricVec<alloc::string::String> =
1106            VarFabricVec::new(lease, 100).expect("new");
1107        v.push(&alloc::string::String::from("first")).expect("push");
1108        let after_first = v.heap_used();
1109        v.push(&alloc::string::String::from("second"))
1110            .expect("push");
1111        let after_second = v.heap_used();
1112        assert!(after_second > after_first);
1113
1114        // Pop reclaims the last element's heap
1115        v.pop().expect("pop");
1116        assert_eq!(v.heap_used(), after_first);
1117    }
1118}