scroll/lib.rs
1//! # Scroll
2//!
3//! ```text, no_run
4//! _______________
5//! ()==( (@==()
6//! '______________'|
7//! | |
8//! | ἀρετή |
9//! __)_____________|
10//! ()==( (@==()
11//! '--------------'
12//!
13//! ```
14//!
15//! Scroll is a library for easily and efficiently reading/writing types from data containers like
16//! byte arrays.
17//!
18//! ## Easily:
19//!
20//! Scroll sets down a number of traits:
21//!
22//! [FromCtx](ctx/trait.FromCtx.html), [IntoCtx](ctx/trait.IntoCtx.html),
23//! [TryFromCtx](ctx/trait.TryFromCtx.html) and [TryIntoCtx](ctx/trait.TryIntoCtx.html) — further
24//! explained in the [ctx module](ctx/index.html); to be implemented on custom types to allow
25//! reading, writing, and potentially fallible reading/writing respectively.
26//!
27//! [Pread](trait.Pread.html) and [Pwrite](trait.Pwrite.html) which are implemented on data
28//! containers such as byte arrays to define how to read or respectively write types implementing
29//! the *Ctx traits above.
30//! In addition scroll also defines [IOread](trait.IOread.html) and
31//! [IOwrite](trait.IOwrite.html) with additional constraits that then allow reading and writing
32//! from `std::io` [Read](https://doc.rust-lang.org/nightly/std/io/trait.Read.html) and
33//! [Write](https://doc.rust-lang.org/nightly/std/io/trait.Write.html).
34//!
35//!
36//! In most cases you can use [scroll_derive](https://docs.rs/scroll_derive) to derive sensible
37//! defaults for `Pread`, `Pwrite`, their IO counterpart and `SizeWith`. More complex situations
38//! call for manual implementation of those traits; refer to [the ctx module](ctx/index.html) for
39//! details.
40//!
41//!
42//! ## Efficiently:
43//!
44//! Reading Slices — including [&str](https://doc.rust-lang.org/std/primitive.str.html) — supports
45//! zero-copy. Scroll is designed with a `no_std` context in mind; every dependency on `std` is
46//! cfg-gated and errors need not allocate.
47//!
48//! Reads by default take only immutable references wherever possible, allowing for trivial
49//! parallelization.
50//!
51//! # Examples
52//!
53//! Let's start with a simple example
54//!
55//! ```rust
56//! use scroll::{ctx, Pread};
57//!
58//! // Let's first define some data, cfg-gated so our assertions later on hold.
59//! #[cfg(target_endian = "little")]
60//! let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef];
61//! #[cfg(target_endian = "big")]
62//! let bytes: [u8; 4] = [0xef, 0xbe, 0xad, 0xde];
63//!
64//! // We can read a u32 from the array `bytes` at offset 0.
65//! // This will use a default context for the type being parsed;
66//! // in the case of u32 this defines to use the host's endianess.
67//! let number = bytes.pread::<u32>(0).unwrap();
68//! assert_eq!(number, 0xefbeadde);
69//!
70//!
71//! // Similarly we can also read a single byte at offset 2
72//! // This time using type ascription instead of the turbofish (::<>) operator.
73//! let byte: u8 = bytes.pread(2).unwrap();
74//! #[cfg(target_endian = "little")]
75//! assert_eq!(byte, 0xbe);
76//! #[cfg(target_endian = "big")]
77//! assert_eq!(byte, 0xad);
78//!
79//!
80//! // If required we can also provide a specific parsing context; e.g. if we want to explicitly
81//! // define the endianess to use:
82//! let be_number: u32 = bytes.pread_with(0, scroll::BE).unwrap();
83//! #[cfg(target_endian = "little")]
84//! assert_eq!(be_number, 0xdeadbeef);
85//! #[cfg(target_endian = "big")]
86//! assert_eq!(be_number, 0xefbeadde);
87//!
88//! let be_number16 = bytes.pread_with::<u16>(1, scroll::BE).unwrap();
89//! #[cfg(target_endian = "little")]
90//! assert_eq!(be_number16, 0xadbe);
91//! #[cfg(target_endian = "big")]
92//! assert_eq!(be_number16, 0xbead);
93//!
94//!
95//! // Reads may fail; in this example due to a too large read for the given container.
96//! // Scroll's error type does not by default allocate to work in environments like no_std.
97//! let byte_err: scroll::Result<i64> = bytes.pread(0);
98//! assert!(byte_err.is_err());
99//!
100//!
101//! // We can parse out custom datatypes, or types with lifetimes, as long as they implement
102//! // the conversion traits `TryFromCtx/FromCtx`.
103//! // Here we use the default context for &str which parses are C-style '\0'-delimited string.
104//! let hello: &[u8] = b"hello world\0more words";
105//! let hello_world: &str = hello.pread(0).unwrap();
106//! assert_eq!("hello world", hello_world);
107//!
108//! // We can again provide a custom context; for example to parse Space-delimited strings.
109//! // As you can see while we still call `pread` changing the context can influence the output —
110//! // instead of splitting at '\0' we split at spaces
111//! let hello2: &[u8] = b"hello world\0more words";
112//! let world: &str = hello2.pread_with(6, ctx::StrCtx::Delimiter(ctx::SPACE)).unwrap();
113//! assert_eq!("world\0more", world);
114//! ```
115//!
116//! ## `std::io` API
117//!
118//! Scroll also allows reading from `std::io`. For this the types to read need to implement
119//! [FromCtx](ctx/trait.FromCtx.html) and [SizeWith](ctx/trait.SizeWith.html).
120//!
121//! ```rust
122//! ##[cfg(feature = "std")] {
123//! use std::io::Cursor;
124//! use scroll::{IOread, ctx, Endian};
125//! let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
126//! let mut cursor = Cursor::new(bytes);
127//!
128//! // IOread uses std::io::Read methods, thus the Cursor will be incremented on these reads:
129//! let prev = cursor.position();
130//!
131//! let integer = cursor.ioread_with::<u64>(Endian::Little).unwrap();
132//!
133//! let after = cursor.position();
134//!
135//! assert!(prev < after);
136//!
137//! // SizeWith allows us to define a context-sensitive size of a read type:
138//! // Contexts can have different instantiations; e.g. the `Endian` context can be either Little or
139//! // Big. This is useful if for example the context contains the word-size of fields to be
140//! // read/written, e.g. switching between ELF32 or ELF64 at runtime.
141//! let size = <u64 as ctx::SizeWith<Endian>>::size_with(&Endian::Little) as u64;
142//! assert_eq!(prev + size, after);
143//! # }
144//! ```
145//!
146//! In the same vein as IOread we can use IOwrite to write a type to anything implementing
147//! `std::io::Write`:
148//!
149//! ```rust
150//! ##[cfg(feature = "std")] {
151//! use std::io::Cursor;
152//! use scroll::{IOwrite};
153//!
154//! let mut bytes = [0x0u8; 5];
155//! let mut cursor = Cursor::new(&mut bytes[..]);
156//!
157//! // This of course once again increments the cursor position
158//! cursor.iowrite_with(0xdeadbeef as u32, scroll::BE).unwrap();
159//!
160//! assert_eq!(cursor.into_inner(), [0xde, 0xad, 0xbe, 0xef, 0x0]);
161//! # }
162//! ```
163//!
164//! ## Complex use cases
165//!
166//! Scoll is designed to be highly adaptable while providing a strong abstraction between the types
167//! being read/written and the data container containing them.
168//!
169//! In this example we'll define a custom Data and allow it to be read from an arbitrary byte
170//! buffer.
171//!
172//! ```rust
173//! use scroll::{self, ctx, Pread, Endian};
174//! use scroll::ctx::StrCtx;
175//!
176//! // Our custom context type. In a more complex situation you could for example store details on
177//! // how to write or read your type, field-sizes or other information.
178//! // In this simple example we could also do without using a custom context in the first place.
179//! #[derive(Copy, Clone)]
180//! struct Context(Endian);
181//!
182//! // Our custom data type
183//! struct Data<'zerocopy> {
184//! // This is only a reference to the actual data; we make use of scroll's zero-copy capability
185//! name: &'zerocopy str,
186//! id: u32,
187//! }
188//!
189//! // To allow for safe zero-copying scroll allows to specify lifetimes explicitly:
190//! // The context
191//! impl<'a> ctx::TryFromCtx<'a, Context> for Data<'a> {
192//! // If necessary you can set a custom error type here, which will be returned by Pread/Pwrite
193//! type Error = scroll::Error;
194//!
195//! // Using the explicit lifetime specification again you ensure that read data doesn't outlife
196//! // its source buffer without having to resort to copying.
197//! fn try_from_ctx (src: &'a [u8], ctx: Context)
198//! // the `usize` returned here is the amount of bytes read.
199//! -> Result<(Self, usize), Self::Error>
200//! {
201//! let offset = &mut 0;
202//!
203//! let id = src.gread_with(offset, ctx.0)?;
204//!
205//! // In a more serious application you would validate data here of course.
206//! let namelen: u16 = src.gread_with(offset, ctx.0)?;
207//! let name = src.gread_with::<&str>(offset, StrCtx::Length(namelen as usize))?;
208//!
209//! Ok((Data { name: name, id: id }, *offset))
210//! }
211//! }
212//!
213//! // In lieu of a complex byte buffer we hearken back to a simple &[u8]; the default source
214//! // of TryFromCtx. However, any type that implements Pread to produce a &[u8] can now read
215//! // `Data` thanks to it's implementation of TryFromCtx.
216//! let bytes = b"\x01\x02\x03\x04\x00\x08UserName";
217//! let data: Data = bytes.pread_with(0, Context(Endian::Big)).unwrap();
218//!
219//! assert_eq!(data.id, 0x01020304);
220//! assert_eq!(data.name.to_string(), "UserName".to_string());
221//! ```
222//!
223//! For further explanation of the traits and how to implement them manually refer to
224//! [Pread](trait.Pread.html) and [TryFromCtx](ctx/trait.TryFromCtx.html).
225
226#![cfg_attr(not(feature = "std"), no_std)]
227
228#[cfg(feature = "derive")]
229#[allow(unused_imports)]
230pub use scroll_derive::{IOread, IOwrite, Pread, Pwrite, SizeWith};
231
232#[cfg(feature = "std")]
233extern crate core;
234
235pub mod ctx;
236mod endian;
237mod error;
238mod greater;
239mod leb128;
240#[cfg(feature = "std")]
241mod lesser;
242mod pread;
243mod pwrite;
244
245pub use crate::endian::*;
246pub use crate::error::*;
247pub use crate::greater::*;
248pub use crate::leb128::*;
249#[cfg(feature = "std")]
250pub use crate::lesser::*;
251pub use crate::pread::*;
252pub use crate::pwrite::*;
253
254#[doc(hidden)]
255pub mod export {
256 pub use ::core::{mem, result};
257}
258
259#[allow(unused)]
260macro_rules! doc_comment {
261 ($x:expr) => {
262 #[doc = $x]
263 #[doc(hidden)]
264 mod readme_tests {}
265 };
266}
267
268#[cfg(feature = "derive")]
269doc_comment!(include_str!("../README.md"));
270
271#[cfg(test)]
272mod tests {
273 use super::LE;
274
275 #[test]
276 fn test_measure_with_bytes() {
277 use super::ctx::MeasureWith;
278 let bytes: [u8; 4] = [0xef, 0xbe, 0xad, 0xde];
279 assert_eq!(bytes.measure_with(&()), 4);
280 }
281
282 #[test]
283 fn test_measurable() {
284 use super::ctx::SizeWith;
285 assert_eq!(8, u64::size_with(&LE));
286 }
287
288 //////////////////////////////////////////////////////////////
289 // begin pread_with
290 //////////////////////////////////////////////////////////////
291
292 macro_rules! pwrite_test {
293 ($write:ident, $read:ident, $deadbeef:expr) => {
294 #[test]
295 fn $write() {
296 use super::{Pread, Pwrite, BE};
297 let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
298 let b = &mut bytes[..];
299 b.pwrite_with::<$read>($deadbeef, 0, LE).unwrap();
300 assert_eq!(b.pread_with::<$read>(0, LE).unwrap(), $deadbeef);
301 b.pwrite_with::<$read>($deadbeef, 0, BE).unwrap();
302 assert_eq!(b.pread_with::<$read>(0, BE).unwrap(), $deadbeef);
303 }
304 };
305 }
306
307 pwrite_test!(pwrite_and_pread_roundtrip_u16, u16, 0xbeef);
308 pwrite_test!(pwrite_and_pread_roundtrip_i16, i16, 0x7eef);
309 pwrite_test!(pwrite_and_pread_roundtrip_u32, u32, 0xbeefbeef);
310 pwrite_test!(pwrite_and_pread_roundtrip_i32, i32, 0x7eefbeef);
311 pwrite_test!(pwrite_and_pread_roundtrip_u64, u64, 0xbeefbeef7eef7eef);
312 pwrite_test!(pwrite_and_pread_roundtrip_i64, i64, 0x7eefbeef7eef7eef);
313
314 #[test]
315 fn pread_with_be() {
316 use super::Pread;
317 let bytes: [u8; 2] = [0x7e, 0xef];
318 let b = &bytes[..];
319 let byte: u16 = b.pread_with(0, super::BE).unwrap();
320 assert_eq!(0x7eef, byte);
321 let bytes: [u8; 2] = [0xde, 0xad];
322 let dead: u16 = bytes.pread_with(0, super::BE).unwrap();
323 assert_eq!(0xdead, dead);
324 }
325
326 #[test]
327 fn pread() {
328 use super::Pread;
329 let bytes: [u8; 2] = [0x7e, 0xef];
330 let b = &bytes[..];
331 let byte: u16 = b.pread(0).unwrap();
332 #[cfg(target_endian = "little")]
333 assert_eq!(0xef7e, byte);
334 #[cfg(target_endian = "big")]
335 assert_eq!(0x7eef, byte);
336 }
337
338 #[test]
339 fn pread_slice() {
340 use super::ctx::StrCtx;
341 use super::Pread;
342 let bytes: [u8; 2] = [0x7e, 0xef];
343 let b = &bytes[..];
344 let iserr: Result<&str, _> = b.pread_with(0, StrCtx::Length(3));
345 assert!(iserr.is_err());
346 // let bytes2: &[u8] = b.pread_with(0, 2).unwrap();
347 // assert_eq!(bytes2.len(), bytes[..].len());
348 // for i in 0..bytes2.len() {
349 // assert_eq!(bytes2[i], bytes[i])
350 // }
351 }
352
353 #[test]
354 fn pread_str() {
355 use super::ctx::*;
356 use super::Pread;
357 let bytes: [u8; 2] = [0x2e, 0x0];
358 let b = &bytes[..];
359 let s: &str = b.pread(0).unwrap();
360 #[cfg(feature = "std")]
361 println!("str: {s}");
362 assert_eq!(s.len(), bytes[..].len() - 1);
363 let bytes: &[u8] = b"hello, world!\0some_other_things";
364 let hello_world: &str = bytes.pread_with(0, StrCtx::Delimiter(NULL)).unwrap();
365 #[cfg(feature = "std")]
366 println!("{hello_world:?}");
367 assert_eq!(hello_world.len(), 13);
368 let hello: &str = bytes.pread_with(0, StrCtx::Delimiter(SPACE)).unwrap();
369 #[cfg(feature = "std")]
370 println!("{hello:?}");
371 assert_eq!(hello.len(), 6);
372 // this could result in underflow so we just try it
373 let _error = bytes.pread_with::<&str>(6, StrCtx::Delimiter(SPACE));
374 let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE));
375 #[cfg(feature = "std")]
376 println!("{error:?}");
377 assert!(error.is_ok());
378 }
379
380 /// In this test, we are testing preading
381 /// at length boundaries.
382 /// In the past, this test was supposed to test failures for `hello_world`.
383 /// Since PR#94, this test is unwrapping as we exploit
384 /// the fact that if you do &x[x.len()..] you get an empty slice.
385 #[test]
386 fn pread_str_weird() {
387 use super::ctx::*;
388 use super::Pread;
389 let bytes: &[u8] = b"";
390 let hello_world = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL));
391 #[cfg(feature = "std")]
392 println!("1 {hello_world:?}");
393 assert!(hello_world.unwrap().is_empty());
394 let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE));
395 #[cfg(feature = "std")]
396 println!("2 {error:?}");
397 assert!(error.is_err());
398 let bytes: &[u8] = b"\0";
399 let null = bytes.pread::<&str>(0).unwrap();
400 #[cfg(feature = "std")]
401 println!("3 {null:?}");
402 assert_eq!(null.len(), 0);
403 }
404
405 #[test]
406 fn pwrite_str_and_bytes() {
407 use super::ctx::*;
408 use super::{Pread, Pwrite};
409 let astring: &str = "lol hello_world lal\0ala imabytes";
410 let mut buffer = [0u8; 33];
411 buffer.pwrite(astring, 0).unwrap();
412 {
413 let hello_world = buffer
414 .pread_with::<&str>(4, StrCtx::Delimiter(SPACE))
415 .unwrap();
416 assert_eq!(hello_world, "hello_world");
417 }
418 let bytes: &[u8] = b"more\0bytes";
419 buffer.pwrite(bytes, 0).unwrap();
420 let more = bytes
421 .pread_with::<&str>(0, StrCtx::Delimiter(NULL))
422 .unwrap();
423 assert_eq!(more, "more");
424 let bytes = bytes
425 .pread_with::<&str>(more.len() + 1, StrCtx::Delimiter(NULL))
426 .unwrap();
427 assert_eq!(bytes, "bytes");
428 }
429
430 use core::fmt::{self, Display};
431
432 #[derive(Debug)]
433 pub struct ExternalError {}
434
435 impl Display for ExternalError {
436 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
437 write!(fmt, "ExternalError")
438 }
439 }
440
441 #[cfg(feature = "std")]
442 impl std::error::Error for ExternalError {
443 fn description(&self) -> &str {
444 "ExternalError"
445 }
446 fn cause(&self) -> Option<&dyn std::error::Error> {
447 None
448 }
449 }
450
451 impl From<super::Error> for ExternalError {
452 fn from(err: super::Error) -> Self {
453 //use super::Error::*;
454 match err {
455 _ => ExternalError {},
456 }
457 }
458 }
459
460 #[derive(Debug, PartialEq, Eq)]
461 pub struct Foo(u16);
462
463 impl super::ctx::TryIntoCtx<super::Endian> for Foo {
464 type Error = ExternalError;
465 fn try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result<usize, Self::Error> {
466 use super::Pwrite;
467 if this.len() < 2 {
468 return Err(ExternalError {});
469 }
470 this.pwrite_with(self.0, 0, le)?;
471 Ok(2)
472 }
473 }
474
475 impl<'a> super::ctx::TryFromCtx<'a, super::Endian> for Foo {
476 type Error = ExternalError;
477 fn try_from_ctx(this: &'a [u8], le: super::Endian) -> Result<(Self, usize), Self::Error> {
478 use super::Pread;
479 if this.len() > 2 {
480 return Err(ExternalError {});
481 }
482 let n = this.pread_with(0, le)?;
483 Ok((Foo(n), 2))
484 }
485 }
486
487 #[test]
488 fn pread_with_iter_bytes() {
489 use super::Pread;
490 let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
491 let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
492 let bytes_to = &mut bytes_to[..];
493 let bytes_from = &bytes_from[..];
494 for i in 0..bytes_from.len() {
495 bytes_to[i] = bytes_from.pread(i).unwrap();
496 }
497 assert_eq!(bytes_to, bytes_from);
498 }
499
500 //////////////////////////////////////////////////////////////
501 // end pread_with
502 //////////////////////////////////////////////////////////////
503
504 //////////////////////////////////////////////////////////////
505 // begin gread_with
506 //////////////////////////////////////////////////////////////
507 macro_rules! g_test {
508 ($read:ident, $deadbeef:expr, $typ:ty) => {
509 #[test]
510 fn $read() {
511 use super::Pread;
512 let bytes: [u8; 8] = [0xf, 0xe, 0xe, 0xb, 0xd, 0xa, 0xe, 0xd];
513 let mut offset = 0;
514 let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap();
515 assert_eq!(deadbeef, $deadbeef as $typ);
516 assert_eq!(offset, ::core::mem::size_of::<$typ>());
517 }
518 };
519 }
520
521 g_test!(simple_gread_u16, 0xe0f, u16);
522 g_test!(simple_gread_u32, 0xb0e0e0f, u32);
523 g_test!(simple_gread_u64, 0xd0e0a0d0b0e0e0f, u64);
524 g_test!(simple_gread_i64, 940700423303335439, i64);
525
526 macro_rules! simple_float_test {
527 ($read:ident, $deadbeef:expr, $typ:ty) => {
528 #[test]
529 fn $read() {
530 use super::Pread;
531 let bytes: [u8; 8] = [0u8, 0, 0, 0, 0, 0, 224, 63];
532 let mut offset = 0;
533 let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap();
534 assert_eq!(deadbeef, $deadbeef as $typ);
535 assert_eq!(offset, ::core::mem::size_of::<$typ>());
536 }
537 };
538 }
539
540 simple_float_test!(gread_f32, 0.0, f32);
541 simple_float_test!(gread_f64, 0.5, f64);
542
543 macro_rules! g_read_write_test {
544 ($read:ident, $val:expr, $typ:ty) => {
545 #[test]
546 fn $read() {
547 use super::{Pread, Pwrite, BE, LE};
548 let mut buffer = [0u8; 16];
549 let offset = &mut 0;
550 buffer.gwrite_with($val.clone(), offset, LE).unwrap();
551 let o2 = &mut 0;
552 let val: $typ = buffer.gread_with(o2, LE).unwrap();
553 assert_eq!(val, $val);
554 assert_eq!(*offset, ::core::mem::size_of::<$typ>());
555 assert_eq!(*o2, ::core::mem::size_of::<$typ>());
556 assert_eq!(*o2, *offset);
557 buffer.gwrite_with($val.clone(), offset, BE).unwrap();
558 let val: $typ = buffer.gread_with(o2, BE).unwrap();
559 assert_eq!(val, $val);
560 }
561 };
562 }
563
564 g_read_write_test!(gread_gwrite_f64_1, 0.25f64, f64);
565 g_read_write_test!(gread_gwrite_f64_2, 0.5f64, f64);
566 g_read_write_test!(gread_gwrite_f64_3, 0.064, f64);
567
568 g_read_write_test!(gread_gwrite_f32_1, 0.25f32, f32);
569 g_read_write_test!(gread_gwrite_f32_2, 0.5f32, f32);
570 g_read_write_test!(gread_gwrite_f32_3, 0.0f32, f32);
571
572 g_read_write_test!(gread_gwrite_i64_1, 0i64, i64);
573 g_read_write_test!(gread_gwrite_i64_2, -1213213211111i64, i64);
574 g_read_write_test!(gread_gwrite_i64_3, -3000i64, i64);
575
576 g_read_write_test!(gread_gwrite_i32_1, 0i32, i32);
577 g_read_write_test!(gread_gwrite_i32_2, -1213213232, i32);
578 g_read_write_test!(gread_gwrite_i32_3, -3000i32, i32);
579
580 // useful for ferreting out problems with impls
581 #[test]
582 fn gread_with_iter_bytes() {
583 use super::Pread;
584 let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
585 let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
586 let bytes_to = &mut bytes_to[..];
587 let bytes_from = &bytes_from[..];
588 let mut offset = &mut 0;
589 for i in 0..bytes_from.len() {
590 bytes_to[i] = bytes_from.gread(&mut offset).unwrap();
591 }
592 assert_eq!(bytes_to, bytes_from);
593 assert_eq!(*offset, bytes_to.len());
594 }
595
596 #[test]
597 fn gread_inout() {
598 use super::Pread;
599 let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
600 let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
601 let bytes = &bytes_from[..];
602 let offset = &mut 0;
603 bytes.gread_inout(offset, &mut bytes_to[..]).unwrap();
604 assert_eq!(bytes_to, bytes_from);
605 assert_eq!(*offset, bytes_to.len());
606 }
607
608 #[test]
609 fn gread_with_byte() {
610 use super::Pread;
611 let bytes: [u8; 1] = [0x7f];
612 let b = &bytes[..];
613 let offset = &mut 0;
614 let byte: u8 = b.gread(offset).unwrap();
615 assert_eq!(0x7f, byte);
616 assert_eq!(*offset, 1);
617 }
618
619 #[test]
620 fn gread_slice() {
621 use super::ctx::StrCtx;
622 use super::Pread;
623 let bytes: [u8; 2] = [0x7e, 0xef];
624 let b = &bytes[..];
625 let offset = &mut 0;
626 let res = b.gread_with::<&str>(offset, StrCtx::Length(3));
627 assert!(res.is_err());
628 *offset = 0;
629 let astring: [u8; 3] = [0x45, 0x42, 0x44];
630 let string = astring.gread_with::<&str>(offset, StrCtx::Length(2));
631 match &string {
632 Ok(_) => {}
633 Err(_err) => {
634 #[cfg(feature = "std")]
635 println!("{_err}");
636 panic!();
637 }
638 }
639 assert_eq!(string.unwrap(), "EB");
640 *offset = 0;
641 let bytes2: &[u8] = b.gread_with(offset, 2).unwrap();
642 assert_eq!(*offset, 2);
643 assert_eq!(bytes2.len(), bytes[..].len());
644 for i in 0..bytes2.len() {
645 assert_eq!(bytes2[i], bytes[i])
646 }
647 }
648
649 /////////////////////////////////////////////////////////////////
650 // end gread_with
651 /////////////////////////////////////////////////////////////////
652}