Skip to main content

substrate_data/wire/frame/
view.rs

1//! Views: a frame read without copying — strings borrowed from it, packed
2//! numbers read in place (no alignment asked), nested records viewed too.
3//! One pass checks the frame and finds every field; nothing is allocated
4//! for strings or number lists. A struct gets its view with
5//! `#[derive(Schema)] #[data(view)]` (`MyStructView<'a>`).
6//!
7//! ```
8//! #[derive(substrate_data::Schema)]
9//! #[data(view)]
10//! struct Sheet { name: String, cells: Vec<u16> }
11//!
12//! let frame = substrate_data::frame::to_frame(&Sheet { name: "q3".into(), cells: vec![0, 2, 1] });
13//! let sheet = substrate_data::frame::read_view::<Sheet>(&frame).unwrap();
14//! assert_eq!(sheet.name, "q3");
15//! assert_eq!(sheet.cells.get(1), Some(2));
16//! ```
17
18use std::marker::PhantomData;
19
20use super::Unpacker;
21use super::sizes::{F32_BYTES, F64_BYTES};
22
23/// A type read as a view of a frame: what it is borrowed (`View`), and what
24/// a list of it is (`ListView`).
25pub trait View<'a>: Sized {
26    /// What it's viewed as.
27    type View;
28    /// What a list of it is viewed as.
29    type ListView;
30    /// It viewed in a frame.
31    fn unpack_view(u: &mut Unpacker<'a>) -> Option<Self::View>;
32    /// A list of it viewed in a frame.
33    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Self::ListView>;
34}
35
36/// `T`'s view of the frame `bytes`.
37#[must_use]
38pub fn read_view<'a, T: View<'a>>(bytes: &'a [u8]) -> Option<T::View> {
39    let mut u = Unpacker::open(bytes)?;
40    let v = T::unpack_view(&mut u)?;
41    u.at_end().then_some(v)
42}
43
44/// A list of anything but numbers, read lazily: its bytes and how many
45/// items — each viewed as it's reached ([`List::iter`]), nothing allocated.
46pub struct List<'a, T> {
47    u: Unpacker<'a>,
48    len: usize,
49    item: PhantomData<fn() -> T>,
50}
51
52impl<T> Clone for List<'_, T> {
53    fn clone(&self) -> Self {
54        List {
55            u: self.u.fork(self.u.position()),
56            len: self.len,
57            item: PhantomData,
58        }
59    }
60}
61
62impl<T> std::fmt::Debug for List<'_, T> {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(f, "List({} items)", self.len)
65    }
66}
67
68impl<'a, T: View<'a>> List<'a, T> {
69    /// How many items.
70    #[must_use]
71    pub fn len(&self) -> usize {
72        self.len
73    }
74
75    /// Whether it has none.
76    #[must_use]
77    pub fn is_empty(&self) -> bool {
78        self.len == 0
79    }
80
81    /// Each item's view, read as it's reached.
82    #[must_use]
83    pub fn iter(&self) -> ListIter<'a, T> {
84        ListIter {
85            u: self.u.fork(self.u.position()),
86            left: self.len,
87            item: PhantomData,
88        }
89    }
90
91    /// Item `i` (the ones before it stepped over).
92    #[must_use]
93    pub fn get(&self, i: usize) -> Option<T::View> {
94        self.iter().nth(i)
95    }
96}
97
98impl<'a, T: View<'a>> IntoIterator for &List<'a, T> {
99    type Item = T::View;
100    type IntoIter = ListIter<'a, T>;
101    fn into_iter(self) -> ListIter<'a, T> {
102        self.iter()
103    }
104}
105
106/// [`List::iter`].
107#[derive(Debug)]
108pub struct ListIter<'a, T> {
109    u: Unpacker<'a>,
110    left: usize,
111    item: PhantomData<fn() -> T>,
112}
113
114impl<'a, T: View<'a>> Iterator for ListIter<'a, T> {
115    type Item = T::View;
116    fn next(&mut self) -> Option<T::View> {
117        self.left = self.left.checked_sub(1)?;
118        T::unpack_view(&mut self.u)
119    }
120}
121
122/// A list ([`super::Packer::begin_list`]) as a [`List`]: stepped over, read later.
123pub fn list<'a, T: View<'a>>(u: &mut Unpacker<'a>) -> Option<List<'a, T>> {
124    let (len, end) = u.list()?;
125    let at = u.position();
126    u.skip_to(end)?;
127    Some(List {
128        u: u.fork(at),
129        len,
130        item: PhantomData,
131    })
132}
133
134impl<'a> View<'a> for bool {
135    type View = bool;
136    type ListView = List<'a, bool>;
137    fn unpack_view(u: &mut Unpacker<'a>) -> Option<bool> {
138        u.bool()
139    }
140    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Self::ListView> {
141        list(u)
142    }
143}
144
145impl<'a> View<'a> for String {
146    type View = &'a str;
147    type ListView = List<'a, Self>;
148    fn unpack_view(u: &mut Unpacker<'a>) -> Option<&'a str> {
149        u.str()
150    }
151    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Self::ListView> {
152        list(u)
153    }
154}
155
156impl<'a> View<'a> for Box<str> {
157    type View = &'a str;
158    type ListView = List<'a, Self>;
159    fn unpack_view(u: &mut Unpacker<'a>) -> Option<&'a str> {
160        u.str()
161    }
162    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Self::ListView> {
163        list(u)
164    }
165}
166
167impl<'a> View<'a> for crate::Atom {
168    type View = crate::Atom;
169    type ListView = List<'a, crate::Atom>;
170    fn unpack_view(u: &mut Unpacker<'a>) -> Option<crate::Atom> {
171        <crate::Atom as crate::Schema>::unpack(u)
172    }
173    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Self::ListView> {
174        list(u)
175    }
176}
177
178impl<'a, T: View<'a>> View<'a> for Option<T> {
179    type View = Option<T::View>;
180    type ListView = List<'a, Option<T>>;
181    fn unpack_view(u: &mut Unpacker<'a>) -> Option<Option<T::View>> {
182        if u.bool()? {
183            T::unpack_view(u).map(Some)
184        } else {
185            Some(None)
186        }
187    }
188    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Self::ListView> {
189        list(u)
190    }
191}
192
193impl<'a, T: View<'a>> View<'a> for Box<T> {
194    type View = T::View;
195    type ListView = T::ListView;
196    fn unpack_view(u: &mut Unpacker<'a>) -> Option<T::View> {
197        T::unpack_view(u)
198    }
199    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<T::ListView> {
200        T::unpack_list_view(u)
201    }
202}
203
204impl<'a, T: View<'a>> View<'a> for Vec<T> {
205    type View = T::ListView;
206    type ListView = List<'a, Vec<T>>;
207    fn unpack_view(u: &mut Unpacker<'a>) -> Option<T::ListView> {
208        T::unpack_list_view(u)
209    }
210    fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Self::ListView> {
211        list(u)
212    }
213}
214
215/// A packed list of ints, read in place: [`Ints::get`] takes one out of the bits.
216#[derive(Debug, Clone, Copy)]
217pub struct Ints<'a, T> {
218    bytes: &'a [u8],
219    len: usize,
220    bits: u32,
221    signed: bool,
222    item: PhantomData<fn() -> T>,
223}
224
225impl<T: TryFrom<i128>> Ints<'_, T> {
226    /// How many numbers.
227    #[must_use]
228    pub fn len(&self) -> usize {
229        self.len
230    }
231
232    /// Whether it has none.
233    #[must_use]
234    pub fn is_empty(&self) -> bool {
235        self.len == 0
236    }
237
238    /// Item `i`, if there's one: read in place, O(1).
239    #[must_use]
240    pub fn get(&self, i: usize) -> Option<T> {
241        if i >= self.len {
242            return None;
243        }
244        let raw = super::lanes::get(self.bytes, self.len, self.bits, i);
245        let n = if self.signed {
246            i128::from(crate::binary::unzigzag(raw))
247        } else {
248            i128::from(raw)
249        };
250        T::try_from(n).ok()
251    }
252
253    /// The numbers, in order.
254    pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
255        (0..self.len).map_while(|i| self.get(i))
256    }
257}
258
259/// A list of floats, read in place.
260#[derive(Debug, Clone, Copy)]
261pub struct Floats<'a, T> {
262    bytes: &'a [u8],
263    len: usize,
264    /// Kept as `f32`s.
265    narrow: bool,
266    item: PhantomData<fn() -> T>,
267}
268
269impl<T: From<f32> + FromF64> Floats<'_, T> {
270    /// How many numbers.
271    #[must_use]
272    pub fn len(&self) -> usize {
273        self.len
274    }
275
276    /// Whether it has none.
277    #[must_use]
278    pub fn is_empty(&self) -> bool {
279        self.len == 0
280    }
281
282    /// Number `i`, if there's one.
283    #[must_use]
284    pub fn get(&self, i: usize) -> Option<T> {
285        if i >= self.len {
286            return None;
287        }
288        if self.narrow {
289            let b = self.bytes.get(i * F32_BYTES..(i + 1) * F32_BYTES)?;
290            Some(T::from(f32::from_le_bytes(b.try_into().ok()?)))
291        } else {
292            let b = self.bytes.get(i * F64_BYTES..(i + 1) * F64_BYTES)?;
293            Some(T::from_f64(f64::from_le_bytes(b.try_into().ok()?)))
294        }
295    }
296
297    /// The numbers, in order.
298    pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
299        (0..self.len).map_while(|i| self.get(i))
300    }
301}
302
303/// A float out of a `f64` (a `f32` rounds).
304pub trait FromF64 {
305    /// `f` as this type.
306    fn from_f64(f: f64) -> Self;
307}
308
309impl FromF64 for f64 {
310    fn from_f64(f: f64) -> Self {
311        f
312    }
313}
314
315impl FromF64 for f32 {
316    #[expect(
317        clippy::cast_possible_truncation,
318        reason = "narrowing to the nearest `f32` is what's asked"
319    )]
320    fn from_f64(f: f64) -> Self {
321        f as f32
322    }
323}
324
325macro_rules! int_views {
326    ($($t:ty => $signed:expr),*) => {$(
327        impl<'a> View<'a> for $t {
328            type View = $t;
329            type ListView = Ints<'a, $t>;
330            fn unpack_view(u: &mut Unpacker<'a>) -> Option<$t> {
331                <$t as crate::Schema>::unpack(u)
332            }
333            fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Ints<'a, $t>> {
334                let (len, bits, bytes) = u.packed_ints()?;
335                Some(Ints { bytes, len, bits, signed: $signed, item: PhantomData })
336            }
337        }
338    )*};
339}
340int_views!(i8 => true, i16 => true, i32 => true, i64 => true, u8 => false, u16 => false, u32 => false, u64 => false, usize => false);
341
342macro_rules! float_views {
343    ($($t:ty => $wide:expr),*) => {$(
344        impl<'a> View<'a> for $t {
345            type View = $t;
346            type ListView = Floats<'a, $t>;
347            fn unpack_view(u: &mut Unpacker<'a>) -> Option<$t> {
348                <$t as crate::Schema>::unpack(u)
349            }
350            fn unpack_list_view(u: &mut Unpacker<'a>) -> Option<Floats<'a, $t>> {
351                let (len, narrow, bytes) = u.packed_floats($wide)?;
352                Some(Floats { bytes, len, narrow, item: PhantomData })
353            }
354        }
355    )*};
356}
357float_views!(f32 => false, f64 => true);