1 //! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al
5 //! Implementors of `TryFromCtx` automatically grant any client user of `pread, pwrite, gread, gwrite` the ability to parse their structure out of the source it has been implemented for, typically `&[u8]`.
7 //! The implementor only needs to specify the error type, and the type of their size, and then implement the parsing/marshalling logic given a byte sequence, starting at the offset `pread`, et. al was called at, with the context you have implemented it for.
9 //! Returning the size allows dynamic content (e.g., `&str`s) to be parsed alongside fixed size content (e.g., `u64`). The parsing context is any information you the implementor need to correctly parse out your datatype - this could be the endianness of the type, more offsets, or other complex data. The only requirement is that your `Ctx` be `Copy`, and hence encourages lightweight contexts (but this isn't required of course).
14 //! Suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary
15 //! byte buffer. In order to do this, we need to provide a `TryFromCtx` impl for our datatype. In particular, if we
16 //! do this for the `[u8]` target, with a "parsing contex", `YourCtx`, you will automatically get access to
17 //! calling `pread_with::<YourDatatype>(offset, your_ctx)` on arrays of bytes.
19 //! In the example below, we implement `TryFromCtx` using the `Endian` parsing context provided by `scroll`, which is used to specifying the endianness at which numbers should be parsed, but you could provide anything, as long as it implements `Copy`.
22 //! use scroll::{self, ctx, Endian, Pread, BE};
29 //! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> {
30 //! type Error = scroll::Error;
31 //! fn try_from_ctx (src: &'a [u8], ctx: Endian)
32 //! -> Result<(Self, usize), Self::Error> {
33 //! let name = src.pread::<&str>(0)?;
34 //! let id = src.pread_with(name.len() + 1, ctx)?;
35 //! Ok((Data { name: name, id: id }, name.len() + 1 + 4))
39 //! let bytes = b"UserName\x00\x01\x02\x03\x04";
40 //! let data = bytes.pread_with::<Data>(0, BE).unwrap();
41 //! assert_eq!(data.id, 0x01020304);
42 //! assert_eq!(data.name.to_string(), "UserName".to_string());
46 use core
::ptr
::copy_nonoverlapping
;
47 use core
::mem
::transmute
;
48 use core
::mem
::size_of
;
52 #[cfg(feature = "std")]
53 use std
::ffi
::{CStr, CString}
;
56 use crate::endian
::Endian
;
58 /// A trait for measuring how large something is; for a byte sequence, it will be its length.
59 pub trait MeasureWith
<Ctx
> {
60 /// How large is `Self`, given the `ctx`?
61 fn measure_with(&self, ctx
: &Ctx
) -> usize;
64 impl<Ctx
> MeasureWith
<Ctx
> for [u8] {
66 fn measure_with(&self, _ctx
: &Ctx
) -> usize {
71 impl<Ctx
, T
: AsRef
<[u8]>> MeasureWith
<Ctx
> for T
{
73 fn measure_with(&self, _ctx
: &Ctx
) -> usize {
78 /// The parsing context for converting a byte sequence to a `&str`
80 /// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful.
81 #[derive(Debug, Copy, Clone)]
84 DelimiterUntil(u8, usize),
88 /// A C-style, null terminator based delimiter
89 pub const NULL
: u8 = 0;
90 /// A space-based delimiter
91 pub const SPACE
: u8 = 0x20;
92 /// A newline-based delimiter
93 pub const RET
: u8 = 0x0a;
94 /// A tab-based delimiter
95 pub const TAB
: u8 = 0x09;
97 impl Default
for StrCtx
{
99 fn default() -> Self {
100 StrCtx
::Delimiter(NULL
)
105 pub fn len(&self) -> usize {
107 StrCtx
::Delimiter(_
) |
108 StrCtx
::DelimiterUntil(_
, _
) => 1,
109 StrCtx
::Length(_
) => 0,
113 pub fn is_empty(&self) -> bool
{
114 if let StrCtx
::Length(_
) = *self { true }
else { false }
118 /// Reads `Self` from `This` using the context `Ctx`; must _not_ fail
119 pub trait FromCtx
<Ctx
: Copy
= (), This
: ?Sized
= [u8]> {
120 fn from_ctx(this
: &This
, ctx
: Ctx
) -> Self;
123 /// Tries to read `Self` from `This` using the context `Ctx`
124 pub trait TryFromCtx
<'a
, Ctx
: Copy
= (), This
: ?Sized
= [u8]> where Self: 'a
+ Sized
{
126 fn try_from_ctx(from
: &'a This
, ctx
: Ctx
) -> Result
<(Self, usize), Self::Error
>;
129 /// Writes `Self` into `This` using the context `Ctx`
130 pub trait IntoCtx
<Ctx
: Copy
= (), This
: ?Sized
= [u8]>: Sized
{
131 fn into_ctx(self, _
: &mut This
, ctx
: Ctx
);
134 /// Tries to write `Self` into `This` using the context `Ctx`
135 pub trait TryIntoCtx
<Ctx
: Copy
= (), This
: ?Sized
= [u8]>: Sized
{
137 fn try_into_ctx(self, _
: &mut This
, ctx
: Ctx
) -> Result
<usize, Self::Error
>;
140 /// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions
142 /// The rationale behind this trait is to:
144 /// 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.
145 /// 2. Allow a context based size, which is useful for 32/64 bit variants for various containers, etc.
146 pub trait SizeWith
<Ctx
= ()> {
147 fn size_with(ctx
: &Ctx
) -> usize;
150 macro_rules
! signed_to_unsigned
{
165 macro_rules
! write_into
{
166 ($typ
:ty
, $size
:expr
, $n
:expr
, $dst
:expr
, $endian
:expr
) => ({
168 assert
!($dst
.len() >= $size
);
169 let bytes
= transmute
::<$typ
, [u8; $size
]>(if $endian
.is_little() { $n.to_le() }
else { $n.to_be() }
);
170 copy_nonoverlapping((&bytes
).as_ptr(), $dst
.as_mut_ptr(), $size
);
175 macro_rules
! into_ctx_impl
{
176 ($typ
:tt
, $size
:expr
) => {
177 impl IntoCtx
<Endian
> for $typ
{
179 fn into_ctx(self, dst
: &mut [u8], le
: Endian
) {
180 assert
!(dst
.len() >= $size
);
181 write_into
!($typ
, $size
, self, dst
, le
);
184 impl<'a
> IntoCtx
<Endian
> for &'a $typ
{
186 fn into_ctx(self, dst
: &mut [u8], le
: Endian
) {
187 (*self).into_ctx(dst
, le
)
190 impl TryIntoCtx
<Endian
> for $typ
where $typ
: IntoCtx
<Endian
> {
191 type Error
= error
::Error
;
193 fn try_into_ctx(self, dst
: &mut [u8], le
: Endian
) -> error
::Result
<usize> {
194 if $size
> dst
.len () {
195 Err(error
::Error
::TooBig{size: $size, len: dst.len()}
)
197 <$typ
as IntoCtx
<Endian
>>::into_ctx(self, dst
, le
);
202 impl<'a
> TryIntoCtx
<Endian
> for &'a $typ
{
203 type Error
= error
::Error
;
205 fn try_into_ctx(self, dst
: &mut [u8], le
: Endian
) -> error
::Result
<usize> {
206 (*self).try_into_ctx(dst
, le
)
212 macro_rules
! from_ctx_impl
{
213 ($typ
:tt
, $size
:expr
) => {
214 impl<'a
> FromCtx
<Endian
> for $typ
{
216 fn from_ctx(src
: &[u8], le
: Endian
) -> Self {
217 assert
!(src
.len() >= $size
);
218 let mut data
: signed_to_unsigned
!($typ
) = 0;
222 &mut data
as *mut signed_to_unsigned
!($typ
) as *mut u8,
225 (if le
.is_little() { data.to_le() }
else { data.to_be() }
) as $typ
229 impl<'a
> TryFromCtx
<'a
, Endian
> for $typ
where $typ
: FromCtx
<Endian
> {
230 type Error
= error
::Error
;
232 fn try_from_ctx(src
: &'a
[u8], le
: Endian
) -> result
::Result
<(Self, usize), Self::Error
> {
233 if $size
> src
.len () {
234 Err(error
::Error
::TooBig{size: $size, len: src.len()}
)
236 Ok((FromCtx
::from_ctx(&src
, le
), $size
))
241 impl<'a
, T
> FromCtx
<Endian
, T
> for $typ
where T
: AsRef
<[u8]> {
243 fn from_ctx(src
: &T
, le
: Endian
) -> Self {
244 let src
= src
.as_ref();
245 assert
!(src
.len() >= $size
);
246 let mut data
: signed_to_unsigned
!($typ
) = 0;
250 &mut data
as *mut signed_to_unsigned
!($typ
) as *mut u8,
253 (if le
.is_little() { data.to_le() }
else { data.to_be() }
) as $typ
257 impl<'a
, T
> TryFromCtx
<'a
, Endian
, T
> for $typ
where $typ
: FromCtx
<Endian
, T
>, T
: AsRef
<[u8]> {
258 type Error
= error
::Error
;
260 fn try_from_ctx(src
: &'a T
, le
: Endian
) -> result
::Result
<(Self, usize), Self::Error
> {
261 let src
= src
.as_ref();
262 Self::try_from_ctx(src
, le
)
268 macro_rules
! ctx_impl
{
269 ($typ
:tt
, $size
:expr
) => {
270 from_ctx_impl
!($typ
, $size
);
285 macro_rules
! from_ctx_float_impl
{
286 ($typ
:tt
, $size
:expr
) => {
287 impl<'a
> FromCtx
<Endian
> for $typ
{
289 fn from_ctx(src
: &[u8], le
: Endian
) -> Self {
290 assert
!(src
.len() >= ::core
::mem
::size_of
::<Self>());
291 let mut data
: signed_to_unsigned
!($typ
) = 0;
295 &mut data
as *mut signed_to_unsigned
!($typ
) as *mut u8,
297 transmute(if le
.is_little() { data.to_le() }
else { data.to_be() }
)
301 impl<'a
> TryFromCtx
<'a
, Endian
> for $typ
where $typ
: FromCtx
<Endian
> {
302 type Error
= error
::Error
;
304 fn try_from_ctx(src
: &'a
[u8], le
: Endian
) -> result
::Result
<(Self, usize), Self::Error
> {
305 if $size
> src
.len () {
306 Err(error
::Error
::TooBig{size: $size, len: src.len()}
)
308 Ok((FromCtx
::from_ctx(src
, le
), $size
))
315 from_ctx_float_impl
!(f32, 4);
316 from_ctx_float_impl
!(f64, 8);
318 into_ctx_impl
!(u8, 1);
319 into_ctx_impl
!(i8, 1);
320 into_ctx_impl
!(u16, 2);
321 into_ctx_impl
!(i16, 2);
322 into_ctx_impl
!(u32, 4);
323 into_ctx_impl
!(i32, 4);
324 into_ctx_impl
!(u64, 8);
325 into_ctx_impl
!(i64, 8);
326 into_ctx_impl
!(u128
, 16);
327 into_ctx_impl
!(i128
, 16);
329 macro_rules
! into_ctx_float_impl
{
330 ($typ
:tt
, $size
:expr
) => {
331 impl IntoCtx
<Endian
> for $typ
{
333 fn into_ctx(self, dst
: &mut [u8], le
: Endian
) {
334 assert
!(dst
.len() >= $size
);
335 write_into
!(signed_to_unsigned
!($typ
), $size
, transmute
::<$typ
, signed_to_unsigned
!($typ
)>(self), dst
, le
);
338 impl<'a
> IntoCtx
<Endian
> for &'a $typ
{
340 fn into_ctx(self, dst
: &mut [u8], le
: Endian
) {
341 (*self).into_ctx(dst
, le
)
344 impl TryIntoCtx
<Endian
> for $typ
where $typ
: IntoCtx
<Endian
> {
345 type Error
= error
::Error
;
347 fn try_into_ctx(self, dst
: &mut [u8], le
: Endian
) -> error
::Result
<usize> {
348 if $size
> dst
.len () {
349 Err(error
::Error
::TooBig{size: $size, len: dst.len()}
)
351 <$typ
as IntoCtx
<Endian
>>::into_ctx(self, dst
, le
);
356 impl<'a
> TryIntoCtx
<Endian
> for &'a $typ
{
357 type Error
= error
::Error
;
359 fn try_into_ctx(self, dst
: &mut [u8], le
: Endian
) -> error
::Result
<usize> {
360 (*self).try_into_ctx(dst
, le
)
366 into_ctx_float_impl
!(f32, 4);
367 into_ctx_float_impl
!(f64, 8);
369 impl<'a
> TryFromCtx
<'a
, StrCtx
> for &'a
str {
370 type Error
= error
::Error
;
372 /// Read a `&str` from `src` using `delimiter`
373 fn try_from_ctx(src
: &'a
[u8], ctx
: StrCtx
) -> Result
<(Self, usize), Self::Error
> {
374 let len
= match ctx
{
375 StrCtx
::Length(len
) => len
,
376 StrCtx
::Delimiter(delimiter
) => src
.iter().take_while(|c
| **c
!= delimiter
).count(),
377 StrCtx
::DelimiterUntil(delimiter
, len
) => {
379 return Err(error
::Error
::TooBig{size: len, len: src.len()}
);
383 .take_while(|c
| **c
!= delimiter
)
390 return Err(error
::Error
::TooBig{size: len, len: src.len()}
);
393 match str::from_utf8(&src
[..len
]) {
394 Ok(res
) => Ok((res
, len
+ ctx
.len())),
395 Err(_
) => Err(error
::Error
::BadInput{size: src.len(), msg: "invalid utf8"}
)
400 impl<'a
, T
> TryFromCtx
<'a
, StrCtx
, T
> for &'a
str where T
: AsRef
<[u8]> {
401 type Error
= error
::Error
;
403 fn try_from_ctx(src
: &'a T
, ctx
: StrCtx
) -> result
::Result
<(Self, usize), Self::Error
> {
404 let src
= src
.as_ref();
405 TryFromCtx
::try_from_ctx(src
, ctx
)
409 impl<'a
> TryIntoCtx
for &'a
[u8] {
410 type Error
= error
::Error
;
412 fn try_into_ctx(self, dst
: &mut [u8], _ctx
: ()) -> error
::Result
<usize> {
413 let src_len
= self.len() as isize;
414 let dst_len
= dst
.len() as isize;
415 // if src_len < 0 || dst_len < 0 || offset < 0 {
416 // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {} dst len: {} offset: {}", src_len, dst_len, offset)).into())
418 if src_len
> dst_len
{
419 Err(error
::Error
::TooBig{ size: self.len(), len: dst.len()}
)
421 unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) }
;
427 // TODO: make TryIntoCtx use StrCtx for awesomeness
428 impl<'a
> TryIntoCtx
for &'a
str {
429 type Error
= error
::Error
;
431 fn try_into_ctx(self, dst
: &mut [u8], _ctx
: ()) -> error
::Result
<usize> {
432 let bytes
= self.as_bytes();
433 TryIntoCtx
::try_into_ctx(bytes
, dst
, ())
437 // TODO: we can make this compile time without size_of call, but compiler probably does that anyway
438 macro_rules
! sizeof_impl
{
440 impl SizeWith
<Endian
> for $ty
{
442 fn size_with(_ctx
: &Endian
) -> usize {
464 impl FromCtx
<Endian
> for usize {
466 fn from_ctx(src
: &[u8], le
: Endian
) -> Self {
467 let size
= ::core
::mem
::size_of
::<Self>();
468 assert
!(src
.len() >= size
);
469 let mut data
: usize = 0;
473 &mut data
as *mut usize as *mut u8,
475 if le
.is_little() { data.to_le() }
else { data.to_be() }
480 impl<'a
> TryFromCtx
<'a
, Endian
> for usize where usize: FromCtx
<Endian
> {
481 type Error
= error
::Error
;
483 fn try_from_ctx(src
: &'a
[u8], le
: Endian
) -> result
::Result
<(Self, usize), Self::Error
> {
484 let size
= ::core
::mem
::size_of
::<usize>();
485 if size
> src
.len () {
486 Err(error
::Error
::TooBig{size, len: src.len()}
)
488 Ok((FromCtx
::from_ctx(src
, le
), size
))
493 impl<'a
> TryFromCtx
<'a
, usize> for &'a
[u8] {
494 type Error
= error
::Error
;
496 fn try_from_ctx(src
: &'a
[u8], size
: usize) -> result
::Result
<(Self, usize), Self::Error
> {
497 if size
> src
.len () {
498 Err(error
::Error
::TooBig{size, len: src.len()}
)
500 Ok((&src
[..size
], size
))
505 impl IntoCtx
<Endian
> for usize {
507 fn into_ctx(self, dst
: &mut [u8], le
: Endian
) {
508 let size
= ::core
::mem
::size_of
::<Self>();
509 assert
!(dst
.len() >= size
);
510 let mut data
= if le
.is_little() { self.to_le() }
else { self.to_be() }
;
511 let data
= &mut data
as *mut usize as *mut u8;
513 copy_nonoverlapping(data
, dst
.as_mut_ptr(), size
);
518 impl TryIntoCtx
<Endian
> for usize where usize: IntoCtx
<Endian
> {
519 type Error
= error
::Error
;
521 fn try_into_ctx(self, dst
: &mut [u8], le
: Endian
) -> error
::Result
<usize> {
522 let size
= ::core
::mem
::size_of
::<usize>();
523 if size
> dst
.len() {
524 Err(error
::Error
::TooBig{size, len: dst.len()}
)
526 <usize as IntoCtx
<Endian
>>::into_ctx(self, dst
, le
);
532 #[cfg(feature = "std")]
533 impl<'a
> TryFromCtx
<'a
> for &'a CStr
{
534 type Error
= error
::Error
;
536 fn try_from_ctx(src
: &'a
[u8], _ctx
: ()) -> result
::Result
<(Self, usize), Self::Error
> {
537 let null_byte
= match src
.iter().position(|b
| *b
== 0) {
539 None
=> return Err(error
::Error
::BadInput
{
541 msg
: "The input doesn't contain a null byte",
545 let cstr
= unsafe { CStr::from_bytes_with_nul_unchecked(&src[..=null_byte]) }
;
546 Ok((cstr
, null_byte
+1))
550 #[cfg(feature = "std")]
551 impl<'a
> TryFromCtx
<'a
> for CString
{
552 type Error
= error
::Error
;
554 fn try_from_ctx(src
: &'a
[u8], _ctx
: ()) -> result
::Result
<(Self, usize), Self::Error
> {
555 let (raw
, bytes_read
) = <&CStr
as TryFromCtx
>::try_from_ctx(src
, _ctx
)?
;
556 Ok((raw
.to_owned(), bytes_read
))
560 #[cfg(feature = "std")]
561 impl<'a
> TryIntoCtx
for &'a CStr
{
562 type Error
= error
::Error
;
564 fn try_into_ctx(self, dst
: &mut [u8], _ctx
: ()) -> error
::Result
<usize> {
565 let data
= self.to_bytes_with_nul();
567 if dst
.len() < data
.len() {
568 Err(error
::Error
::TooBig
{
574 copy_nonoverlapping(data
.as_ptr(), dst
.as_mut_ptr(), data
.len());
582 #[cfg(feature = "std")]
583 impl TryIntoCtx
for CString
{
584 type Error
= error
::Error
;
586 fn try_into_ctx(self, dst
: &mut [u8], _ctx
: ()) -> error
::Result
<usize> {
587 self.as_c_str().try_into_ctx(dst
, ())
592 // example of marshalling to bytes, let's wait until const is an option
593 // impl FromCtx for [u8; 10] {
594 // fn from_ctx(bytes: &[u8], _ctx: Endian) -> Self {
595 // let mut dst: Self = [0; 10];
596 // assert!(bytes.len() >= dst.len());
598 // copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr(), dst.len());
609 #[cfg(feature = "std")]
611 let src
= CString
::new("Hello World").unwrap();
612 let as_bytes
= src
.as_bytes_with_nul();
614 let (got
, bytes_read
) = <&CStr
as TryFromCtx
>::try_from_ctx(as_bytes
, ()).unwrap();
616 assert_eq
!(bytes_read
, as_bytes
.len());
617 assert_eq
!(got
, src
.as_c_str());
621 #[cfg(feature = "std")]
622 fn round_trip_a_c_str() {
623 let src
= CString
::new("Hello World").unwrap();
624 let src
= src
.as_c_str();
625 let as_bytes
= src
.to_bytes_with_nul();
627 let mut buffer
= vec
![0; as_bytes
.len()];
628 let bytes_written
= src
.try_into_ctx(&mut buffer
, ()).unwrap();
629 assert_eq
!(bytes_written
, as_bytes
.len());
631 let (got
, bytes_read
) = <&CStr
as TryFromCtx
>::try_from_ctx(&buffer
, ()).unwrap();
633 assert_eq
!(bytes_read
, as_bytes
.len());
634 assert_eq
!(got
, src
);