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 183 use core::ptr::copy_nonoverlapping; 184 use core::mem::transmute; 185 use core::mem::size_of; 186 use core::str; 187 use core::result; 188 189 #[cfg(feature = "std")] 190 use std::ffi::{CStr, CString}; 191 192 use crate::error; 193 use crate::endian::Endian; 194 195 /// A trait for measuring how large something is; for a byte sequence, it will be its length. 196 pub trait MeasureWith<Ctx> { 197 /// How large is `Self`, given the `ctx`? measure_with(&self, ctx: &Ctx) -> usize198 fn measure_with(&self, ctx: &Ctx) -> usize; 199 } 200 201 impl<Ctx> MeasureWith<Ctx> for [u8] { 202 #[inline] measure_with(&self, _ctx: &Ctx) -> usize203 fn measure_with(&self, _ctx: &Ctx) -> usize { 204 self.len() 205 } 206 } 207 208 impl<Ctx, T: AsRef<[u8]>> MeasureWith<Ctx> for T { 209 #[inline] measure_with(&self, _ctx: &Ctx) -> usize210 fn measure_with(&self, _ctx: &Ctx) -> usize { 211 self.as_ref().len() 212 } 213 } 214 215 /// The parsing context for converting a byte sequence to a `&str` 216 /// 217 /// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful. 218 #[derive(Debug, Copy, Clone)] 219 pub enum StrCtx { 220 Delimiter(u8), 221 DelimiterUntil(u8, usize), 222 Length(usize), 223 } 224 225 /// A C-style, null terminator based delimiter 226 pub const NULL: u8 = 0; 227 /// A space-based delimiter 228 pub const SPACE: u8 = 0x20; 229 /// A newline-based delimiter 230 pub const RET: u8 = 0x0a; 231 /// A tab-based delimiter 232 pub const TAB: u8 = 0x09; 233 234 impl Default for StrCtx { 235 #[inline] default() -> Self236 fn default() -> Self { 237 StrCtx::Delimiter(NULL) 238 } 239 } 240 241 impl StrCtx { len(&self) -> usize242 pub fn len(&self) -> usize { 243 match *self { 244 StrCtx::Delimiter(_) | 245 StrCtx::DelimiterUntil(_, _) => 1, 246 StrCtx::Length(_) => 0, 247 } 248 } 249 is_empty(&self) -> bool250 pub fn is_empty(&self) -> bool { 251 if let StrCtx::Length(_) = *self { true } else { false } 252 } 253 } 254 255 /// Reads `Self` from `This` using the context `Ctx`; must _not_ fail 256 pub trait FromCtx<Ctx: Copy = (), This: ?Sized = [u8]> { from_ctx(this: &This, ctx: Ctx) -> Self257 fn from_ctx(this: &This, ctx: Ctx) -> Self; 258 } 259 260 /// Tries to read `Self` from `This` using the context `Ctx` 261 /// 262 /// # Implementing Your Own Reader 263 /// If you want to implement your own reader for a type `Foo` from some kind of buffer (say 264 /// `[u8]`), then you need to implement this trait 265 /// 266 /// ```rust 267 /// use scroll::{self, ctx, Pread}; 268 /// #[derive(Debug, PartialEq, Eq)] 269 /// pub struct Foo(u16); 270 /// 271 /// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { 272 /// type Error = scroll::Error; 273 /// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { 274 /// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } 275 /// let n = this.pread_with(0, le)?; 276 /// Ok((Foo(n), 2)) 277 /// } 278 /// } 279 /// 280 /// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; 281 /// let foo = bytes.pread_with::<Foo>(0, scroll::LE).unwrap(); 282 /// assert_eq!(Foo(0xadde), foo); 283 /// 284 /// let foo2 = bytes.pread_with::<Foo>(0, scroll::BE).unwrap(); 285 /// assert_eq!(Foo(0xdeadu16), foo2); 286 /// ``` 287 /// 288 /// # Advanced: Using Your Own Error in `TryFromCtx` 289 /// ```rust 290 /// use scroll::{self, ctx, Pread}; 291 /// use std::error; 292 /// use std::fmt::{self, Display}; 293 /// // make some kind of normal error which also can transformed from a scroll error 294 /// #[derive(Debug)] 295 /// pub struct ExternalError {} 296 /// 297 /// impl Display for ExternalError { 298 /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 299 /// write!(fmt, "ExternalError") 300 /// } 301 /// } 302 /// 303 /// impl error::Error for ExternalError { 304 /// fn description(&self) -> &str { 305 /// "ExternalError" 306 /// } 307 /// fn cause(&self) -> Option<&error::Error> { None} 308 /// } 309 /// 310 /// impl From<scroll::Error> for ExternalError { 311 /// fn from(err: scroll::Error) -> Self { 312 /// match err { 313 /// _ => ExternalError{}, 314 /// } 315 /// } 316 /// } 317 /// #[derive(Debug, PartialEq, Eq)] 318 /// pub struct Foo(u16); 319 /// 320 /// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { 321 /// type Error = ExternalError; 322 /// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { 323 /// if this.len() <= 2 { return Err((ExternalError {}).into()) } 324 /// let offset = &mut 0; 325 /// let n = this.gread_with(offset, le)?; 326 /// Ok((Foo(n), *offset)) 327 /// } 328 /// } 329 /// 330 /// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; 331 /// let foo: Result<Foo, ExternalError> = bytes.pread(0); 332 /// ``` 333 pub trait TryFromCtx<'a, Ctx: Copy = (), This: ?Sized = [u8]> where Self: 'a + Sized { 334 type Error; try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>335 fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>; 336 } 337 338 /// Writes `Self` into `This` using the context `Ctx` 339 pub trait IntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { into_ctx(self, _: &mut This, ctx: Ctx)340 fn into_ctx(self, _: &mut This, ctx: Ctx); 341 } 342 343 /// Tries to write `Self` into `This` using the context `Ctx` 344 /// To implement writing into an arbitrary byte buffer, implement `TryIntoCtx` 345 /// # Example 346 /// ```rust 347 /// use scroll::{self, ctx, LE, Endian, Pwrite}; 348 /// #[derive(Debug, PartialEq, Eq)] 349 /// pub struct Foo(u16); 350 /// 351 /// // this will use the default `DefaultCtx = scroll::Endian` 352 /// impl ctx::TryIntoCtx<Endian> for Foo { 353 /// // you can use your own error here too, but you will then need to specify it in fn generic parameters 354 /// type Error = scroll::Error; 355 /// // you can write using your own context type, see `leb128.rs` 356 /// fn try_into_ctx(self, this: &mut [u8], le: Endian) -> Result<usize, Self::Error> { 357 /// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } 358 /// this.pwrite_with(self.0, 0, le)?; 359 /// Ok(2) 360 /// } 361 /// } 362 /// // now we can write a `Foo` into some buffer (in this case, a byte buffer, because that's what we implemented it for above) 363 /// 364 /// let mut bytes: [u8; 4] = [0, 0, 0, 0]; 365 /// bytes.pwrite_with(Foo(0x7f), 1, LE).unwrap(); 366 /// ``` 367 pub trait TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { 368 type Error; try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result<usize, Self::Error>369 fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result<usize, Self::Error>; 370 } 371 372 /// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions 373 /// 374 /// The rationale behind this trait is to: 375 /// 376 /// 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. 377 /// 2. Allow a context based size, which is useful for 32/64 bit variants for various containers, etc. 378 pub trait SizeWith<Ctx = ()> { size_with(ctx: &Ctx) -> usize379 fn size_with(ctx: &Ctx) -> usize; 380 } 381 382 macro_rules! signed_to_unsigned { 383 (i8) => {u8 }; 384 (u8) => {u8 }; 385 (i16) => {u16}; 386 (u16) => {u16}; 387 (i32) => {u32}; 388 (u32) => {u32}; 389 (i64) => {u64}; 390 (u64) => {u64}; 391 (i128) => {u128}; 392 (u128) => {u128}; 393 (f32) => {u32}; 394 (f64) => {u64}; 395 } 396 397 macro_rules! write_into { 398 ($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => ({ 399 unsafe { 400 assert!($dst.len() >= $size); 401 let bytes = transmute::<$typ, [u8; $size]>(if $endian.is_little() { $n.to_le() } else { $n.to_be() }); 402 copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); 403 } 404 }); 405 } 406 407 macro_rules! into_ctx_impl { 408 ($typ:tt, $size:expr) => { 409 impl IntoCtx<Endian> for $typ { 410 #[inline] 411 fn into_ctx(self, dst: &mut [u8], le: Endian) { 412 assert!(dst.len() >= $size); 413 write_into!($typ, $size, self, dst, le); 414 } 415 } 416 impl<'a> IntoCtx<Endian> for &'a $typ { 417 #[inline] 418 fn into_ctx(self, dst: &mut [u8], le: Endian) { 419 (*self).into_ctx(dst, le) 420 } 421 } 422 impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> { 423 type Error = error::Error; 424 #[inline] 425 fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { 426 if $size > dst.len () { 427 Err(error::Error::TooBig{size: $size, len: dst.len()}) 428 } else { 429 <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le); 430 Ok($size) 431 } 432 } 433 } 434 impl<'a> TryIntoCtx<Endian> for &'a $typ { 435 type Error = error::Error; 436 #[inline] 437 fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { 438 (*self).try_into_ctx(dst, le) 439 } 440 } 441 } 442 } 443 444 macro_rules! from_ctx_impl { 445 ($typ:tt, $size:expr) => { 446 impl<'a> FromCtx<Endian> for $typ { 447 #[inline] 448 fn from_ctx(src: &[u8], le: Endian) -> Self { 449 assert!(src.len() >= $size); 450 let mut data: signed_to_unsigned!($typ) = 0; 451 unsafe { 452 copy_nonoverlapping( 453 src.as_ptr(), 454 &mut data as *mut signed_to_unsigned!($typ) as *mut u8, 455 $size); 456 } 457 (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ 458 } 459 } 460 461 impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> { 462 type Error = error::Error; 463 #[inline] 464 fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { 465 if $size > src.len () { 466 Err(error::Error::TooBig{size: $size, len: src.len()}) 467 } else { 468 Ok((FromCtx::from_ctx(&src, le), $size)) 469 } 470 } 471 } 472 // as ref 473 impl<'a, T> FromCtx<Endian, T> for $typ where T: AsRef<[u8]> { 474 #[inline] 475 fn from_ctx(src: &T, le: Endian) -> Self { 476 let src = src.as_ref(); 477 assert!(src.len() >= $size); 478 let mut data: signed_to_unsigned!($typ) = 0; 479 unsafe { 480 copy_nonoverlapping( 481 src.as_ptr(), 482 &mut data as *mut signed_to_unsigned!($typ) as *mut u8, 483 $size); 484 } 485 (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ 486 } 487 } 488 489 impl<'a, T> TryFromCtx<'a, Endian, T> for $typ where $typ: FromCtx<Endian, T>, T: AsRef<[u8]> { 490 type Error = error::Error; 491 #[inline] 492 fn try_from_ctx(src: &'a T, le: Endian) -> result::Result<(Self, usize), Self::Error> { 493 let src = src.as_ref(); 494 Self::try_from_ctx(src, le) 495 } 496 } 497 }; 498 } 499 500 macro_rules! ctx_impl { 501 ($typ:tt, $size:expr) => { 502 from_ctx_impl!($typ, $size); 503 }; 504 } 505 506 ctx_impl!(u8, 1); 507 ctx_impl!(i8, 1); 508 ctx_impl!(u16, 2); 509 ctx_impl!(i16, 2); 510 ctx_impl!(u32, 4); 511 ctx_impl!(i32, 4); 512 ctx_impl!(u64, 8); 513 ctx_impl!(i64, 8); 514 ctx_impl!(u128, 16); 515 ctx_impl!(i128, 16); 516 517 macro_rules! from_ctx_float_impl { 518 ($typ:tt, $size:expr) => { 519 impl<'a> FromCtx<Endian> for $typ { 520 #[inline] 521 fn from_ctx(src: &[u8], le: Endian) -> Self { 522 assert!(src.len() >= ::core::mem::size_of::<Self>()); 523 let mut data: signed_to_unsigned!($typ) = 0; 524 unsafe { 525 copy_nonoverlapping( 526 src.as_ptr(), 527 &mut data as *mut signed_to_unsigned!($typ) as *mut u8, 528 $size); 529 transmute(if le.is_little() { data.to_le() } else { data.to_be() }) 530 } 531 } 532 } 533 impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> { 534 type Error = error::Error; 535 #[inline] 536 fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { 537 if $size > src.len () { 538 Err(error::Error::TooBig{size: $size, len: src.len()}) 539 } else { 540 Ok((FromCtx::from_ctx(src, le), $size)) 541 } 542 } 543 } 544 } 545 } 546 547 from_ctx_float_impl!(f32, 4); 548 from_ctx_float_impl!(f64, 8); 549 550 into_ctx_impl!(u8, 1); 551 into_ctx_impl!(i8, 1); 552 into_ctx_impl!(u16, 2); 553 into_ctx_impl!(i16, 2); 554 into_ctx_impl!(u32, 4); 555 into_ctx_impl!(i32, 4); 556 into_ctx_impl!(u64, 8); 557 into_ctx_impl!(i64, 8); 558 into_ctx_impl!(u128, 16); 559 into_ctx_impl!(i128, 16); 560 561 macro_rules! into_ctx_float_impl { 562 ($typ:tt, $size:expr) => { 563 impl IntoCtx<Endian> for $typ { 564 #[inline] 565 fn into_ctx(self, dst: &mut [u8], le: Endian) { 566 assert!(dst.len() >= $size); 567 write_into!(signed_to_unsigned!($typ), $size, transmute::<$typ, signed_to_unsigned!($typ)>(self), dst, le); 568 } 569 } 570 impl<'a> IntoCtx<Endian> for &'a $typ { 571 #[inline] 572 fn into_ctx(self, dst: &mut [u8], le: Endian) { 573 (*self).into_ctx(dst, le) 574 } 575 } 576 impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> { 577 type Error = error::Error; 578 #[inline] 579 fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { 580 if $size > dst.len () { 581 Err(error::Error::TooBig{size: $size, len: dst.len()}) 582 } else { 583 <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le); 584 Ok($size) 585 } 586 } 587 } 588 impl<'a> TryIntoCtx<Endian> for &'a $typ { 589 type Error = error::Error; 590 #[inline] 591 fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { 592 (*self).try_into_ctx(dst, le) 593 } 594 } 595 } 596 } 597 598 into_ctx_float_impl!(f32, 4); 599 into_ctx_float_impl!(f64, 8); 600 601 impl<'a> TryFromCtx<'a, StrCtx> for &'a str { 602 type Error = error::Error; 603 #[inline] 604 /// Read a `&str` from `src` using `delimiter` try_from_ctx(src: &'a [u8], ctx: StrCtx) -> Result<(Self, usize), Self::Error>605 fn try_from_ctx(src: &'a [u8], ctx: StrCtx) -> Result<(Self, usize), Self::Error> { 606 let len = match ctx { 607 StrCtx::Length(len) => len, 608 StrCtx::Delimiter(delimiter) => src.iter().take_while(|c| **c != delimiter).count(), 609 StrCtx::DelimiterUntil(delimiter, len) => { 610 if len > src.len() { 611 return Err(error::Error::TooBig{size: len, len: src.len()}); 612 }; 613 src 614 .iter() 615 .take_while(|c| **c != delimiter) 616 .take(len) 617 .count() 618 } 619 }; 620 621 if len > src.len() { 622 return Err(error::Error::TooBig{size: len, len: src.len()}); 623 }; 624 625 match str::from_utf8(&src[..len]) { 626 Ok(res) => Ok((res, len + ctx.len())), 627 Err(_) => Err(error::Error::BadInput{size: src.len(), msg: "invalid utf8"}) 628 } 629 } 630 } 631 632 impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str where T: AsRef<[u8]> { 633 type Error = error::Error; 634 #[inline] try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, usize), Self::Error>635 fn try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, usize), Self::Error> { 636 let src = src.as_ref(); 637 TryFromCtx::try_from_ctx(src, ctx) 638 } 639 } 640 641 impl<'a> TryIntoCtx for &'a [u8] { 642 type Error = error::Error; 643 #[inline] try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize>644 fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { 645 let src_len = self.len() as isize; 646 let dst_len = dst.len() as isize; 647 // if src_len < 0 || dst_len < 0 || offset < 0 { 648 // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {} dst len: {} offset: {}", src_len, dst_len, offset)).into()) 649 // } 650 if src_len > dst_len { 651 Err(error::Error::TooBig{ size: self.len(), len: dst.len()}) 652 } else { 653 unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) }; 654 Ok(self.len()) 655 } 656 } 657 } 658 659 // TODO: make TryIntoCtx use StrCtx for awesomeness 660 impl<'a> TryIntoCtx for &'a str { 661 type Error = error::Error; 662 #[inline] try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize>663 fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { 664 let bytes = self.as_bytes(); 665 TryIntoCtx::try_into_ctx(bytes, dst, ()) 666 } 667 } 668 669 // TODO: we can make this compile time without size_of call, but compiler probably does that anyway 670 macro_rules! sizeof_impl { 671 ($ty:ty) => { 672 impl SizeWith<Endian> for $ty { 673 #[inline] 674 fn size_with(_ctx: &Endian) -> usize { 675 size_of::<$ty>() 676 } 677 } 678 } 679 } 680 681 sizeof_impl!(u8); 682 sizeof_impl!(i8); 683 sizeof_impl!(u16); 684 sizeof_impl!(i16); 685 sizeof_impl!(u32); 686 sizeof_impl!(i32); 687 sizeof_impl!(u64); 688 sizeof_impl!(i64); 689 sizeof_impl!(u128); 690 sizeof_impl!(i128); 691 sizeof_impl!(f32); 692 sizeof_impl!(f64); 693 sizeof_impl!(usize); 694 sizeof_impl!(isize); 695 696 impl FromCtx<Endian> for usize { 697 #[inline] from_ctx(src: &[u8], le: Endian) -> Self698 fn from_ctx(src: &[u8], le: Endian) -> Self { 699 let size = ::core::mem::size_of::<Self>(); 700 assert!(src.len() >= size); 701 let mut data: usize = 0; 702 unsafe { 703 copy_nonoverlapping( 704 src.as_ptr(), 705 &mut data as *mut usize as *mut u8, 706 size); 707 if le.is_little() { data.to_le() } else { data.to_be() } 708 } 709 } 710 } 711 712 impl<'a> TryFromCtx<'a, Endian> for usize where usize: FromCtx<Endian> { 713 type Error = error::Error; 714 #[inline] try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error>715 fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { 716 let size = ::core::mem::size_of::<usize>(); 717 if size > src.len () { 718 Err(error::Error::TooBig{size, len: src.len()}) 719 } else { 720 Ok((FromCtx::from_ctx(src, le), size)) 721 } 722 } 723 } 724 725 impl<'a> TryFromCtx<'a, usize> for &'a[u8] { 726 type Error = error::Error; 727 #[inline] try_from_ctx(src: &'a [u8], size: usize) -> result::Result<(Self, usize), Self::Error>728 fn try_from_ctx(src: &'a [u8], size: usize) -> result::Result<(Self, usize), Self::Error> { 729 if size > src.len () { 730 Err(error::Error::TooBig{size, len: src.len()}) 731 } else { 732 Ok((&src[..size], size)) 733 } 734 } 735 } 736 737 impl IntoCtx<Endian> for usize { 738 #[inline] into_ctx(self, dst: &mut [u8], le: Endian)739 fn into_ctx(self, dst: &mut [u8], le: Endian) { 740 let size = ::core::mem::size_of::<Self>(); 741 assert!(dst.len() >= size); 742 let mut data = if le.is_little() { self.to_le() } else { self.to_be() }; 743 let data = &mut data as *mut usize as *mut u8; 744 unsafe { 745 copy_nonoverlapping(data, dst.as_mut_ptr(), size); 746 } 747 } 748 } 749 750 impl TryIntoCtx<Endian> for usize where usize: IntoCtx<Endian> { 751 type Error = error::Error; 752 #[inline] try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize>753 fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { 754 let size = ::core::mem::size_of::<usize>(); 755 if size > dst.len() { 756 Err(error::Error::TooBig{size, len: dst.len()}) 757 } else { 758 <usize as IntoCtx<Endian>>::into_ctx(self, dst, le); 759 Ok(size) 760 } 761 } 762 } 763 764 #[cfg(feature = "std")] 765 impl<'a> TryFromCtx<'a> for &'a CStr { 766 type Error = error::Error; 767 #[inline] try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error>768 fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { 769 let null_byte = match src.iter().position(|b| *b == 0) { 770 Some(ix) => ix, 771 None => return Err(error::Error::BadInput { 772 size: 0, 773 msg: "The input doesn't contain a null byte", 774 }) 775 }; 776 777 let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(&src[..=null_byte]) }; 778 Ok((cstr, null_byte+1)) 779 } 780 } 781 782 #[cfg(feature = "std")] 783 impl<'a> TryFromCtx<'a> for CString { 784 type Error = error::Error; 785 #[inline] try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error>786 fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { 787 let (raw, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(src, _ctx)?; 788 Ok((raw.to_owned(), bytes_read)) 789 } 790 } 791 792 #[cfg(feature = "std")] 793 impl<'a> TryIntoCtx for &'a CStr { 794 type Error = error::Error; 795 #[inline] try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize>796 fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { 797 let data = self.to_bytes_with_nul(); 798 799 if dst.len() < data.len() { 800 Err(error::Error::TooBig { 801 size: dst.len(), 802 len: data.len(), 803 }) 804 } else { 805 unsafe { 806 copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len()); 807 } 808 809 Ok(data.len()) 810 } 811 } 812 } 813 814 #[cfg(feature = "std")] 815 impl TryIntoCtx for CString { 816 type Error = error::Error; 817 #[inline] try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize>818 fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { 819 self.as_c_str().try_into_ctx(dst, ()) 820 } 821 } 822 823 824 // example of marshalling to bytes, let's wait until const is an option 825 // impl FromCtx for [u8; 10] { 826 // fn from_ctx(bytes: &[u8], _ctx: Endian) -> Self { 827 // let mut dst: Self = [0; 10]; 828 // assert!(bytes.len() >= dst.len()); 829 // unsafe { 830 // copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr(), dst.len()); 831 // } 832 // dst 833 // } 834 // } 835 836 #[cfg(test)] 837 mod tests { 838 use super::*; 839 840 #[test] 841 #[cfg(feature = "std")] parse_a_cstr()842 fn parse_a_cstr() { 843 let src = CString::new("Hello World").unwrap(); 844 let as_bytes = src.as_bytes_with_nul(); 845 846 let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(as_bytes, ()).unwrap(); 847 848 assert_eq!(bytes_read, as_bytes.len()); 849 assert_eq!(got, src.as_c_str()); 850 } 851 852 #[test] 853 #[cfg(feature = "std")] round_trip_a_c_str()854 fn round_trip_a_c_str() { 855 let src = CString::new("Hello World").unwrap(); 856 let src = src.as_c_str(); 857 let as_bytes = src.to_bytes_with_nul(); 858 859 let mut buffer = vec![0; as_bytes.len()]; 860 let bytes_written = src.try_into_ctx(&mut buffer, ()).unwrap(); 861 assert_eq!(bytes_written, as_bytes.len()); 862 863 let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(&buffer, ()).unwrap(); 864 865 assert_eq!(bytes_read, as_bytes.len()); 866 assert_eq!(got, src); 867 } 868 } 869 870 871