cookie_factory/
internal.rs

1//! main structures and traits used to build serializers
2use crate::lib::std::{
3    fmt,
4    io::{self, Seek as _, SeekFrom, Write},
5};
6
7/// Holds the result of serializing functions
8///
9/// The `Ok` case returns the `Write` used for writing, in the `Err` case an instance of
10/// `cookie_factory::GenError` is returned.
11pub type GenResult<W> = Result<WriteContext<W>, GenError>;
12
13/// Base type for generator errors
14#[derive(Debug)]
15pub enum GenError {
16    /// Input buffer is too small. Argument is the maximum index that is required
17    BufferTooSmall(usize),
18    /// We expected to fill the whole buffer but there is some space left
19    BufferTooBig(usize),
20    /// Operation asked for accessing an invalid index
21    InvalidOffset,
22    /// IoError returned by Write
23    IoError(io::Error),
24
25    /// Allocated for custom errors
26    CustomError(u32),
27    /// Generator or function not yet implemented
28    NotYetImplemented,
29}
30
31impl fmt::Display for GenError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        write!(f, "{:?}", self)
34    }
35}
36
37#[cfg(feature = "std")]
38impl std::error::Error for GenError {}
39
40impl From<io::Error> for GenError {
41    fn from(err: io::Error) -> Self {
42        GenError::IoError(err)
43    }
44}
45
46/// Trait for serializing functions
47///
48/// Serializing functions take one input `W` that is the target of writing and return an instance
49/// of `cookie_factory::GenResult`.
50///
51/// This trait is implemented for all `Fn(W) -> GenResult<W>`.
52pub trait SerializeFn<W>: Fn(WriteContext<W>) -> GenResult<W> {}
53
54impl<W, F: Fn(WriteContext<W>) -> GenResult<W>> SerializeFn<W> for F {}
55
56/// Context around a `Write` impl that is passed through serializing functions
57///
58/// Currently this only keeps track of the current write position since the start of serialization.
59pub struct WriteContext<W> {
60    pub write: W,
61    pub position: u64,
62}
63
64impl<W: Write> From<W> for WriteContext<W> {
65    fn from(write: W) -> Self {
66        Self { write, position: 0 }
67    }
68}
69
70impl<W: Write> WriteContext<W> {
71    /// Returns the contained `Write` and the current position
72    pub fn into_inner(self) -> (W, u64) {
73        (self.write, self.position)
74    }
75}
76
77impl<W: Write> Write for WriteContext<W> {
78    fn write(&mut self, data: &[u8]) -> crate::lib::std::io::Result<usize> {
79        let amt = self.write.write(data)?;
80        self.position += amt as u64;
81        Ok(amt)
82    }
83
84    #[cfg(feature = "std")]
85    fn flush(&mut self) -> io::Result<()> {
86        self.write.flush()
87    }
88}
89
90impl<W: Seek> io::Seek for WriteContext<W> {
91    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
92        let old_pos = self.write.stream_position()?;
93        let new_pos = self.write.seek(pos)?;
94        if new_pos >= old_pos {
95            self.position += new_pos - old_pos;
96        } else {
97            self.position -= old_pos - new_pos;
98        }
99        Ok(new_pos)
100    }
101}
102
103/// Runs the given serializer `f` with the `Write` impl `w` and returns the result
104///
105/// This internally wraps `w` in a `WriteContext`, starting at position 0.
106///
107/// ```rust
108/// use cookie_factory::{gen, combinator::slice};
109///
110/// let mut buf = [0u8; 100];
111///
112/// {
113///   let (buf, pos) = gen(slice(&b"abcd"[..]), &mut buf[..]).unwrap();
114///   assert_eq!(buf.len(), 100 - 4);
115///   assert_eq!(pos, 4);
116/// }
117///
118/// assert_eq!(&buf[..4], &b"abcd"[..]);
119/// ```
120pub fn gen<W: Write, F: SerializeFn<W>>(f: F, w: W) -> Result<(W, u64), GenError> {
121    f(WriteContext::from(w)).map(|ctx| ctx.into_inner())
122}
123
124/// Runs the given serializer `f` with the `Write` impl `w` and returns the updated `w`
125///
126/// This internally wraps `w` in a `WriteContext`, starting at position 0.
127///
128/// ```rust
129/// use cookie_factory::{gen_simple, combinator::slice};
130///
131/// let mut buf = [0u8; 100];
132///
133/// {
134///   let buf = gen_simple(slice(&b"abcd"[..]), &mut buf[..]).unwrap();
135///   assert_eq!(buf.len(), 100 - 4);
136/// }
137///
138/// assert_eq!(&buf[..4], &b"abcd"[..]);
139/// ```
140pub fn gen_simple<W: Write, F: SerializeFn<W>>(f: F, w: W) -> Result<W, GenError> {
141    f(WriteContext::from(w)).map(|ctx| ctx.into_inner().0)
142}
143
144/// Trait for `Write` types that allow skipping over the data
145pub trait Skip: Write {
146    fn skip(s: WriteContext<Self>, sz: usize) -> GenResult<Self>
147    where
148        Self: Sized;
149}
150
151/// Trait for `Write` types that allow skipping and reserving a slice, then writing some data,
152/// then write something in the slice we reserved using the return for our data write.
153pub trait BackToTheBuffer: Write {
154    fn reserve_write_use<
155        Tmp,
156        Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
157        Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
158    >(
159        s: WriteContext<Self>,
160        reserved: usize,
161        gen: &Gen,
162        before: &Before,
163    ) -> Result<WriteContext<Self>, GenError>
164    where
165        Self: Sized;
166}
167
168/// Trait for `Seek` types that want to automatically implement `BackToTheBuffer`
169pub trait Seek: Write + io::Seek {}
170impl Seek for io::Cursor<&mut [u8]> {}
171
172impl<W: Seek> BackToTheBuffer for W {
173    fn reserve_write_use<
174        Tmp,
175        Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
176        Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
177    >(
178        mut s: WriteContext<Self>,
179        reserved: usize,
180        gen: &Gen,
181        before: &Before,
182    ) -> Result<WriteContext<Self>, GenError> {
183        let start = s.stream_position()?;
184        let begin = s.seek(SeekFrom::Current(reserved as i64))?;
185        let (mut buf, tmp) = gen(s)?;
186        let end = buf.stream_position()?;
187        buf.seek(SeekFrom::Start(start))?;
188        let mut buf = before(buf, tmp)?;
189        let pos = buf.stream_position()?;
190        if pos != begin {
191            return Err(GenError::BufferTooBig((begin - pos) as usize));
192        }
193        buf.seek(SeekFrom::Start(end))?;
194        Ok(buf)
195    }
196}
197
198impl Skip for &mut [u8] {
199    fn skip(s: WriteContext<Self>, len: usize) -> Result<WriteContext<Self>, GenError> {
200        if s.write.len() < len {
201            Err(GenError::BufferTooSmall(len - s.write.len()))
202        } else {
203            Ok(WriteContext {
204                write: &mut s.write[len..],
205                position: s.position + len as u64,
206            })
207        }
208    }
209}
210
211impl Skip for io::Cursor<&mut [u8]> {
212    fn skip(mut s: WriteContext<Self>, len: usize) -> GenResult<Self> {
213        let remaining = s
214            .write
215            .get_ref()
216            .len()
217            .saturating_sub(s.write.position() as usize);
218        if remaining < len {
219            Err(GenError::BufferTooSmall(len - remaining))
220        } else {
221            let cursor_position = s.write.position();
222            s.write.set_position(cursor_position + len as u64);
223            s.position += len as u64;
224            Ok(s)
225        }
226    }
227}
228
229impl BackToTheBuffer for &mut [u8] {
230    fn reserve_write_use<
231        Tmp,
232        Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
233        Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
234    >(
235        s: WriteContext<Self>,
236        reserved: usize,
237        gen: &Gen,
238        before: &Before,
239    ) -> Result<WriteContext<Self>, GenError> {
240        let WriteContext {
241            write: slice,
242            position: original_position,
243        } = s;
244
245        let (res, buf) = slice.split_at_mut(reserved);
246        let (new_context, tmp) = gen(WriteContext {
247            write: buf,
248            position: original_position + reserved as u64,
249        })?;
250
251        let res = before(
252            WriteContext {
253                write: res,
254                position: original_position,
255            },
256            tmp,
257        )?;
258
259        if !res.write.is_empty() {
260            return Err(GenError::BufferTooBig(res.write.len()));
261        }
262
263        Ok(new_context)
264    }
265}
266
267#[cfg(feature = "std")]
268impl BackToTheBuffer for Vec<u8> {
269    fn reserve_write_use<
270        Tmp,
271        Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
272        Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
273    >(
274        s: WriteContext<Self>,
275        reserved: usize,
276        gen: &Gen,
277        before: &Before,
278    ) -> Result<WriteContext<Self>, GenError> {
279        let WriteContext {
280            write: mut vec,
281            position: original_position,
282        } = s;
283
284        let start_len = vec.len();
285        vec.extend(std::iter::repeat(0).take(reserved));
286
287        let (mut new_context, tmp) = gen(WriteContext {
288            write: vec,
289            position: original_position + reserved as u64,
290        })?;
291
292        let tmp_context = before(
293            WriteContext {
294                write: Vec::new(),
295                position: original_position,
296            },
297            tmp,
298        )?;
299
300        let tmp_written = tmp_context.write.len();
301        if tmp_written != reserved {
302            return Err(GenError::BufferTooBig(reserved - tmp_written));
303        }
304
305        // FIXME?: find a way to do that without copying
306        // Vec::from_raw_parts + core::mem::forget makes it work, but
307        // if `before` writes more than `reserved`, realloc will cause troubles
308        new_context.write[start_len..(start_len + reserved)]
309            .copy_from_slice(&tmp_context.write[..]);
310
311        Ok(new_context)
312    }
313}