scroll/
ctx.rs

1//! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al
2//!
3//! The context traits are arguably the center piece of the scroll crate. In simple terms they
4//! define how to actually read and write, respectively, a data type from a container, being able to
5//! take context into account.
6//!
7//! ### Reading
8//!
9//! Types implementing [TryFromCtx](trait.TryFromCtx.html) and it's infallible cousin [FromCtx](trait.FromCtx.html)
10//! allow a user of [Pread::pread](../trait.Pread.html#method.pread) or respectively
11//! [Cread::cread](../trait.Cread.html#method.cread) and
12//! [IOread::ioread](../trait.IOread.html#method.ioread) to read that data type from a data source one
13//! of the `*read` traits has been implemented for.
14//!
15//! Implementations of `TryFromCtx` specify a source (called `This`) and an `Error` type for failed
16//! reads. The source defines the kind of container the type can be read from, and defaults to
17//! `[u8]` for any type that implements `AsRef<[u8]>`.
18//!
19//! `FromCtx` is slightly more restricted; it requires the implementer to use `[u8]` as source and
20//! never fail, and thus does not have an `Error` type.
21//!
22//! Types chosen here are of relevance to `Pread` implementations; of course only a container which
23//! can produce a source of the type `This` can be used to read a `TryFromCtx` requiring it and the
24//! `Error` type returned in `Err` of `Pread::pread`'s Result.
25//!
26//! ### Writing
27//!
28//! [TryIntoCtx](trait.TryIntoCtx.html) and the infallible [IntoCtx](trait.IntoCtx.html) work
29//! similarly to the above traits, allowing [Pwrite::pwrite](../trait.Pwrite.html#method.pwrite) or
30//! respectively [Cwrite::cwrite](../trait.Cwrite.html#method.cwrite) and
31//! [IOwrite::iowrite](../trait.IOwrite.html#method.iowrite) to write data into a byte sink for
32//! which one of the `*write` traits has been implemented for.
33//!
34//! `IntoCtx` is similarly restricted as `FromCtx` is to `TryFromCtx`. And equally the types chosen
35//! affect usable `Pwrite` implementation.
36//!
37//! ### Context
38//!
39//! Each of the traits passes along a `Ctx` to the marshalling logic. This context type contains
40//! any additional information that may be required to successfully parse or write the data:
41//! Examples would be endianness to use, field lengths of a serialized struct, or delimiters to use
42//! when reading/writing `&str`. The context type can be any type but must derive
43//! [Copy](https://doc.rust-lang.org/std/marker/trait.Copy.html). In addition if you want to use
44//! the `*read`-methods instead of the `*read_with` ones you must also implement
45//! [default::Default](https://doc.rust-lang.org/std/default/trait.Default.html).
46//!
47//! # Example
48//!
49//! Let's expand on the [previous example](../index.html#complex-use-cases).
50//!
51//! ```rust
52//! use scroll::{self, ctx, Pread, Endian};
53//! use scroll::ctx::StrCtx;
54//!
55//! #[derive(Copy, Clone, PartialEq, Eq)]
56//! enum FieldSize {
57//!     U32,
58//!     U64
59//! }
60//!
61//! // Our custom context type. As said above it has to derive Copy.
62//! #[derive(Copy, Clone)]
63//! struct Context {
64//!     fieldsize: FieldSize,
65//!     endianess: Endian,
66//! }
67//!
68//! // Our custom data type
69//! struct Data<'b> {
70//!   // These u64 are encoded either as 32-bit or 64-bit wide ints. Which one it is is defined in
71//!   // the Context.
72//!   // Also, let's imagine they have a strict relationship: A < B < C otherwise the struct is
73//!   // invalid.
74//!   field_a: u64,
75//!   field_b: u64,
76//!   field_c: u64,
77//!
78//!   // Both of these are marshalled with a prefixed length.
79//!   name: &'b str,
80//!   value: &'b [u8],
81//! }
82//!
83//! #[derive(Debug)]
84//! enum Error {
85//!     // We'll return this custom error if the field* relationship doesn't hold
86//!     BadFieldMatchup,
87//!     Scroll(scroll::Error),
88//! }
89//!
90//! impl<'a> ctx::TryFromCtx<'a, Context> for Data<'a> {
91//!   type Error = Error;
92//!
93//!   // Using the explicit lifetime specification again you ensure that read data doesn't outlife
94//!   // its source buffer without having to resort to copying.
95//!   fn try_from_ctx (src: &'a [u8], ctx: Context)
96//!     // the `usize` returned here is the amount of bytes read.
97//!     -> Result<(Self, usize), Self::Error>
98//!   {
99//!     // The offset counter; gread and gread_with increment a given counter automatically so we
100//!     // don't have to manually care.
101//!     let offset = &mut 0;
102//!
103//!     let field_a;
104//!     let field_b;
105//!     let field_c;
106//!
107//!     // Switch the amount of bytes read depending on the parsing context
108//!     if ctx.fieldsize == FieldSize::U32 {
109//!       field_a = src.gread_with::<u32>(offset, ctx.endianess)? as u64;
110//!       field_b = src.gread_with::<u32>(offset, ctx.endianess)? as u64;
111//!       field_c = src.gread_with::<u32>(offset, ctx.endianess)? as u64;
112//!     } else {
113//!       field_a = src.gread_with::<u64>(offset, ctx.endianess)?;
114//!       field_b = src.gread_with::<u64>(offset, ctx.endianess)?;
115//!       field_c = src.gread_with::<u64>(offset, ctx.endianess)?;
116//!     }
117//!
118//!     // You can use type ascribition or turbofish operators, whichever you prefer.
119//!     let namelen = src.gread_with::<u16>(offset, ctx.endianess)? as usize;
120//!     let name: &str = src.gread_with(offset, scroll::ctx::StrCtx::Length(namelen))?;
121//!
122//!     let vallen = src.gread_with::<u16>(offset, ctx.endianess)? as usize;
123//!     let value = &src[*offset..(*offset+vallen)];
124//!
125//!     // Let's sanity check those fields, shall we?
126//!     if ! (field_a < field_b && field_b < field_c) {
127//!       return Err(Error::BadFieldMatchup);
128//!     }
129//!
130//!     Ok((Data { field_a, field_b, field_c, name, value }, *offset))
131//!   }
132//! }
133//!
134//! // In lieu of a complex byte buffer we hearken back to the venerable &[u8]; do note however
135//! // that the implementation of TryFromCtx did not specify such. In fact any type that implements
136//! // Pread can now read `Data` as it implements TryFromCtx.
137//! let bytes = b"\x00\x02\x03\x04\x01\x02\x03\x04\xde\xad\xbe\xef\x00\x08UserName\x00\x02\xCA\xFE";
138//!
139//! // We define an appropiate context, and get going
140//! let contextA = Context {
141//!     fieldsize: FieldSize::U32,
142//!     endianess: Endian::Big,
143//! };
144//! let data: Data = bytes.pread_with(0, contextA).unwrap();
145//!
146//! assert_eq!(data.field_a, 0x00020304);
147//! assert_eq!(data.field_b, 0x01020304);
148//! assert_eq!(data.field_c, 0xdeadbeef);
149//! assert_eq!(data.name, "UserName");
150//! assert_eq!(data.value, [0xCA, 0xFE]);
151//!
152//! // Here we have a context with a different FieldSize, changing parsing information at runtime.
153//! let contextB = Context {
154//!     fieldsize: FieldSize::U64,
155//!     endianess: Endian::Big,
156//! };
157//!
158//! // Which will of course error with a malformed input for the context
159//! let err: Result<Data, Error> = bytes.pread_with(0, contextB);
160//! assert!(err.is_err());
161//!
162//! let bytes_long = [0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x04,0x00,0x00,0x00,0x00,0x01,0x02,0x03,
163//!                   0x04,0x00,0x00,0x00,0x00,0xde,0xad,0xbe,0xef,0x00,0x08,0x55,0x73,0x65,0x72,
164//!                   0x4e,0x61,0x6d,0x65,0x00,0x02,0xCA,0xFE];
165//!
166//! let data: Data = bytes_long.pread_with(0, contextB).unwrap();
167//!
168//! assert_eq!(data.field_a, 0x00020304);
169//! assert_eq!(data.field_b, 0x01020304);
170//! assert_eq!(data.field_c, 0xdeadbeef);
171//! assert_eq!(data.name, "UserName");
172//! assert_eq!(data.value, [0xCA, 0xFE]);
173//!
174//! // Ergonomic conversion, not relevant really.
175//! use std::convert::From;
176//! impl From<scroll::Error> for Error {
177//!   fn from(error: scroll::Error) -> Error {
178//!     Error::Scroll(error)
179//!   }
180//! }
181//! ```
182
183use core::mem::{MaybeUninit, size_of};
184use core::ptr::copy_nonoverlapping;
185use core::{result, str};
186#[cfg(feature = "std")]
187use std::ffi::{CStr, CString};
188
189use crate::endian::Endian;
190use crate::{Pread, Pwrite, error};
191
192/// A trait for measuring how large something is; for a byte sequence, it will be its length.
193pub trait MeasureWith<Ctx> {
194    /// How large is `Self`, given the `ctx`?
195    fn measure_with(&self, ctx: &Ctx) -> usize;
196}
197
198impl<Ctx> MeasureWith<Ctx> for [u8] {
199    #[inline]
200    fn measure_with(&self, _ctx: &Ctx) -> usize {
201        self.len()
202    }
203}
204
205impl<Ctx, T: AsRef<[u8]>> MeasureWith<Ctx> for T {
206    #[inline]
207    fn measure_with(&self, _ctx: &Ctx) -> usize {
208        self.as_ref().len()
209    }
210}
211
212/// The parsing context for converting a byte sequence to a `&str`
213///
214/// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful.
215#[derive(Debug, Copy, Clone)]
216pub enum StrCtx {
217    Delimiter(u8),
218    DelimiterUntil(u8, usize),
219    Length(usize),
220}
221
222/// A C-style, null terminator based delimiter
223pub const NULL: u8 = 0;
224/// A space-based delimiter
225pub const SPACE: u8 = 0x20;
226/// A newline-based delimiter
227pub const RET: u8 = 0x0a;
228/// A tab-based delimiter
229pub const TAB: u8 = 0x09;
230
231impl Default for StrCtx {
232    #[inline]
233    fn default() -> Self {
234        StrCtx::Delimiter(NULL)
235    }
236}
237
238impl StrCtx {
239    pub fn len(&self) -> usize {
240        match self {
241            StrCtx::Delimiter(_) | StrCtx::DelimiterUntil(_, _) => 1,
242            StrCtx::Length(_) => 0,
243        }
244    }
245
246    pub fn is_empty(&self) -> bool {
247        matches!(self, StrCtx::Length(_))
248    }
249}
250
251/// Reads `Self` from `This` using the context `Ctx`; must _not_ fail
252pub trait FromCtx<Ctx: Copy = (), This: ?Sized = [u8]> {
253    fn from_ctx(this: &This, ctx: Ctx) -> Self;
254}
255
256/// Tries to read `Self` from `This` using the context `Ctx`
257///
258/// # Implementing Your Own Reader
259/// If you want to implement your own reader for a type `Foo` from some kind of buffer (say
260/// `[u8]`), then you need to implement this trait
261///
262/// ```rust
263/// ##[cfg(feature = "std")] {
264/// use scroll::{self, ctx, Pread};
265/// #[derive(Debug, PartialEq, Eq)]
266/// pub struct Foo(u16);
267///
268/// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo {
269///      type Error = scroll::Error;
270///      fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
271///          if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) }
272///          let n = this.pread_with(0, le)?;
273///          Ok((Foo(n), 2))
274///      }
275/// }
276///
277/// let bytes: [u8; 4] = [0xde, 0xad, 0, 0];
278/// let foo = bytes.pread_with::<Foo>(0, scroll::LE).unwrap();
279/// assert_eq!(Foo(0xadde), foo);
280///
281/// let foo2 = bytes.pread_with::<Foo>(0, scroll::BE).unwrap();
282/// assert_eq!(Foo(0xdeadu16), foo2);
283/// # }
284/// ```
285///
286/// # Advanced: Using Your Own Error in `TryFromCtx`
287/// ```rust
288///  use scroll::{self, ctx, Pread};
289///  use std::error;
290///  use std::fmt::{self, Display};
291///  // make some kind of normal error which also can transformed from a scroll error
292///  #[derive(Debug)]
293///  pub struct ExternalError {}
294///
295///  impl Display for ExternalError {
296///      fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
297///          write!(fmt, "ExternalError")
298///      }
299///  }
300///
301///  impl error::Error for ExternalError {
302///      fn description(&self) -> &str {
303///          "ExternalError"
304///      }
305///      fn cause(&self) -> Option<&dyn error::Error> { None}
306///  }
307///
308///  impl From<scroll::Error> for ExternalError {
309///      fn from(err: scroll::Error) -> Self {
310///          match err {
311///              _ => ExternalError{},
312///          }
313///      }
314///  }
315///  #[derive(Debug, PartialEq, Eq)]
316///  pub struct Foo(u16);
317///
318///  impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo {
319///      type Error = ExternalError;
320///      fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
321///          if this.len() <= 2 { return Err((ExternalError {}).into()) }
322///          let offset = &mut 0;
323///          let n = this.gread_with(offset, le)?;
324///          Ok((Foo(n), *offset))
325///      }
326///  }
327///
328/// let bytes: [u8; 4] = [0xde, 0xad, 0, 0];
329/// let foo: Result<Foo, ExternalError> = bytes.pread(0);
330/// ```
331pub trait TryFromCtx<'a, Ctx: Copy = (), This: ?Sized = [u8]>
332where
333    Self: 'a + Sized,
334{
335    type Error;
336    fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>;
337}
338
339/// Writes `Self` into `This` using the context `Ctx`
340pub trait IntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized {
341    fn into_ctx(self, _: &mut This, ctx: Ctx);
342}
343
344/// Tries to write `Self` into `This` using the context `Ctx`
345/// To implement writing into an arbitrary byte buffer, implement `TryIntoCtx`
346/// # Example
347/// ```rust
348/// ##[cfg(feature = "std")] {
349/// use scroll::{self, ctx, LE, Endian, Pwrite};
350/// #[derive(Debug, PartialEq, Eq)]
351/// pub struct Foo(u16);
352///
353/// // this will use the default `DefaultCtx = scroll::Endian`
354/// impl ctx::TryIntoCtx<Endian> for Foo {
355///     // you can use your own error here too, but you will then need to specify it in fn generic parameters
356///     type Error = scroll::Error;
357///     // you can write using your own context type, see `leb128.rs`
358///     fn try_into_ctx(self, this: &mut [u8], le: Endian) -> Result<usize, Self::Error> {
359///         if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) }
360///         this.pwrite_with(self.0, 0, le)?;
361///         Ok(2)
362///     }
363/// }
364/// // now we can write a `Foo` into some buffer (in this case, a byte buffer, because that's what we implemented it for above)
365///
366/// let mut bytes: [u8; 4] = [0, 0, 0, 0];
367/// bytes.pwrite_with(Foo(0x7f), 1, LE).unwrap();
368/// # }
369/// ```
370pub trait TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized {
371    type Error;
372    fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result<usize, Self::Error>;
373}
374
375/// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions
376///
377/// The rationale behind this trait is to:
378///
379/// 1. Prevent `gread` from being used, and the offset being modified based on simply the sizeof the value, which can be a misnomer, e.g., for Leb128, etc.
380/// 2. Allow a context based size, which is useful for 32/64 bit variants for various containers, etc.
381pub trait SizeWith<Ctx = ()> {
382    fn size_with(ctx: &Ctx) -> usize;
383}
384
385#[rustfmt::skip]
386macro_rules! signed_to_unsigned {
387    (i8) =>  {u8 };
388    (u8) =>  {u8 };
389    (i16) => {u16};
390    (u16) => {u16};
391    (i32) => {u32};
392    (u32) => {u32};
393    (i64) => {u64};
394    (u64) => {u64};
395    (i128) => {u128};
396    (u128) => {u128};
397    (f32) => {u32};
398    (f64) => {u64};
399}
400
401macro_rules! write_into {
402    ($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => {{
403        assert!($dst.len() >= $size);
404        let bytes = if $endian.is_little() {
405            $n.to_le()
406        } else {
407            $n.to_be()
408        }
409        .to_ne_bytes();
410        unsafe {
411            copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size);
412        }
413    }};
414}
415
416macro_rules! into_ctx_impl {
417    ($typ:tt, $size:expr) => {
418        impl IntoCtx<Endian> for $typ {
419            #[inline]
420            fn into_ctx(self, dst: &mut [u8], le: Endian) {
421                assert!(dst.len() >= $size);
422                write_into!($typ, $size, self, dst, le);
423            }
424        }
425        impl<'a> IntoCtx<Endian> for &'a $typ {
426            #[inline]
427            fn into_ctx(self, dst: &mut [u8], le: Endian) {
428                (*self).into_ctx(dst, le)
429            }
430        }
431        impl TryIntoCtx<Endian> for $typ
432        where
433            $typ: IntoCtx<Endian>,
434        {
435            type Error = error::Error;
436            #[inline]
437            fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> {
438                if $size > dst.len() {
439                    Err(error::Error::TooBig {
440                        size: $size,
441                        len: dst.len(),
442                    })
443                } else {
444                    <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le);
445                    Ok($size)
446                }
447            }
448        }
449        impl<'a> TryIntoCtx<Endian> for &'a $typ {
450            type Error = error::Error;
451            #[inline]
452            fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> {
453                (*self).try_into_ctx(dst, le)
454            }
455        }
456    };
457}
458
459macro_rules! from_ctx_impl {
460    ($typ:tt, $size:expr) => {
461        impl<'a> FromCtx<Endian> for $typ {
462            #[inline]
463            fn from_ctx(src: &[u8], le: Endian) -> Self {
464                assert!(src.len() >= $size);
465                let mut data: signed_to_unsigned!($typ) = 0;
466                unsafe {
467                    copy_nonoverlapping(
468                        src.as_ptr(),
469                        &mut data as *mut signed_to_unsigned!($typ) as *mut u8,
470                        $size,
471                    );
472                }
473                (if le.is_little() {
474                    data.to_le()
475                } else {
476                    data.to_be()
477                }) as $typ
478            }
479        }
480
481        impl<'a> TryFromCtx<'a, Endian> for $typ
482        where
483            $typ: FromCtx<Endian>,
484        {
485            type Error = error::Error;
486            #[inline]
487            fn try_from_ctx(
488                src: &'a [u8],
489                le: Endian,
490            ) -> result::Result<(Self, usize), Self::Error> {
491                if $size > src.len() {
492                    Err(error::Error::TooBig {
493                        size: $size,
494                        len: src.len(),
495                    })
496                } else {
497                    Ok((FromCtx::from_ctx(&src, le), $size))
498                }
499            }
500        }
501        // as ref
502        impl<'a, T> FromCtx<Endian, T> for $typ
503        where
504            T: AsRef<[u8]>,
505        {
506            #[inline]
507            fn from_ctx(src: &T, le: Endian) -> Self {
508                let src = src.as_ref();
509                assert!(src.len() >= $size);
510                let mut data: signed_to_unsigned!($typ) = 0;
511                unsafe {
512                    copy_nonoverlapping(
513                        src.as_ptr(),
514                        &mut data as *mut signed_to_unsigned!($typ) as *mut u8,
515                        $size,
516                    );
517                }
518                (if le.is_little() {
519                    data.to_le()
520                } else {
521                    data.to_be()
522                }) as $typ
523            }
524        }
525
526        impl<'a, T> TryFromCtx<'a, Endian, T> for $typ
527        where
528            $typ: FromCtx<Endian, T>,
529            T: AsRef<[u8]>,
530        {
531            type Error = error::Error;
532            #[inline]
533            fn try_from_ctx(src: &'a T, le: Endian) -> result::Result<(Self, usize), Self::Error> {
534                let src = src.as_ref();
535                Self::try_from_ctx(src, le)
536            }
537        }
538    };
539}
540
541macro_rules! ctx_impl {
542    ($typ:tt, $size:expr) => {
543        from_ctx_impl!($typ, $size);
544    };
545}
546
547ctx_impl!(u8, 1);
548ctx_impl!(i8, 1);
549ctx_impl!(u16, 2);
550ctx_impl!(i16, 2);
551ctx_impl!(u32, 4);
552ctx_impl!(i32, 4);
553ctx_impl!(u64, 8);
554ctx_impl!(i64, 8);
555ctx_impl!(u128, 16);
556ctx_impl!(i128, 16);
557
558macro_rules! from_ctx_float_impl {
559    ($typ:tt, $size:expr) => {
560        impl<'a> FromCtx<Endian> for $typ {
561            #[inline]
562            fn from_ctx(src: &[u8], le: Endian) -> Self {
563                assert!(src.len() >= ::core::mem::size_of::<Self>());
564                let mut data: signed_to_unsigned!($typ) = 0;
565                unsafe {
566                    copy_nonoverlapping(
567                        src.as_ptr(),
568                        &mut data as *mut signed_to_unsigned!($typ) as *mut u8,
569                        $size,
570                    );
571                }
572                $typ::from_bits(if le.is_little() {
573                    data.to_le()
574                } else {
575                    data.to_be()
576                })
577            }
578        }
579        impl<'a> TryFromCtx<'a, Endian> for $typ
580        where
581            $typ: FromCtx<Endian>,
582        {
583            type Error = error::Error;
584            #[inline]
585            fn try_from_ctx(
586                src: &'a [u8],
587                le: Endian,
588            ) -> result::Result<(Self, usize), Self::Error> {
589                if $size > src.len() {
590                    Err(error::Error::TooBig {
591                        size: $size,
592                        len: src.len(),
593                    })
594                } else {
595                    Ok((FromCtx::from_ctx(src, le), $size))
596                }
597            }
598        }
599    };
600}
601
602from_ctx_float_impl!(f32, 4);
603from_ctx_float_impl!(f64, 8);
604
605into_ctx_impl!(u8, 1);
606into_ctx_impl!(i8, 1);
607into_ctx_impl!(u16, 2);
608into_ctx_impl!(i16, 2);
609into_ctx_impl!(u32, 4);
610into_ctx_impl!(i32, 4);
611into_ctx_impl!(u64, 8);
612into_ctx_impl!(i64, 8);
613into_ctx_impl!(u128, 16);
614into_ctx_impl!(i128, 16);
615
616macro_rules! into_ctx_float_impl {
617    ($typ:tt, $size:expr) => {
618        impl IntoCtx<Endian> for $typ {
619            #[inline]
620            fn into_ctx(self, dst: &mut [u8], le: Endian) {
621                assert!(dst.len() >= $size);
622                write_into!(signed_to_unsigned!($typ), $size, self.to_bits(), dst, le);
623            }
624        }
625        impl<'a> IntoCtx<Endian> for &'a $typ {
626            #[inline]
627            fn into_ctx(self, dst: &mut [u8], le: Endian) {
628                (*self).into_ctx(dst, le)
629            }
630        }
631        impl TryIntoCtx<Endian> for $typ
632        where
633            $typ: IntoCtx<Endian>,
634        {
635            type Error = error::Error;
636            #[inline]
637            fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> {
638                if $size > dst.len() {
639                    Err(error::Error::TooBig {
640                        size: $size,
641                        len: dst.len(),
642                    })
643                } else {
644                    <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le);
645                    Ok($size)
646                }
647            }
648        }
649        impl<'a> TryIntoCtx<Endian> for &'a $typ {
650            type Error = error::Error;
651            #[inline]
652            fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> {
653                (*self).try_into_ctx(dst, le)
654            }
655        }
656    };
657}
658
659into_ctx_float_impl!(f32, 4);
660into_ctx_float_impl!(f64, 8);
661
662impl<'a> TryFromCtx<'a, StrCtx> for &'a str {
663    type Error = error::Error;
664    #[inline]
665    /// Read a `&str` from `src` using `delimiter`
666    fn try_from_ctx(src: &'a [u8], ctx: StrCtx) -> Result<(Self, usize), Self::Error> {
667        let len = match ctx {
668            StrCtx::Length(len) => len,
669            StrCtx::Delimiter(delimiter) => src.iter().take_while(|c| **c != delimiter).count(),
670            StrCtx::DelimiterUntil(delimiter, len) => {
671                if len > src.len() {
672                    return Err(error::Error::TooBig {
673                        size: len,
674                        len: src.len(),
675                    });
676                };
677                src.iter()
678                    .take_while(|c| **c != delimiter)
679                    .take(len)
680                    .count()
681            }
682        };
683
684        if len > src.len() {
685            return Err(error::Error::TooBig {
686                size: len,
687                len: src.len(),
688            });
689        };
690
691        match str::from_utf8(&src[..len]) {
692            Ok(res) => Ok((res, len + ctx.len())),
693            Err(_) => Err(error::Error::BadInput {
694                size: src.len(),
695                msg: "invalid utf8",
696            }),
697        }
698    }
699}
700
701impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str
702where
703    T: AsRef<[u8]>,
704{
705    type Error = error::Error;
706    #[inline]
707    fn try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, usize), Self::Error> {
708        let src = src.as_ref();
709        TryFromCtx::try_from_ctx(src, ctx)
710    }
711}
712
713impl<'a> TryIntoCtx for &'a [u8] {
714    type Error = error::Error;
715    #[inline]
716    fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> {
717        let src_len = self.len() as isize;
718        let dst_len = dst.len() as isize;
719        // if src_len < 0 || dst_len < 0 || offset < 0 {
720        //     return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {src_len} dst len: {dst_len} offset: {offset}")).into())
721        // }
722        if src_len > dst_len {
723            Err(error::Error::TooBig {
724                size: self.len(),
725                len: dst.len(),
726            })
727        } else {
728            unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) };
729            Ok(self.len())
730        }
731    }
732}
733
734// TODO: make TryIntoCtx use StrCtx for awesomeness
735impl<'a> TryIntoCtx for &'a str {
736    type Error = error::Error;
737    #[inline]
738    fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> {
739        let bytes = self.as_bytes();
740        TryIntoCtx::try_into_ctx(bytes, dst, ())
741    }
742}
743
744// TODO: we can make this compile time without size_of call, but compiler probably does that anyway
745macro_rules! sizeof_impl {
746    ($ty:ty) => {
747        impl SizeWith<Endian> for $ty {
748            #[inline]
749            fn size_with(_ctx: &Endian) -> usize {
750                size_of::<$ty>()
751            }
752        }
753        impl SizeWith for $ty {
754            #[inline]
755            fn size_with(_ctx: &()) -> usize {
756                size_of::<$ty>()
757            }
758        }
759    };
760}
761
762sizeof_impl!(u8);
763sizeof_impl!(i8);
764sizeof_impl!(u16);
765sizeof_impl!(i16);
766sizeof_impl!(u32);
767sizeof_impl!(i32);
768sizeof_impl!(u64);
769sizeof_impl!(i64);
770sizeof_impl!(u128);
771sizeof_impl!(i128);
772sizeof_impl!(f32);
773sizeof_impl!(f64);
774
775impl<'a> TryFromCtx<'a, usize> for &'a [u8] {
776    type Error = error::Error;
777    #[inline]
778    fn try_from_ctx(src: &'a [u8], size: usize) -> result::Result<(Self, usize), Self::Error> {
779        if size > src.len() {
780            Err(error::Error::TooBig {
781                size,
782                len: src.len(),
783            })
784        } else {
785            Ok((&src[..size], size))
786        }
787    }
788}
789
790impl<'a, Ctx: Copy, T: TryFromCtx<'a, Ctx, Error = error::Error>, const N: usize>
791    TryFromCtx<'a, Ctx> for [T; N]
792{
793    type Error = error::Error;
794    fn try_from_ctx(src: &'a [u8], ctx: Ctx) -> Result<(Self, usize), Self::Error> {
795        let mut offset = 0;
796
797        let mut buf: [MaybeUninit<T>; N] = core::array::from_fn(|_| MaybeUninit::uninit());
798
799        let mut error_ctx = None;
800        for (idx, element) in buf.iter_mut().enumerate() {
801            match src.gread_with::<T>(&mut offset, ctx) {
802                Ok(val) => {
803                    *element = MaybeUninit::new(val);
804                }
805                Err(e) => {
806                    error_ctx = Some((e, idx));
807                    break;
808                }
809            }
810        }
811        if let Some((e, idx)) = error_ctx {
812            for element in &mut buf[0..idx].iter_mut() {
813                // SAFETY: Any element upto idx must have already been initialized, since
814                // we iterate until we encounter an error.
815                unsafe {
816                    element.assume_init_drop();
817                }
818            }
819            Err(e)
820        } else {
821            // SAFETY: we initialized each element above by preading them out, correctness
822            // of the initialized element is guaranted by pread itself
823            Ok((buf.map(|element| unsafe { element.assume_init() }), offset))
824        }
825    }
826}
827impl<Ctx: Copy, T: TryIntoCtx<Ctx, Error = error::Error>, const N: usize> TryIntoCtx<Ctx>
828    for [T; N]
829{
830    type Error = error::Error;
831    fn try_into_ctx(self, buf: &mut [u8], ctx: Ctx) -> Result<usize, Self::Error> {
832        let mut offset = 0;
833        for element in self {
834            buf.gwrite_with(element, &mut offset, ctx)?;
835        }
836        Ok(offset)
837    }
838}
839impl<Ctx, T: SizeWith<Ctx>, const N: usize> SizeWith<Ctx> for [T; N] {
840    fn size_with(ctx: &Ctx) -> usize {
841        T::size_with(ctx) * N
842    }
843}
844
845#[cfg(feature = "std")]
846impl<'a> TryFromCtx<'a> for &'a CStr {
847    type Error = error::Error;
848    #[inline]
849    fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> {
850        let cstr = CStr::from_bytes_until_nul(src).map_err(|_| error::Error::BadInput {
851            size: 0,
852            msg: "The input doesn't contain a null byte",
853        })?;
854        Ok((cstr, cstr.to_bytes_with_nul().len()))
855    }
856}
857
858#[cfg(feature = "std")]
859impl<'a> TryFromCtx<'a> for CString {
860    type Error = error::Error;
861    #[inline]
862    fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> {
863        let (raw, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(src, _ctx)?;
864        Ok((raw.to_owned(), bytes_read))
865    }
866}
867
868#[cfg(feature = "std")]
869impl<'a> TryIntoCtx for &'a CStr {
870    type Error = error::Error;
871    #[inline]
872    fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> {
873        dst.pwrite(self.to_bytes_with_nul(), 0)
874    }
875}
876
877#[cfg(feature = "std")]
878impl TryIntoCtx for CString {
879    type Error = error::Error;
880    #[inline]
881    fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> {
882        self.as_c_str().try_into_ctx(dst, ())
883    }
884}
885
886// example of marshalling to bytes, let's wait until const is an option
887// impl FromCtx for [u8; 10] {
888//     fn from_ctx(bytes: &[u8], _ctx: Endian) -> Self {
889//         let mut dst: Self = [0; 10];
890//         assert!(bytes.len() >= dst.len());
891//         unsafe {
892//             copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr(), dst.len());
893//         }
894//         dst
895//     }
896// }
897
898#[cfg(test)]
899#[cfg(feature = "std")]
900mod tests {
901    use super::*;
902
903    #[test]
904    fn parse_a_cstr() {
905        let src = CString::new("Hello World").unwrap();
906        let as_bytes = src.as_bytes_with_nul();
907
908        let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(as_bytes, ()).unwrap();
909
910        assert_eq!(bytes_read, as_bytes.len());
911        assert_eq!(got, src.as_c_str());
912    }
913
914    #[test]
915    fn round_trip_a_c_str() {
916        let src = CString::new("Hello World").unwrap();
917        let src = src.as_c_str();
918        let as_bytes = src.to_bytes_with_nul();
919
920        let mut buffer = vec![0; as_bytes.len()];
921        let bytes_written = src.try_into_ctx(&mut buffer, ()).unwrap();
922        assert_eq!(bytes_written, as_bytes.len());
923
924        let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(&buffer, ()).unwrap();
925
926        assert_eq!(bytes_read, as_bytes.len());
927        assert_eq!(got, src);
928    }
929}