Skip to main content

columnar/
bytes.rs

1//! Logic related to the transformation to and from bytes.
2//!
3//! The methods here line up with the `AsBytes` and `FromBytes` traits.
4//!
5//! The encoding uses an index of byte offsets prepended to the data, enabling
6//! random access to individual byte slices and `u64`-aligned decoding.
7//!
8//! The most reliable entry point to the read side of this functionality is the `Stash` type,
9//! which can be formed from any type that implements `Deref<Target=[u8]>`. Doing so will check
10//! `u64` alignment, copy the contents if misaligned, and perform some structural validation.
11
12/// A trait for writing bytes, usable in `no_std` environments.
13///
14/// This replaces `std::io::Write` for the columnar encoding functions.
15/// Implementations exist for `Vec<u8>` (always) and `std::io::Write` (with the `std` feature).
16pub trait WriteBytes {
17    /// The error type returned by write operations.
18    type Error;
19    /// Write all bytes from the slice, or return an error.
20    fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error>;
21}
22
23#[cfg(feature = "std")]
24impl<W: std::io::Write> WriteBytes for W {
25    type Error = std::io::Error;
26    #[inline(always)]
27    fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
28        std::io::Write::write_all(self, bytes)
29    }
30}
31
32#[cfg(not(feature = "std"))]
33impl WriteBytes for alloc::vec::Vec<u8> {
34    type Error = core::convert::Infallible;
35    #[inline(always)]
36    fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
37        self.extend_from_slice(bytes);
38        Ok(())
39    }
40}
41
42
43/// A binary encoding of sequences of byte slices.
44///
45/// The encoding starts with a sequence of n+1 offsets describing where to find the n slices in the bytes that follow.
46/// Treating the offsets as a byte slice too, each offset indicates the location (in bytes) of the end of its slice.
47/// Each byte slice can be found from a pair of adjacent offsets, where the first is rounded up to a multiple of eight.
48/// This means that slices that are not multiples of eight bytes may leave unread bytes at their end, which is fine.
49pub mod indexed {
50
51    use alloc::{vec::Vec, string::String};
52    use crate::AsBytes;
53
54    /// Encoded length in number of `u64` words required.
55    pub fn length_in_words<'a, A>(bytes: &A) -> usize where A : AsBytes<'a> {
56        1 + bytes.as_bytes().map(|(_align, bytes)| 1 + bytes.len().div_ceil(8)).sum::<usize>()
57    }
58    /// Encoded length in number of `u8` bytes required.
59    pub fn length_in_bytes<'a, A>(bytes: &A) -> usize where A : AsBytes<'a> { 8 * length_in_words(bytes) }
60
61    /// Encodes `item` into `u64` aligned words.
62    ///
63    /// The sequence of byte slices are appended, with padding to have each slice start `u64` aligned.
64    /// The sequence is then pre-pended with as many byte offsets as there are slices in `item`, plus one.
65    /// The byte offsets indicate where each slice ends, and by rounding up to `u64` alignemnt where the next slice begins.
66    /// The first offset indicates where the list of offsets itself ends, and where the first slice begins.
67    ///
68    /// We will need to visit `as_bytes` three times to extract this information, so the method should be efficient and inlined.
69    /// The first read writes the first offset, the second writes each other offset, and the third writes the bytes themselves.
70    ///
71    /// The offsets are zero-based, rather than based on `store.len()`.
72    /// If you call the method with a non-empty `store` be careful decoding.
73    pub fn encode<'a, A>(store: &mut Vec<u64>, iter: &A)
74    where A : AsBytes<'a>,
75    {
76        // Read 1: Number of offsets we will record, equal to the number of slices plus one.
77        // TODO: right-size `store` before first call to `push`.
78        let offsets = 1 + iter.as_bytes().count();
79        let offsets_end: u64 = TryInto::<u64>::try_into((offsets) * core::mem::size_of::<u64>()).unwrap();
80        store.push(offsets_end);
81        // Read 2: Establish each of the offsets based on lengths of byte slices.
82        let mut position_bytes = offsets_end;
83        for (align, bytes) in iter.as_bytes() {
84            assert!(align <= 8);
85            // Write length in bytes, but round up to words before updating `position_bytes`.
86            let to_push: u64 = position_bytes + TryInto::<u64>::try_into(bytes.len()).unwrap();
87            store.push(to_push);
88            let round_len: u64 = ((bytes.len() + 7) & !7).try_into().unwrap();
89            position_bytes += round_len;
90        }
91        // Read 3: Append each byte slice, with padding to align starts to `u64`.
92        for (_align, bytes) in iter.as_bytes() {
93            let whole_words = 8 * (bytes.len() / 8);
94            // We want to extend `store` by `bytes`, but `bytes` may not be `u64` aligned.
95            // In the latter case, init `store` and cast and copy onto it as a byte slice.
96            if let Ok(words) = bytemuck::try_cast_slice(&bytes[.. whole_words]) {
97                store.extend_from_slice(words);
98            }
99            else {
100                let store_len = store.len();
101                store.resize(store_len + whole_words/8, 0);
102                let slice = bytemuck::try_cast_slice_mut(&mut store[store_len..]).expect("&[u64] should convert to &[u8]");
103                slice.copy_from_slice(&bytes[.. whole_words]);
104            }
105            let remaining_bytes = &bytes[whole_words..];
106            if !remaining_bytes.is_empty() {
107                let mut remainder = 0u64;
108                let transmute: &mut [u8] = bytemuck::try_cast_slice_mut(core::slice::from_mut(&mut remainder)).expect("&[u64] should convert to &[u8]");
109                for (i, byte) in remaining_bytes.iter().enumerate() {
110                    transmute[i] = *byte;
111                }
112                store.push(remainder);
113            }
114        }
115    }
116
117    pub fn write<'a, A, W>(writer: &mut W, iter: &A) -> Result<(), W::Error>
118    where
119        A: AsBytes<'a>,
120        W: super::WriteBytes,
121    {
122        // Read 1: Number of offsets we will record, equal to the number of slices plus one.
123        let offsets = 1 + iter.as_bytes().count();
124        let offsets_end: u64 = TryInto::<u64>::try_into((offsets) * core::mem::size_of::<u64>()).unwrap();
125        writer.write_all(bytemuck::cast_slice(core::slice::from_ref(&offsets_end)))?;
126        // Read 2: Establish each of the offsets based on lengths of byte slices.
127        let mut position_bytes = offsets_end;
128        for (align, bytes) in iter.as_bytes() {
129            assert!(align <= 8);
130            // Write length in bytes, but round up to words before updating `position_bytes`.
131            let to_push: u64 = position_bytes + TryInto::<u64>::try_into(bytes.len()).unwrap();
132            writer.write_all(bytemuck::cast_slice(core::slice::from_ref(&to_push)))?;
133            let round_len: u64 = ((bytes.len() + 7) & !7).try_into().unwrap();
134            position_bytes += round_len;
135        }
136        // Read 3: Append each byte slice, with padding to align starts to `u64`.
137        for (_align, bytes) in iter.as_bytes() {
138            writer.write_all(bytes)?;
139            let padding = ((bytes.len() + 7) & !7) - bytes.len();
140            if padding > 0 {
141                writer.write_all(&[0u8;8][..padding])?;
142            }
143        }
144
145        Ok(())
146    }
147
148    /// Decodes an encoded sequence of byte slices. Each result will be `u64` aligned.
149    #[inline(always)]
150    pub fn decode(store: &[u64]) -> impl Iterator<Item=&[u8]> {
151        let slices = store[0] as usize / 8 - 1;
152        let index = &store[..slices + 1];
153        let last = index[slices] as usize;
154        let bytes: &[u8] = &bytemuck::cast_slice(store)[..last];
155        (0 .. slices).map(move |i| {
156            let upper = (index[i + 1] as usize).min(last);
157            let lower = (((index[i] as usize) + 7) & !7).min(upper);
158            &bytes[lower .. upper]
159        })
160    }
161
162
163    /// A zero-allocation view into indexed-encoded data, providing random access to individual slices.
164    ///
165    /// Constructed from `&[u64]` in O(1), this wraps the offset index and data region
166    /// and provides `get(k)` to retrieve the k-th slice as `(&[u64], u8)`.
167    /// Each access is independent — no iterator state — enabling LLVM to eliminate
168    /// unused field lookups entirely.
169    #[derive(Copy, Clone)]
170    pub struct DecodedStore<'a> {
171        /// The offset index: `index[0]` is the byte offset where data starts,
172        /// `index[k+1]` is the byte offset where slice k ends.
173        index: &'a [u64],
174        /// The data region, pre-sliced to include only valid words.
175        words: &'a [u64],
176    }
177
178    impl<'a> DecodedStore<'a> {
179        /// Creates a decoded view of an indexed-encoded `&[u64]` store.
180        ///
181        /// This is O(1) — it just reads the first offset to locate the index
182        /// and data regions. No allocation, no iteration.
183        #[inline(always)]
184        pub fn new(store: &'a [u64]) -> Self {
185            let slices = store.first().copied().unwrap_or(0) as usize / 8;
186            debug_assert!(slices <= store.len(), "DecodedStore::new: slice count {slices} exceeds store length {}", store.len());
187            let index = store.get(..slices).unwrap_or(&[]);
188            let last = index.last().copied().unwrap_or(0) as usize;
189            let last_w = (last + 7) / 8;
190            debug_assert!(last_w <= store.len(), "DecodedStore::new: last word offset {last_w} exceeds store length {}", store.len());
191            let words = store.get(..last_w).unwrap_or(&[]);
192            Self { index, words }
193        }
194        /// Returns the k-th slice as `(&[u64], u8)`.
195        ///
196        /// The `u8` is the number of valid trailing bytes in the last word
197        /// (0 means all 8 are valid). Returns an empty slice for out-of-bounds access.
198        #[inline(always)]
199        pub fn get(&self, k: usize) -> (&'a [u64], u8) {
200            debug_assert!(k + 1 < self.index.len(), "DecodedStore::get: index {k} out of bounds (len {})", self.index.len().saturating_sub(1));
201            let upper = (*self.index.get(k + 1).unwrap_or(&0) as usize)
202                .min(self.words.len() * 8);
203            let lower = (((*self.index.get(k).unwrap_or(&0) as usize) + 7) & !7)
204                .min(upper);
205            let upper_w = ((upper + 7) / 8).min(self.words.len());
206            let lower_w = (lower / 8).min(upper_w);
207            let tail = (upper % 8) as u8;
208            (self.words.get(lower_w..upper_w).unwrap_or(&[]), tail)
209        }
210        /// The number of slices in the store.
211        #[inline(always)]
212        pub fn len(&self) -> usize {
213            self.index.len().saturating_sub(1)
214        }
215    }
216
217    /// Validates the internal structure of indexed-encoded data.
218    ///
219    /// Checks that offsets are well-formed, in bounds, and that the slice count matches
220    /// `expected_slices`. This is a building block for [`validate`]; prefer calling
221    /// `validate` directly unless you need structural checks alone.
222    pub fn validate_structure(store: &[u64], expected_slices: usize) -> Result<(), String> {
223        if store.is_empty() {
224            return Err("store is empty".into());
225        }
226        let first = store[0] as usize;
227        if first % 8 != 0 {
228            return Err(format!("first offset {} is not a multiple of 8", first));
229        }
230        let slices = first / 8 - 1;
231        if slices + 1 > store.len() {
232            return Err(format!("index requires {} words but store has {}", slices + 1, store.len()));
233        }
234        if slices != expected_slices {
235            return Err(format!("expected {} slices but found {}", expected_slices, slices));
236        }
237        let store_bytes = store.len() * 8;
238        let mut prev_upper = first;
239        for i in 0..slices {
240            let offset = store[i + 1] as usize;
241            if offset > store_bytes {
242                return Err(format!("slice {} offset {} exceeds store size {}", i, offset, store_bytes));
243            }
244            if offset < prev_upper {
245                return Err(format!("slice {} offset {} precedes previous end {}", i, offset, prev_upper));
246            }
247            // Advance prev_upper to the aligned start of the next slice.
248            prev_upper = (offset + 7) & !7;
249        }
250        Ok(())
251    }
252
253    /// Validates that `store` contains well-formed data compatible with type `T`.
254    ///
255    /// Checks both the internal structure of the encoding (offsets, slice count) and
256    /// type-level compatibility (each slice's byte length is a multiple of its element
257    /// size). Call this once at trust boundaries when receiving encoded data.
258    ///
259    /// The `from_store` decode path performs no further validation at access time:
260    /// it will not panic on malformed data, but may return incorrect results.
261    /// There is no undefined behavior in any case. Call this method once before
262    /// using `from_store` to ensure the data is well-formed.
263    ///
264    /// ```ignore
265    /// type B<'a> = <MyContainer as Borrow>::Borrowed<'a>;
266    /// indexed::validate::<B>(&store)?;
267    /// // Now safe to use the non-panicking path:
268    /// let ds = indexed::DecodedStore::new(&store);
269    /// let borrowed = B::from_store(&ds, &mut 0);
270    /// ```
271    pub fn validate<'a, T: crate::FromBytes<'a>>(store: &[u64]) -> Result<(), String> {
272        validate_structure(store, T::SLICE_COUNT)?;
273        let ds = DecodedStore::new(store);
274        let slices: Vec<_> = (0..ds.len()).map(|i| ds.get(i)).collect();
275        T::validate(&slices)
276    }
277
278    /// Decodes a specific byte slice by index. It will be `u64` aligned.
279    #[inline(always)]
280    pub fn decode_index(store: &[u64], index: u64) -> &[u8] {
281        let index = index as usize;
282        let bytes: &[u8] = bytemuck::cast_slice(store);
283        let upper = (store[index + 1] as usize).min(bytes.len());
284        let lower = (((store[index] as usize) + 7) & !7).min(upper);
285        &bytes[lower .. upper]
286    }
287
288    #[cfg(test)]
289    mod test {
290
291        use alloc::{vec, vec::Vec, string::String};
292        use crate::{Borrow, ContainerOf};
293        use crate::common::Push;
294        use crate::AsBytes;
295
296        use super::{encode, decode};
297
298        fn assert_roundtrip<'a, AB: AsBytes<'a>>(item: &AB) {
299            let mut store = Vec::new();
300            encode(&mut store, item);
301            assert!(item.as_bytes().map(|x| x.1).eq(decode(&store)));
302        }
303
304        #[test]
305        fn round_trip() {
306
307            let mut column: ContainerOf<Result<u64, String>> = Default::default();
308            for i in 0..10000u64 {
309                column.push(&Ok::<u64, String>(i));
310                column.push(&Err::<u64, String>(format!("{:?}", i)));
311            }
312
313            assert_roundtrip(&column.borrow());
314        }
315
316        #[test]
317        fn validate_well_formed() {
318            use crate::common::Push;
319
320            let mut column: ContainerOf<(u64, u64, u64)> = Default::default();
321            for i in 0..100u64 { column.push(&(i, i+1, i+2)); }
322            let mut store = Vec::new();
323            encode(&mut store, &column.borrow());
324
325            type B<'a> = crate::BorrowedOf<'a, (u64, u64, u64)>;
326            assert!(super::validate::<B>(&store).is_ok());
327
328            // Wrong slice count should fail structural validation.
329            assert!(super::validate_structure(&store, 5).is_err());
330        }
331
332        #[test]
333        fn validate_mixed_types() {
334            use crate::common::Push;
335
336            let mut column: ContainerOf<(u64, String, Vec<u32>)> = Default::default();
337            for i in 0..50u64 {
338                column.push(&(i, format!("hello {i}"), vec![i as u32; i as usize]));
339            }
340            let mut store = Vec::new();
341            encode(&mut store, &column.borrow());
342
343            type B<'a> = crate::BorrowedOf<'a, (u64, String, Vec<u32>)>;
344            assert!(super::validate::<B>(&store).is_ok());
345        }
346    }
347}
348
349/// A container of either typed columns, or serialized bytes that can be borrowed as the former.
350pub mod stash {
351
352    use alloc::{vec::Vec, string::String};
353    use crate::{Len, FromBytes};
354    /// A container of either typed columns, or serialized bytes that can be borrowed as the former.
355    ///
356    /// When `B` dereferences to a byte slice, the container can be borrowed as if the container type `C`.
357    /// This container inherents the readable properties of `C` through borrowing, but does not implement
358    /// the traits itself.
359    ///
360    /// The container can be cleared and pushed into. When cleared it reverts to a typed variant, and when
361    /// pushed into if the typed variant it will accept the item, and if not it will panic.
362    ///
363    /// The best ways to construct a `Stash` is with either the `Default` implementation to get an empty
364    /// writeable version, or with the `try_from_bytes` method that attempts to install a type that dereferences
365    /// to a byte slice in the `Bytes` variant, after validating some structural properties.
366    ///
367    /// One can form a `Stash` directly by loading the variants, which are public. Do so with care,
368    /// as loading mis-aligned `B` into the `Bytes` variant can result in a run-time panic, and
369    /// loading structurally invalid data into either the `Bytes` or `Align` variant can produce
370    /// incorrect results at runtime (clamped index accesses, for example). The validation does not
371    /// confirm that the internal structure of types are valid, for example that all vector bounds
372    /// are in-bounds for their values, and these may result in panics at runtime for invalid data.
373    #[derive(Clone)]
374    pub enum Stash<C, B> {
375        /// The typed variant of the container.
376        Typed(C),
377        /// The bytes variant of the container.
378        Bytes(B),
379        /// Relocated, aligned binary data, if `Bytes` doesn't work for some reason.
380        ///
381        /// Most commonly this works around misaligned binary data, but it can also be useful if the `B`
382        /// type is a scarce resource that should be released.
383        Align(alloc::sync::Arc<[u64]>),
384    }
385
386    impl<C: Default, B> Default for Stash<C, B> { fn default() -> Self { Self::Typed(Default::default()) } }
387
388    impl<C: crate::ContainerBytes, B: core::ops::Deref<Target = [u8]>> Stash<C, B> {
389        /// An analogue of `TryFrom` for any `B: Deref<Target=[u8]>`, avoiding coherence issues.
390        ///
391        /// This is the recommended way to form a `Stash`, as it performs certain structural validation
392        /// steps that the stash will then skip in future borrowing and indexing operations. If the data
393        /// are structurally invalid, e.g. the wrong framing header, the wrong number of slices for `C`,
394        /// this will return an error. If this returns a `Stash` then all accesses that do not panic should
395        /// be correct. The resulting `Stash` may still panic if the internal structure of the data
396        /// are inconsistent, for example if any vector bounds are out-of-bounds for their values slice.
397        ///
398        /// There is no `unsafe` that is called through this type, and invalid data can result in panics
399        /// or incorrect results, but not undefined behavior.
400        ///
401        /// # Example
402        ///
403        /// ```rust
404        /// use columnar::{Columnar, Borrow, ContainerOf};
405        /// use columnar::common::{Push, Index};
406        /// use columnar::bytes::stash::Stash;
407        ///
408        /// // Build a typed container and populate it.
409        /// let mut stash: Stash<ContainerOf<(u64, String)>, Vec<u8>> = Default::default();
410        /// stash.push(&(0u64, format!("hello")));
411        /// stash.push(&(1u64, format!("world")));
412        ///
413        /// // Serialize to bytes.
414        /// let mut bytes: Vec<u8> = Vec::new();
415        /// stash.write_bytes(&mut bytes);
416        ///
417        /// // Reconstruct from bytes, with validation.
418        /// let stash: Stash<ContainerOf<(u64, String)>, Vec<u8>> =
419        ///     Stash::try_from_bytes(bytes).expect("valid data");
420        ///
421        /// // Borrow and index into individual columns.
422        /// let borrowed = stash.borrow();
423        /// assert_eq!(*Index::get(&borrowed.0, 0), 0u64);
424        /// assert_eq!(borrowed.1.get(1), b"world");
425        /// ```
426        pub fn try_from_bytes(bytes: B) -> Result<Self, String> {
427            use crate::bytes::indexed::validate;
428            use crate::Borrow;
429            if !(bytes.len() % 8 == 0) { return Err(format!("bytes.len() = {:?} not a multiple of 8", bytes.len())) }
430            if let Ok(words) = bytemuck::try_cast_slice::<_, u64>(&bytes) {
431                validate::<<C as Borrow>::Borrowed<'_>>(words)?;
432                Ok(Self::Bytes(bytes))
433            }
434            else {
435                // Re-locating bytes for alignment reasons.
436                let mut alloc: Vec<u64> = vec![0; bytes.len() / 8];
437                bytemuck::cast_slice_mut(&mut alloc[..]).copy_from_slice(&bytes[..]);
438                validate::<<C as Borrow>::Borrowed<'_>>(&alloc)?;
439                Ok(Self::Align(alloc.into()))
440            }
441        }
442    }
443
444    impl<C: crate::ContainerBytes, B: core::ops::Deref<Target=[u8]> + Clone + 'static> crate::Borrow for Stash<C, B> {
445
446        type Ref<'a> = <C as crate::Borrow>::Ref<'a>;
447        type Borrowed<'a> = <C as crate::Borrow>::Borrowed<'a>;
448
449        #[inline(always)] fn borrow<'a>(&'a self) -> Self::Borrowed<'a> { self.borrow() }
450        #[inline(always)] fn reborrow<'b, 'a: 'b>(item: Self::Borrowed<'a>) -> Self::Borrowed<'b> where Self: 'a { <C as crate::Borrow>::reborrow(item) }
451        #[inline(always)] fn reborrow_ref<'b, 'a: 'b>(item: Self::Ref<'a>) -> Self::Ref<'b> where Self: 'a { <C as crate::Borrow>::reborrow_ref(item) }
452    }
453
454    impl<C: crate::ContainerBytes, B: core::ops::Deref<Target=[u8]>> Len for Stash<C, B> {
455        #[inline(always)] fn len(&self) -> usize { self.borrow().len() }
456    }
457
458    impl<C: crate::Container + crate::ContainerBytes, B: core::ops::Deref<Target=[u8]>> Stash<C, B> {
459        /// Converts the stash to the `Typed` variant, by copying the borrowed data into a new container.
460        pub fn to_typed(&self) -> Self {
461            let borrowed = self.borrow();
462            let len = borrowed.len();
463            let mut container = C::with_capacity_for(core::iter::once(borrowed));
464            container.extend_from_self(borrowed, 0..len);
465            Self::Typed(container)
466        }
467        /// Converts the stash to the `Align` variant, by serializing the borrowed data into aligned words.
468        pub fn to_aligned(&self) -> Self {
469            let borrowed = self.borrow();
470            let mut store = Vec::with_capacity(crate::bytes::indexed::length_in_words(&borrowed));
471            crate::bytes::indexed::encode(&mut store, &borrowed);
472            Self::Align(store.into())
473        }
474        /// Ensures the stash is in the `Typed` variant, converting in place if needed, and returns a mutable reference.
475        pub fn make_typed(&mut self) -> &mut C {
476            if !matches!(self, Self::Typed(_)) {
477                *self = self.to_typed();
478            }
479            match self {
480                Stash::Typed(t) => t,
481                _ => unreachable!(),
482            }
483        }
484        /// Ensures the stash is in the `Align` variant, converting in place if needed, and returns a reference.
485        pub fn make_aligned(&mut self) -> &alloc::sync::Arc<[u64]> {
486            if !matches!(self, Self::Align(_)) {
487                *self = self.to_aligned();
488            }
489            match self {
490                Stash::Align(a) => a,
491                _ => unreachable!(),
492            }
493        }
494    }
495
496    impl<C: crate::ContainerBytes, B: core::ops::Deref<Target=[u8]>> Stash<C, B> {
497        /// Borrows the contents, either from a typed container or by decoding serialized bytes.
498        ///
499        /// This method is relatively cheap but is not free.
500        #[inline(always)] pub fn borrow<'a>(&'a self) -> <C as crate::Borrow>::Borrowed<'a> {
501            match self {
502                Stash::Typed(t) => t.borrow(),
503                Stash::Bytes(b) => {
504                    let store = crate::bytes::indexed::DecodedStore::new(bytemuck::cast_slice(b));
505                    <C::Borrowed<'_> as FromBytes>::from_store(&store, &mut 0)
506                },
507                Stash::Align(a) => {
508                    let store = crate::bytes::indexed::DecodedStore::new(a);
509                    <C::Borrowed<'_> as FromBytes>::from_store(&store, &mut 0)
510                },
511            }
512        }
513        /// The number of bytes needed to write the contents using the [`indexed`] encoder.
514        pub fn length_in_bytes(&self) -> usize {
515            match self {
516                // We'll need one u64 for the length, then the length rounded up to a multiple of 8.
517                Stash::Typed(t) => crate::bytes::indexed::length_in_bytes(&t.borrow()),
518                Stash::Bytes(b) => b.len(),
519                Stash::Align(a) => 8 * a.len(),
520            }
521        }
522        /// Write the contents into a [`WriteBytes`](crate::bytes::WriteBytes) destination.
523        pub fn write_bytes<W: crate::bytes::WriteBytes>(&self, writer: &mut W) -> Result<(), W::Error> {
524            match self {
525                Stash::Typed(t) => { crate::bytes::indexed::write(writer, &t.borrow())?; },
526                Stash::Bytes(b) => writer.write_all(&b[..])?,
527                Stash::Align(a) => writer.write_all(bytemuck::cast_slice(&a[..]))?,
528            }
529            Ok(())
530        }
531    }
532
533    // This implementation converts to owned data if it is not already, which can be expensive.
534    impl<T, C: crate::Container + crate::ContainerBytes + crate::Push<T>, B: core::ops::Deref<Target=[u8]>> crate::Push<T> for Stash<C, B> {
535        fn push(&mut self, item: T) {
536            self.make_typed();
537            match self {
538                Stash::Typed(t) => t.push(item),
539                _ => unreachable!(),
540            }
541        }
542    }
543
544    impl<C: crate::Clear + Default, B> crate::Clear for Stash<C, B> {
545        fn clear(&mut self) {
546            match self {
547                Stash::Typed(t) => t.clear(),
548                Stash::Bytes(_) | Stash::Align(_) => {
549                    *self = Stash::Typed(Default::default());
550                }
551            }
552        }
553    }
554}
555
556#[cfg(test)]
557mod test {
558    use crate::ContainerOf;
559    use alloc::{vec, vec::Vec, string::{String, ToString}};
560
561    #[test]
562    fn round_trip() {
563
564        use crate::common::{Push, Len, Index};
565        use crate::{Borrow, AsBytes, FromBytes};
566
567        let mut column: ContainerOf<Result<u64, u64>> = Default::default();
568        for i in 0..100u64 {
569            column.push(Ok::<u64, u64>(i));
570            column.push(Err::<u64, u64>(i));
571        }
572
573        assert_eq!(column.len(), 200);
574
575        for i in 0..100 {
576            assert_eq!(column.get(2*i+0), Ok(i as u64));
577            assert_eq!(column.get(2*i+1), Err(i as u64));
578        }
579
580        let column2 = crate::Results::<&[u64], &[u64], &[u64], &[u64], &[u64]>::from_bytes(&mut column.borrow().as_bytes().map(|(_, bytes)| bytes));
581        for i in 0..100 {
582            assert_eq!(column.get(2*i+0), column2.get(2*i+0).copied().map_err(|e| *e));
583            assert_eq!(column.get(2*i+1), column2.get(2*i+1).copied().map_err(|e| *e));
584        }
585
586        let column3 = crate::Results::<&[u64], &[u64], &[u64], &[u64], &[u64]>::from_bytes(&mut column2.as_bytes().map(|(_, bytes)| bytes));
587        for i in 0..100 {
588            assert_eq!(column3.get(2*i+0), column2.get(2*i+0));
589            assert_eq!(column3.get(2*i+1), column2.get(2*i+1));
590        }
591
592        // Test from_store round-trip.
593        let mut store = Vec::new();
594        crate::bytes::indexed::encode(&mut store, &column.borrow());
595        let ds = crate::bytes::indexed::DecodedStore::new(&store);
596        let column4 = crate::Results::<&[u64], &[u64], &[u64], &[u64], &[u64]>::from_store(&ds, &mut 0);
597        for i in 0..100 {
598            assert_eq!(column.get(2*i+0), column4.get(2*i+0).copied().map_err(|e| *e));
599            assert_eq!(column.get(2*i+1), column4.get(2*i+1).copied().map_err(|e| *e));
600        }
601    }
602
603    /// Test that try_from_bytes works for Result, Option, and derived enum types.
604    #[test]
605    fn validate_sum_types() {
606        use crate::common::{Push, Index};
607        use crate::{Borrow, ContainerOf};
608        use crate::bytes::stash::Stash;
609
610        // Result<u64, u64>
611        let mut c: ContainerOf<Result<u64, u64>> = Default::default();
612        for i in 0..100u64 {
613            c.push(Ok::<u64, u64>(i));
614            c.push(Err::<u64, u64>(i));
615        }
616        let mut bytes: Vec<u8> = Vec::new();
617        crate::bytes::indexed::write(&mut bytes, &c.borrow()).unwrap();
618        let stash: Stash<ContainerOf<Result<u64, u64>>, Vec<u8>> =
619            Stash::try_from_bytes(bytes).expect("Result<u64, u64> should validate");
620        assert_eq!(stash.borrow().get(0), Ok(&0u64));
621        assert_eq!(stash.borrow().get(1), Err(&0u64));
622
623        // Option<String>
624        let mut c: ContainerOf<Option<String>> = Default::default();
625        c.push(&Some("hello".to_string()));
626        c.push(&None::<String>);
627        c.push(&Some("world".to_string()));
628        let mut bytes: Vec<u8> = Vec::new();
629        crate::bytes::indexed::write(&mut bytes, &c.borrow()).unwrap();
630        let stash: Stash<ContainerOf<Option<String>>, Vec<u8>> =
631            Stash::try_from_bytes(bytes).expect("Option<String> should validate");
632        assert_eq!(stash.borrow().get(0), Some(&b"hello"[..]));
633        assert_eq!(stash.borrow().get(1), None);
634        assert_eq!(stash.borrow().get(2), Some(&b"world"[..]));
635
636        // Result<(u64, String), u64>
637        let mut c: ContainerOf<Result<(u64, String), u64>> = Default::default();
638        let val: Result<(u64, String), u64> = Ok((42, "test".to_string()));
639        c.push(&val);
640        let val2: Result<(u64, String), u64> = Err(99);
641        c.push(&val2);
642        let mut bytes: Vec<u8> = Vec::new();
643        crate::bytes::indexed::write(&mut bytes, &c.borrow()).unwrap();
644        let stash: Stash<ContainerOf<Result<(u64, String), u64>>, Vec<u8>> =
645            Stash::try_from_bytes(bytes).expect("Result<(u64, String), u64> should validate");
646        let borrowed = stash.borrow();
647        match borrowed.get(0) {
648            Ok((n, s)) => { assert_eq!(*n, 42); assert_eq!(s, b"test"); },
649            Err(_) => panic!("expected Ok"),
650        }
651        match borrowed.get(1) {
652            Err(n) => assert_eq!(*n, 99),
653            Ok(_) => panic!("expected Err"),
654        }
655    }
656
657    /// Test from_store for tuples.
658    #[test]
659    fn from_store_tuple() {
660        use crate::common::{Push, Index};
661        use crate::{Borrow, FromBytes, ContainerOf};
662
663        let mut column: ContainerOf<(u64, String, Vec<u32>)> = Default::default();
664        for i in 0..50u64 {
665            column.push(&(i, format!("hello {i}"), vec![i as u32; i as usize]));
666        }
667
668        let mut store = Vec::new();
669        crate::bytes::indexed::encode(&mut store, &column.borrow());
670        let ds = crate::bytes::indexed::DecodedStore::new(&store);
671        type Borrowed<'a> = crate::BorrowedOf<'a, (u64, String, Vec<u32>)>;
672        let reconstructed = Borrowed::from_store(&ds, &mut 0);
673        for i in 0..50 {
674            let (a, b, _c) = reconstructed.get(i);
675            assert_eq!(*a, i as u64);
676            assert_eq!(b, format!("hello {i}").as_bytes());
677        }
678    }
679
680}