Skip to main content

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