//! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al //! //! The context traits are arguably the center piece of the scroll crate. In simple terms they //! define how to actually read and write, respectively, a data type from a container, being able to //! take context into account. //! //! ### Reading //! //! Types implementing [TryFromCtx](trait.TryFromCtx.html) and it's infallible cousin [FromCtx](trait.FromCtx.html) //! allow a user of [Pread::pread](../trait.Pread.html#method.pread) or respectively //! [Cread::cread](../trait.Cread.html#method.cread) and //! [IOread::ioread](../trait.IOread.html#method.ioread) to read that data type from a data source one //! of the `*read` traits has been implemented for. //! //! Implementations of `TryFromCtx` specify a source (called `This`) and an `Error` type for failed //! reads. The source defines the kind of container the type can be read from, and defaults to //! `[u8]` for any type that implements `AsRef<[u8]>`. //! //! `FromCtx` is slightly more restricted; it requires the implementer to use `[u8]` as source and //! never fail, and thus does not have an `Error` type. //! //! Types chosen here are of relevance to `Pread` implementations; of course only a container which //! can produce a source of the type `This` can be used to read a `TryFromCtx` requiring it and the //! `Error` type returned in `Err` of `Pread::pread`'s Result. //! //! ### Writing //! //! [TryIntoCtx](trait.TryIntoCtx.html) and the infallible [IntoCtx](trait.IntoCtx.html) work //! similarly to the above traits, allowing [Pwrite::pwrite](../trait.Pwrite.html#method.pwrite) or //! respectively [Cwrite::cwrite](../trait.Cwrite.html#method.cwrite) and //! [IOwrite::iowrite](../trait.IOwrite.html#method.iowrite) to write data into a byte sink for //! which one of the `*write` traits has been implemented for. //! //! `IntoCtx` is similarly restricted as `FromCtx` is to `TryFromCtx`. And equally the types chosen //! affect usable `Pwrite` implementation. //! //! ### Context //! //! Each of the traits passes along a `Ctx` to the marshalling logic. This context type contains //! any additional information that may be required to successfully parse or write the data: //! Examples would be endianness to use, field lengths of a serialized struct, or delimiters to use //! when reading/writing `&str`. The context type can be any type but must derive //! [Copy](https://doc.rust-lang.org/std/marker/trait.Copy.html). In addition if you want to use //! the `*read`-methods instead of the `*read_with` ones you must also implement //! [default::Default](https://doc.rust-lang.org/std/default/trait.Default.html). //! //! # Example //! //! Let's expand on the [previous example](../index.html#complex-use-cases). //! //! ```rust //! use scroll::{self, ctx, Pread, Endian}; //! use scroll::ctx::StrCtx; //! //! #[derive(Copy, Clone, PartialEq, Eq)] //! enum FieldSize { //! U32, //! U64 //! } //! //! // Our custom context type. As said above it has to derive Copy. //! #[derive(Copy, Clone)] //! struct Context { //! fieldsize: FieldSize, //! endianess: Endian, //! } //! //! // Our custom data type //! struct Data<'b> { //! // These u64 are encoded either as 32-bit or 64-bit wide ints. Which one it is is defined in //! // the Context. //! // Also, let's imagine they have a strict relationship: A < B < C otherwise the struct is //! // invalid. //! field_a: u64, //! field_b: u64, //! field_c: u64, //! //! // Both of these are marshalled with a prefixed length. //! name: &'b str, //! value: &'b [u8], //! } //! //! #[derive(Debug)] //! enum Error { //! // We'll return this custom error if the field* relationship doesn't hold //! BadFieldMatchup, //! Scroll(scroll::Error), //! } //! //! impl<'a> ctx::TryFromCtx<'a, Context> for Data<'a> { //! type Error = Error; //! //! // Using the explicit lifetime specification again you ensure that read data doesn't outlife //! // its source buffer without having to resort to copying. //! fn try_from_ctx (src: &'a [u8], ctx: Context) //! // the `usize` returned here is the amount of bytes read. //! -> Result<(Self, usize), Self::Error> //! { //! // The offset counter; gread and gread_with increment a given counter automatically so we //! // don't have to manually care. //! let offset = &mut 0; //! //! let field_a; //! let field_b; //! let field_c; //! //! // Switch the amount of bytes read depending on the parsing context //! if ctx.fieldsize == FieldSize::U32 { //! field_a = src.gread_with::<u32>(offset, ctx.endianess)? as u64; //! field_b = src.gread_with::<u32>(offset, ctx.endianess)? as u64; //! field_c = src.gread_with::<u32>(offset, ctx.endianess)? as u64; //! } else { //! field_a = src.gread_with::<u64>(offset, ctx.endianess)?; //! field_b = src.gread_with::<u64>(offset, ctx.endianess)?; //! field_c = src.gread_with::<u64>(offset, ctx.endianess)?; //! } //! //! // You can use type ascribition or turbofish operators, whichever you prefer. //! let namelen = src.gread_with::<u16>(offset, ctx.endianess)? as usize; //! let name: &str = src.gread_with(offset, scroll::ctx::StrCtx::Length(namelen))?; //! //! let vallen = src.gread_with::<u16>(offset, ctx.endianess)? as usize; //! let value = &src[*offset..(*offset+vallen)]; //! //! // Let's sanity check those fields, shall we? //! if ! (field_a < field_b && field_b < field_c) { //! return Err(Error::BadFieldMatchup); //! } //! //! Ok((Data { field_a, field_b, field_c, name, value }, *offset)) //! } //! } //! //! // In lieu of a complex byte buffer we hearken back to the venerable &[u8]; do note however //! // that the implementation of TryFromCtx did not specify such. In fact any type that implements //! // Pread can now read `Data` as it implements TryFromCtx. //! let bytes = b"\x00\x02\x03\x04\x01\x02\x03\x04\xde\xad\xbe\xef\x00\x08UserName\x00\x02\xCA\xFE"; //! //! // We define an appropiate context, and get going //! let contextA = Context { //! fieldsize: FieldSize::U32, //! endianess: Endian::Big, //! }; //! let data: Data = bytes.pread_with(0, contextA).unwrap(); //! //! assert_eq!(data.field_a, 0x00020304); //! assert_eq!(data.field_b, 0x01020304); //! assert_eq!(data.field_c, 0xdeadbeef); //! assert_eq!(data.name, "UserName"); //! assert_eq!(data.value, [0xCA, 0xFE]); //! //! // Here we have a context with a different FieldSize, changing parsing information at runtime. //! let contextB = Context { //! fieldsize: FieldSize::U64, //! endianess: Endian::Big, //! }; //! //! // Which will of course error with a malformed input for the context //! let err: Result<Data, Error> = bytes.pread_with(0, contextB); //! assert!(err.is_err()); //! //! let bytes_long = [0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x04,0x00,0x00,0x00,0x00,0x01,0x02,0x03, //! 0x04,0x00,0x00,0x00,0x00,0xde,0xad,0xbe,0xef,0x00,0x08,0x55,0x73,0x65,0x72, //! 0x4e,0x61,0x6d,0x65,0x00,0x02,0xCA,0xFE]; //! //! let data: Data = bytes_long.pread_with(0, contextB).unwrap(); //! //! assert_eq!(data.field_a, 0x00020304); //! assert_eq!(data.field_b, 0x01020304); //! assert_eq!(data.field_c, 0xdeadbeef); //! assert_eq!(data.name, "UserName"); //! assert_eq!(data.value, [0xCA, 0xFE]); //! //! // Ergonomic conversion, not relevant really. //! use std::convert::From; //! impl From<scroll::Error> for Error { //! fn from(error: scroll::Error) -> Error { //! Error::Scroll(error) //! } //! } //! ```
use core::mem::{size_of, MaybeUninit}; use core::ptr::copy_nonoverlapping; use core::{result, str}; #[cfg(feature = "std")] use std::ffi::{CStr, CString};
/// A trait for measuring how large something is; for a byte sequence, it will be its length. pubtrait MeasureWith<Ctx> { /// How large is `Self`, given the `ctx`? fn measure_with(&self, ctx: &Ctx) -> usize;
}
impl<Ctx, T: AsRef<[u8]>> MeasureWith<Ctx> for T { #[inline] fn measure_with(&self, _ctx: &Ctx) -> usize { self.as_ref().len()
}
}
/// The parsing context for converting a byte sequence to a `&str` /// /// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful. #[derive(Debug, Copy, Clone)] pubenum StrCtx {
Delimiter(u8),
DelimiterUntil(u8, usize),
Length(usize),
}
/// A C-style, null terminator based delimiter pubconst NULL: u8 = 0; /// A space-based delimiter pubconst SPACE: u8 = 0x20; /// A newline-based delimiter pubconst RET: u8 = 0x0a; /// A tab-based delimiter pubconst TAB: u8 = 0x09;
/// Reads `Self` from `This` using the context `Ctx`; must _not_ fail pubtrait FromCtx<Ctx: Copy = (), This: ?Sized = [u8]> { fn from_ctx(this: &This, ctx: Ctx) -> Self;
}
/// Tries to read `Self` from `This` using the context `Ctx` /// /// # Implementing Your Own Reader /// If you want to implement your own reader for a type `Foo` from some kind of buffer (say /// `[u8]`), then you need to implement this trait /// /// ```rust /// ##[cfg(feature = "std")] { /// use scroll::{self, ctx, Pread}; /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); /// /// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { /// type Error = scroll::Error; /// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { /// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } /// let n = this.pread_with(0, le)?; /// Ok((Foo(n), 2)) /// } /// } /// /// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; /// let foo = bytes.pread_with::<Foo>(0, scroll::LE).unwrap(); /// assert_eq!(Foo(0xadde), foo); /// /// let foo2 = bytes.pread_with::<Foo>(0, scroll::BE).unwrap(); /// assert_eq!(Foo(0xdeadu16), foo2); /// # } /// ``` /// /// # Advanced: Using Your Own Error in `TryFromCtx` /// ```rust /// use scroll::{self, ctx, Pread}; /// use std::error; /// use std::fmt::{self, Display}; /// // make some kind of normal error which also can transformed from a scroll error /// #[derive(Debug)] /// pub struct ExternalError {} /// /// impl Display for ExternalError { /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { /// write!(fmt, "ExternalError") /// } /// } /// /// impl error::Error for ExternalError { /// fn description(&self) -> &str { /// "ExternalError" /// } /// fn cause(&self) -> Option<&dyn error::Error> { None} /// } /// /// impl From<scroll::Error> for ExternalError { /// fn from(err: scroll::Error) -> Self { /// match err { /// _ => ExternalError{}, /// } /// } /// } /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); /// /// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { /// type Error = ExternalError; /// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { /// if this.len() <= 2 { return Err((ExternalError {}).into()) } /// let offset = &mut 0; /// let n = this.gread_with(offset, le)?; /// Ok((Foo(n), *offset)) /// } /// } /// /// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; /// let foo: Result<Foo, ExternalError> = bytes.pread(0); /// ``` pubtrait TryFromCtx<'a, Ctx: Copy = (), This: ?Sized = [u8]> where Self: 'a + Sized,
{ type Error; fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>;
}
/// Writes `Self` into `This` using the context `Ctx` pubtrait IntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { fn into_ctx(self, _: &mut This, ctx: Ctx);
}
/// Tries to write `Self` into `This` using the context `Ctx` /// To implement writing into an arbitrary byte buffer, implement `TryIntoCtx` /// # Example /// ```rust /// ##[cfg(feature = "std")] { /// use scroll::{self, ctx, LE, Endian, Pwrite}; /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); /// /// // this will use the default `DefaultCtx = scroll::Endian` /// impl ctx::TryIntoCtx<Endian> for Foo { /// // you can use your own error here too, but you will then need to specify it in fn generic parameters /// type Error = scroll::Error; /// // you can write using your own context type, see `leb128.rs` /// fn try_into_ctx(self, this: &mut [u8], le: Endian) -> Result<usize, Self::Error> { /// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } /// this.pwrite_with(self.0, 0, le)?; /// Ok(2) /// } /// } /// // now we can write a `Foo` into some buffer (in this case, a byte buffer, because that's what we implemented it for above) /// /// let mut bytes: [u8; 4] = [0, 0, 0, 0]; /// bytes.pwrite_with(Foo(0x7f), 1, LE).unwrap(); /// # } /// ``` pubtrait TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { type Error; fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result<usize, Self::Error>;
}
/// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions /// /// The rationale behind this trait is to: /// /// 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. /// 2. Allow a context based size, which is useful for 32/64 bit variants for various containers, etc. pubtrait SizeWith<Ctx = ()> { fn size_with(ctx: &Ctx) -> usize;
}
impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str where
T: AsRef<[u8]>,
{ type Error = error::Error; #[inline] fn try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, usize), Self::Error> { let src = src.as_ref();
TryFromCtx::try_from_ctx(src, ctx)
}
}
impl<'a> TryIntoCtx for &'a [u8] { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { let src_len = self.len() as isize; let dst_len = dst.len() as isize; // if src_len < 0 || dst_len < 0 || offset < 0 { // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {src_len} dst len: {dst_len} offset: {offset}")).into()) // } if src_len > dst_len {
Err(error::Error::TooBig {
size: self.len(),
len: dst.len(),
})
} else { unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) };
Ok(self.len())
}
}
}
// TODO: make TryIntoCtx use StrCtx for awesomeness impl<'a> TryIntoCtx for &'a str { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { let bytes = self.as_bytes();
TryIntoCtx::try_into_ctx(bytes, dst, ())
}
}
// TODO: we can make this compile time without size_of call, but compiler probably does that anyway
macro_rules! sizeof_impl {
($ty:ty) => { impl SizeWith<Endian> for $ty { #[inline] fn size_with(_ctx: &Endian) -> usize {
size_of::<$ty>()
}
}
};
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.