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}