scroll/
pread.rs

1use core::result;
2
3use crate::ctx::TryFromCtx;
4use crate::error;
5
6/// A very generic, contextual pread interface in Rust.
7///
8/// Like [Pwrite](trait.Pwrite.html) — but for reading!
9///
10/// Implementing `Pread` on a data store allows you to then read almost arbitrarily complex types
11/// efficiently.
12///
13/// To this end the Pread trait works in conjuction with the [TryFromCtx](ctx/trait.TryFromCtx.html);
14/// The `TryFromCtx` trait implemented on a type defines how to convert data to an object of that
15/// type, the Pread trait implemented on a data store defines how to extract said data from that
16/// store.
17///
18/// It should be noted though that in this context, data does not necessarily mean `&[u8]` —
19/// `Pread` and `TryFromCtx` are generic over what 'data' means and could be implemented instead
20/// over chunks of memory or any other indexable type — but scroll does come with a set of powerful
21/// blanket implementations for data being a continous block of byte-addressable memory.
22///
23/// Note that in the particular case of the implementation of `Pread` for `[u8]`,
24/// reading it at the length boundary of that slice will cause to read from an empty slice.
25/// i.e. we make use of the fact that `&bytes[bytes.len()..]` will return an empty slice, rather
26/// than returning an error. In the past, scroll returned an offset error.
27///
28/// Pread provides two main groups of functions: pread and gread.
29///
30/// `pread` is the basic function that simply extracts a given type from a given data store - either
31/// using a provided Context in the case of [pread_with](trait.Pread.html#method.pread_with) or
32/// with the default context for the given type in the case of [pread](trait.Pread.html#method.pread)
33///
34/// `gread` does in addition to that update the offset it's currently at, allowing for a cursored
35/// read — `gread_inout` expands on that and reads a number of continous types from the data store.
36/// gread again comes with `_with` variants to allow using a specific context.
37///
38/// Since pread and friends are very generic functions their types are rather complex, but very
39/// much understandable; `TryFromCtx` is generic over `Ctx` ([described
40/// here](ctx/index.html#context)), `Output` and `Error`. The Error type is hopefully
41/// self-explanatory, however the `Output` type is rather important; it defines what Pread extracts
42/// from the data store and has to match up with what `TryFromCtx` expects as input to convert into
43/// the resulting type. scroll defaults to `&[u8]` here.
44///
45/// Unless you need to implement your own data store — that is either can't convert to `&[u8]` or
46/// have a data that does not expose a `&[u8]` — you will probably want to implement
47/// [TryFromCtx](ctx/trait.TryFromCtx.html) on your Rust types to be extracted.
48///
49pub trait Pread<Ctx: Copy, E> {
50    #[inline]
51    /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness.
52    /// # Example
53    /// ```rust
54    /// use scroll::Pread;
55    /// let bytes = [0x7fu8; 0x01];
56    /// let byte = bytes.pread::<u8>(0).unwrap();
57    fn pread<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>(
58        &'a self,
59        offset: usize,
60    ) -> result::Result<N, E>
61    where
62        Ctx: Default,
63    {
64        self.pread_with(offset, Ctx::default())
65    }
66
67    #[inline]
68    /// Reads a value from `self` at `offset` with the given `ctx`
69    /// # Example
70    /// ```rust
71    /// use scroll::Pread;
72    /// let bytes: [u8; 2] = [0xde, 0xad];
73    /// let dead: u16 = bytes.pread_with(0, scroll::BE).unwrap();
74    /// assert_eq!(dead, 0xdeadu16);
75    fn pread_with<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>(
76        &'a self,
77        offset: usize,
78        ctx: Ctx,
79    ) -> result::Result<N, E> {
80        let mut ignored = offset;
81        self.gread_with(&mut ignored, ctx)
82    }
83
84    #[inline]
85    /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness. Updates the offset
86    /// # Example
87    /// ```rust
88    /// use scroll::Pread;
89    /// let offset = &mut 0;
90    /// let bytes = [0x7fu8; 0x01];
91    /// let byte = bytes.gread::<u8>(offset).unwrap();
92    /// assert_eq!(*offset, 1);
93    fn gread<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>(
94        &'a self,
95        offset: &mut usize,
96    ) -> result::Result<N, E>
97    where
98        Ctx: Default,
99    {
100        let ctx = Ctx::default();
101        self.gread_with(offset, ctx)
102    }
103
104    /// Reads a value from `self` at `offset` with the given `ctx`, and updates the offset.
105    /// # Example
106    /// ```rust
107    /// use scroll::Pread;
108    /// let offset = &mut 0;
109    /// let bytes: [u8; 2] = [0xde, 0xad];
110    /// let dead: u16 = bytes.gread_with(offset, scroll::BE).unwrap();
111    /// assert_eq!(dead, 0xdeadu16);
112    /// assert_eq!(*offset, 2);
113    fn gread_with<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>(
114        &'a self,
115        offset: &mut usize,
116        ctx: Ctx,
117    ) -> result::Result<N, E>;
118
119    /// Tries to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the default context for `N`, and updates the offset.
120    /// # Example
121    /// ```rust
122    /// use scroll::Pread;
123    /// let mut bytes: Vec<u8> = vec![0, 0];
124    /// let offset = &mut 0;
125    /// let bytes_from: [u8; 2] = [0x48, 0x49];
126    /// bytes_from.gread_inout(offset, &mut bytes).unwrap();
127    /// assert_eq!(&bytes, &bytes_from);
128    /// assert_eq!(*offset, 2);
129    #[inline]
130    fn gread_inout<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>(
131        &'a self,
132        offset: &mut usize,
133        inout: &mut [N],
134    ) -> result::Result<(), E>
135    where
136        Ctx: Default,
137    {
138        for i in inout.iter_mut() {
139            *i = self.gread(offset)?;
140        }
141        Ok(())
142    }
143
144    /// Tries to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the context `ctx`
145    /// # Example
146    /// ```rust
147    /// use scroll::{ctx, LE, Pread};
148    /// let mut bytes: Vec<u8> = vec![0, 0];
149    /// let offset = &mut 0;
150    /// let bytes_from: [u8; 2] = [0x48, 0x49];
151    /// bytes_from.gread_inout_with(offset, &mut bytes, LE).unwrap();
152    /// assert_eq!(&bytes, &bytes_from);
153    /// assert_eq!(*offset, 2);
154    #[inline]
155    fn gread_inout_with<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>(
156        &'a self,
157        offset: &mut usize,
158        inout: &mut [N],
159        ctx: Ctx,
160    ) -> result::Result<(), E> {
161        for i in inout.iter_mut() {
162            *i = self.gread_with(offset, ctx)?;
163        }
164        Ok(())
165    }
166}
167
168impl<Ctx: Copy, E: From<error::Error>> Pread<Ctx, E> for [u8] {
169    fn gread_with<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>(
170        &'a self,
171        offset: &mut usize,
172        ctx: Ctx,
173    ) -> result::Result<N, E> {
174        let start = *offset;
175        if start > self.len() {
176            return Err(error::Error::BadOffset(start).into());
177        }
178        N::try_from_ctx(&self[start..], ctx).map(|(n, size)| {
179            *offset += size;
180            n
181        })
182    }
183}