]> git.proxmox.com Git - rustc.git/blob - vendor/scroll/src/ctx.rs
New upstream version 1.48.0+dfsg1
[rustc.git] / vendor / scroll / src / ctx.rs
1 //! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al
2 //!
3 //! # Discussion
4 //!
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]`.
6 //!
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.
8 //!
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).
10 //!
11 //!
12 //! # Example
13 //!
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.
18 //!
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`.
20 //!
21 //! ```rust
22 //! use scroll::{self, ctx, Endian, Pread, BE};
23 //!
24 //! struct Data<'a> {
25 //! name: &'a str,
26 //! id: u32,
27 //! }
28 //!
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))
36 //! }
37 //! }
38 //!
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());
43 //!
44 //! ```
45
46 use core::ptr::copy_nonoverlapping;
47 use core::mem::transmute;
48 use core::mem::size_of;
49 use core::str;
50 use core::result;
51
52 #[cfg(feature = "std")]
53 use std::ffi::{CStr, CString};
54
55 use crate::error;
56 use crate::endian::Endian;
57
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;
62 }
63
64 impl<Ctx> MeasureWith<Ctx> for [u8] {
65 #[inline]
66 fn measure_with(&self, _ctx: &Ctx) -> usize {
67 self.len()
68 }
69 }
70
71 impl<Ctx, T: AsRef<[u8]>> MeasureWith<Ctx> for T {
72 #[inline]
73 fn measure_with(&self, _ctx: &Ctx) -> usize {
74 self.as_ref().len()
75 }
76 }
77
78 /// The parsing context for converting a byte sequence to a `&str`
79 ///
80 /// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful.
81 #[derive(Debug, Copy, Clone)]
82 pub enum StrCtx {
83 Delimiter(u8),
84 DelimiterUntil(u8, usize),
85 Length(usize),
86 }
87
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;
96
97 impl Default for StrCtx {
98 #[inline]
99 fn default() -> Self {
100 StrCtx::Delimiter(NULL)
101 }
102 }
103
104 impl StrCtx {
105 pub fn len(&self) -> usize {
106 match *self {
107 StrCtx::Delimiter(_) |
108 StrCtx::DelimiterUntil(_, _) => 1,
109 StrCtx::Length(_) => 0,
110 }
111 }
112
113 pub fn is_empty(&self) -> bool {
114 if let StrCtx::Length(_) = *self { true } else { false }
115 }
116 }
117
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;
121 }
122
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 {
125 type Error;
126 fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>;
127 }
128
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);
132 }
133
134 /// Tries to write `Self` into `This` using the context `Ctx`
135 pub trait TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized {
136 type Error;
137 fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result<usize, Self::Error>;
138 }
139
140 /// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions
141 ///
142 /// The rationale behind this trait is to:
143 ///
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;
148 }
149
150 macro_rules! signed_to_unsigned {
151 (i8) => {u8 };
152 (u8) => {u8 };
153 (i16) => {u16};
154 (u16) => {u16};
155 (i32) => {u32};
156 (u32) => {u32};
157 (i64) => {u64};
158 (u64) => {u64};
159 (i128) => {u128};
160 (u128) => {u128};
161 (f32) => {u32};
162 (f64) => {u64};
163 }
164
165 macro_rules! write_into {
166 ($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => ({
167 unsafe {
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);
171 }
172 });
173 }
174
175 macro_rules! into_ctx_impl {
176 ($typ:tt, $size:expr) => {
177 impl IntoCtx<Endian> for $typ {
178 #[inline]
179 fn into_ctx(self, dst: &mut [u8], le: Endian) {
180 assert!(dst.len() >= $size);
181 write_into!($typ, $size, self, dst, le);
182 }
183 }
184 impl<'a> IntoCtx<Endian> for &'a $typ {
185 #[inline]
186 fn into_ctx(self, dst: &mut [u8], le: Endian) {
187 (*self).into_ctx(dst, le)
188 }
189 }
190 impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> {
191 type Error = error::Error;
192 #[inline]
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()})
196 } else {
197 <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le);
198 Ok($size)
199 }
200 }
201 }
202 impl<'a> TryIntoCtx<Endian> for &'a $typ {
203 type Error = error::Error;
204 #[inline]
205 fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> {
206 (*self).try_into_ctx(dst, le)
207 }
208 }
209 }
210 }
211
212 macro_rules! from_ctx_impl {
213 ($typ:tt, $size:expr) => {
214 impl<'a> FromCtx<Endian> for $typ {
215 #[inline]
216 fn from_ctx(src: &[u8], le: Endian) -> Self {
217 assert!(src.len() >= $size);
218 let mut data: signed_to_unsigned!($typ) = 0;
219 unsafe {
220 copy_nonoverlapping(
221 src.as_ptr(),
222 &mut data as *mut signed_to_unsigned!($typ) as *mut u8,
223 $size);
224 }
225 (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ
226 }
227 }
228
229 impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> {
230 type Error = error::Error;
231 #[inline]
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()})
235 } else {
236 Ok((FromCtx::from_ctx(&src, le), $size))
237 }
238 }
239 }
240 // as ref
241 impl<'a, T> FromCtx<Endian, T> for $typ where T: AsRef<[u8]> {
242 #[inline]
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;
247 unsafe {
248 copy_nonoverlapping(
249 src.as_ptr(),
250 &mut data as *mut signed_to_unsigned!($typ) as *mut u8,
251 $size);
252 }
253 (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ
254 }
255 }
256
257 impl<'a, T> TryFromCtx<'a, Endian, T> for $typ where $typ: FromCtx<Endian, T>, T: AsRef<[u8]> {
258 type Error = error::Error;
259 #[inline]
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)
263 }
264 }
265 };
266 }
267
268 macro_rules! ctx_impl {
269 ($typ:tt, $size:expr) => {
270 from_ctx_impl!($typ, $size);
271 };
272 }
273
274 ctx_impl!(u8, 1);
275 ctx_impl!(i8, 1);
276 ctx_impl!(u16, 2);
277 ctx_impl!(i16, 2);
278 ctx_impl!(u32, 4);
279 ctx_impl!(i32, 4);
280 ctx_impl!(u64, 8);
281 ctx_impl!(i64, 8);
282 ctx_impl!(u128, 16);
283 ctx_impl!(i128, 16);
284
285 macro_rules! from_ctx_float_impl {
286 ($typ:tt, $size:expr) => {
287 impl<'a> FromCtx<Endian> for $typ {
288 #[inline]
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;
292 unsafe {
293 copy_nonoverlapping(
294 src.as_ptr(),
295 &mut data as *mut signed_to_unsigned!($typ) as *mut u8,
296 $size);
297 transmute(if le.is_little() { data.to_le() } else { data.to_be() })
298 }
299 }
300 }
301 impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> {
302 type Error = error::Error;
303 #[inline]
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()})
307 } else {
308 Ok((FromCtx::from_ctx(src, le), $size))
309 }
310 }
311 }
312 }
313 }
314
315 from_ctx_float_impl!(f32, 4);
316 from_ctx_float_impl!(f64, 8);
317
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);
328
329 macro_rules! into_ctx_float_impl {
330 ($typ:tt, $size:expr) => {
331 impl IntoCtx<Endian> for $typ {
332 #[inline]
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);
336 }
337 }
338 impl<'a> IntoCtx<Endian> for &'a $typ {
339 #[inline]
340 fn into_ctx(self, dst: &mut [u8], le: Endian) {
341 (*self).into_ctx(dst, le)
342 }
343 }
344 impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> {
345 type Error = error::Error;
346 #[inline]
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()})
350 } else {
351 <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le);
352 Ok($size)
353 }
354 }
355 }
356 impl<'a> TryIntoCtx<Endian> for &'a $typ {
357 type Error = error::Error;
358 #[inline]
359 fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> {
360 (*self).try_into_ctx(dst, le)
361 }
362 }
363 }
364 }
365
366 into_ctx_float_impl!(f32, 4);
367 into_ctx_float_impl!(f64, 8);
368
369 impl<'a> TryFromCtx<'a, StrCtx> for &'a str {
370 type Error = error::Error;
371 #[inline]
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) => {
378 if len > src.len() {
379 return Err(error::Error::TooBig{size: len, len: src.len()});
380 };
381 src
382 .iter()
383 .take_while(|c| **c != delimiter)
384 .take(len)
385 .count()
386 }
387 };
388
389 if len > src.len() {
390 return Err(error::Error::TooBig{size: len, len: src.len()});
391 };
392
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"})
396 }
397 }
398 }
399
400 impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str where T: AsRef<[u8]> {
401 type Error = error::Error;
402 #[inline]
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)
406 }
407 }
408
409 impl<'a> TryIntoCtx for &'a [u8] {
410 type Error = error::Error;
411 #[inline]
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())
417 // }
418 if src_len > dst_len {
419 Err(error::Error::TooBig{ size: self.len(), len: dst.len()})
420 } else {
421 unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) };
422 Ok(self.len())
423 }
424 }
425 }
426
427 // TODO: make TryIntoCtx use StrCtx for awesomeness
428 impl<'a> TryIntoCtx for &'a str {
429 type Error = error::Error;
430 #[inline]
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, ())
434 }
435 }
436
437 // TODO: we can make this compile time without size_of call, but compiler probably does that anyway
438 macro_rules! sizeof_impl {
439 ($ty:ty) => {
440 impl SizeWith<Endian> for $ty {
441 #[inline]
442 fn size_with(_ctx: &Endian) -> usize {
443 size_of::<$ty>()
444 }
445 }
446 }
447 }
448
449 sizeof_impl!(u8);
450 sizeof_impl!(i8);
451 sizeof_impl!(u16);
452 sizeof_impl!(i16);
453 sizeof_impl!(u32);
454 sizeof_impl!(i32);
455 sizeof_impl!(u64);
456 sizeof_impl!(i64);
457 sizeof_impl!(u128);
458 sizeof_impl!(i128);
459 sizeof_impl!(f32);
460 sizeof_impl!(f64);
461 sizeof_impl!(usize);
462 sizeof_impl!(isize);
463
464 impl FromCtx<Endian> for usize {
465 #[inline]
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;
470 unsafe {
471 copy_nonoverlapping(
472 src.as_ptr(),
473 &mut data as *mut usize as *mut u8,
474 size);
475 if le.is_little() { data.to_le() } else { data.to_be() }
476 }
477 }
478 }
479
480 impl<'a> TryFromCtx<'a, Endian> for usize where usize: FromCtx<Endian> {
481 type Error = error::Error;
482 #[inline]
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()})
487 } else {
488 Ok((FromCtx::from_ctx(src, le), size))
489 }
490 }
491 }
492
493 impl<'a> TryFromCtx<'a, usize> for &'a[u8] {
494 type Error = error::Error;
495 #[inline]
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()})
499 } else {
500 Ok((&src[..size], size))
501 }
502 }
503 }
504
505 impl IntoCtx<Endian> for usize {
506 #[inline]
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;
512 unsafe {
513 copy_nonoverlapping(data, dst.as_mut_ptr(), size);
514 }
515 }
516 }
517
518 impl TryIntoCtx<Endian> for usize where usize: IntoCtx<Endian> {
519 type Error = error::Error;
520 #[inline]
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()})
525 } else {
526 <usize as IntoCtx<Endian>>::into_ctx(self, dst, le);
527 Ok(size)
528 }
529 }
530 }
531
532 #[cfg(feature = "std")]
533 impl<'a> TryFromCtx<'a> for &'a CStr {
534 type Error = error::Error;
535 #[inline]
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) {
538 Some(ix) => ix,
539 None => return Err(error::Error::BadInput {
540 size: 0,
541 msg: "The input doesn't contain a null byte",
542 })
543 };
544
545 let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(&src[..=null_byte]) };
546 Ok((cstr, null_byte+1))
547 }
548 }
549
550 #[cfg(feature = "std")]
551 impl<'a> TryFromCtx<'a> for CString {
552 type Error = error::Error;
553 #[inline]
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))
557 }
558 }
559
560 #[cfg(feature = "std")]
561 impl<'a> TryIntoCtx for &'a CStr {
562 type Error = error::Error;
563 #[inline]
564 fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> {
565 let data = self.to_bytes_with_nul();
566
567 if dst.len() < data.len() {
568 Err(error::Error::TooBig {
569 size: dst.len(),
570 len: data.len(),
571 })
572 } else {
573 unsafe {
574 copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len());
575 }
576
577 Ok(data.len())
578 }
579 }
580 }
581
582 #[cfg(feature = "std")]
583 impl TryIntoCtx for CString {
584 type Error = error::Error;
585 #[inline]
586 fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> {
587 self.as_c_str().try_into_ctx(dst, ())
588 }
589 }
590
591
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());
597 // unsafe {
598 // copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr(), dst.len());
599 // }
600 // dst
601 // }
602 // }
603
604 #[cfg(test)]
605 mod tests {
606 use super::*;
607
608 #[test]
609 #[cfg(feature = "std")]
610 fn parse_a_cstr() {
611 let src = CString::new("Hello World").unwrap();
612 let as_bytes = src.as_bytes_with_nul();
613
614 let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(as_bytes, ()).unwrap();
615
616 assert_eq!(bytes_read, as_bytes.len());
617 assert_eq!(got, src.as_c_str());
618 }
619
620 #[test]
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();
626
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());
630
631 let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(&buffer, ()).unwrap();
632
633 assert_eq!(bytes_read, as_bytes.len());
634 assert_eq!(got, src);
635 }
636 }
637
638