1 //! # Scroll
2 //!
3 //! ```text, no_run
4 //!         _______________
5 //!    ()==(              (@==()
6 //!         '______________'|
7 //!           |             |
8 //!           |   ἀρετή     |
9 //!         __)_____________|
10 //!    ()==(               (@==()
11 //!         '--------------'
12 //!
13 //! ```
14 //!
15 //! Scroll is a library for efficiently and easily reading/writing types from byte arrays. All the builtin types are supported, e.g., `u32`, `i8`, etc., where the type is specified as a type parameter, or type inferred when possible. In addition, it supports zero-copy reading of string slices, or any other kind of slice.  The library can be used in a no_std context as well; the [Error](enum.Error.html) type only has the `IO` and `String` variants if the default features are used, and is `no_std` safe when compiled without default features.
16 //!
17 //! There are 3 traits for reading that you can import:
18 //!
19 //! 1. [Pread](trait.Pread.html), for reading (immutable) data at an offset;
20 //! 2. [Gread](trait.Gread.html), for reading data at an offset which automatically gets incremented by the size;
21 //! 3. [IOread](trait.IOread.html), for reading _simple_ data out of a `std::io::Read` based interface, e.g., a stream. (**Note**: only available when compiled with `std`)
22 //!
23 //! Each of these interfaces also have their corresponding writer versions as well, e.g., [Pwrite](trait.Pwrite.html), [Gwrite](trait.Gwrite.html), and [IOwrite](trait.IOwrite.html), respectively.
24 //!
25 //! Most familiar will likely be the `Pread` trait (inspired from the C function), which in our case takes an immutable reference to self, an immutable offset to read at, (and _optionally_ a parsing context, more on that later), and then returns the deserialized value.
26 //!
27 //! Because self is immutable, _**all** reads can be performed in parallel_ and hence are trivially parallelizable.
28 //!
29 //! For most usecases, you can use [scroll_derive](https://docs.rs/scroll_derive) to annotate your types with `derive(Pread, Pwrite, IOread, IOwrite, SizeWith)` to automatically add sensible derive defaults, and you should be ready to roll.  For more complex usescases, you can implement the conversion traits yourself, see the [context module](ctx/index.html) for more information.
30 //!
31 //! # Example
32 //!
33 //! A simple example demonstrates its flexibility:
34 //!
35 //! ```rust
36 //! use scroll::{ctx, Pread, LE};
37 //! let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef];
38 //!
39 //! // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style
40 //! let number: u32 = bytes.pread::<u32>(0).unwrap();
41 //! // ...or a byte, with type ascription on the binding.
42 //! let byte: u8 = bytes.pread(0).unwrap();
43 //!
44 //! //If the type is known another way by the compiler, say reading into a struct field, we can omit the turbofish, and type ascription altogether!
45 //!
46 //! // If we want, we can explicitly add a endianness to read with by calling `pread_with`.
47 //! // The following reads a u32 out of `b` with Big Endian byte order, at offset 0
48 //! let be_number: u32 = bytes.pread_with(0, scroll::BE).unwrap();
49 //! // or a u16 - specify the type either on the variable or with the beloved turbofish
50 //! let be_number2 = bytes.pread_with::<u16>(2, scroll::BE).unwrap();
51 //!
52 //! // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound
53 //! let byte: scroll::Result<i64> = bytes.pread(0);
54 //!
55 //! // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array!
56 //!
57 //! // We can parse out custom datatypes, or types with lifetimes
58 //! // if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely)
59 //! let hello: &[u8] = b"hello_world\0more words";
60 //! let hello_world: &str = hello.pread(0).unwrap();
61 //! assert_eq!("hello_world", hello_world);
62 //!
63 //! // ... and this parses the string if its space separated!
64 //! use scroll::ctx::*;
65 //! let spaces: &[u8] = b"hello world some junk";
66 //! let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE)).unwrap();
67 //! assert_eq!("world", world);
68 //! ```
69 //!
70 //! # `std::io` API
71 //!
72 //! Scroll can also read/write simple types from a `std::io::Read` or `std::io::Write` implementor. The  built-in numeric types are taken care of for you.  If you want to read a custom type, you need to implement the [FromCtx](trait.FromCtx.html) (_how_ to parse) and [SizeWith](ctx/trait.SizeWith.html) (_how_ big the parsed thing will be) traits.  You must compile with default features. For example:
73 //!
74 //! ```rust
75 //! use std::io::Cursor;
76 //! use scroll::IOread;
77 //! let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
78 //! let mut bytes = Cursor::new(bytes_);
79 //!
80 //! // this will bump the cursor's Seek
81 //! let foo = bytes.ioread::<u64>().unwrap();
82 //! // ..ditto
83 //! let bar = bytes.ioread::<u32>().unwrap();
84 //! ```
85 //!
86 //! Similarly, we can write to anything that implements `std::io::Write` quite naturally:
87 //!
88 //! ```rust
89 //! use scroll::{IOwrite, LE, BE};
90 //! use std::io::{Write, Cursor};
91 //!
92 //! let mut bytes = [0x0u8; 10];
93 //! let mut cursor = Cursor::new(&mut bytes[..]);
94 //! cursor.write_all(b"hello").unwrap();
95 //! cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap();
96 //! assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]);
97 //! ```
98 //!
99 //! # Advanced Uses
100 //!
101 //! Scroll is designed to be highly configurable - it allows you to implement various context (`Ctx`) sensitive traits, which then grants the implementor _automatic_ uses of the `Pread` and/or `Pwrite` traits.
102 //!
103 //! For example, suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary
104 //! byte buffer. In order to do this, we need to provide a [TryFromCtx](trait.TryFromCtx.html) impl for our datatype.
105 //!
106 //! In particular, if we do this for the `[u8]` target, using the convention `(usize, YourCtx)`, you will automatically get access to
107 //! calling `pread_with::<YourDatatype>` on arrays of bytes.
108 //!
109 //! ```rust
110 //! use scroll::{self, ctx, Pread, BE, Endian};
111 //!
112 //! struct Data<'a> {
113 //!   name: &'a str,
114 //!   id: u32,
115 //! }
116 //!
117 //! // note the lifetime specified here
118 //! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> {
119 //!   type Error = scroll::Error;
120 //!   type Size = usize;
121 //!   // and the lifetime annotation on `&'a [u8]` here
122 //!   fn try_from_ctx (src: &'a [u8], endian: Endian)
123 //!     -> Result<(Self, Self::Size), Self::Error> {
124 //!     let offset = &mut 0;
125 //!     let name = src.gread::<&str>(offset)?;
126 //!     let id = src.gread_with(offset, endian)?;
127 //!     Ok((Data { name: name, id: id }, *offset))
128 //!   }
129 //! }
130 //!
131 //! let bytes = b"UserName\x00\x01\x02\x03\x04";
132 //! let data = bytes.pread_with::<Data>(0, BE).unwrap();
133 //! assert_eq!(data.id, 0x01020304);
134 //! assert_eq!(data.name.to_string(), "UserName".to_string());
135 //! ```
136 //!
137 //! Please see the [Pread documentation examples](trait.Pread.html#implementing-your-own-reader)
138 
139 #![cfg_attr(not(feature = "std"), no_std)]
140 
141 #[cfg(feature = "derive")]
142 #[allow(unused_imports)]
143 #[macro_use]
144 extern crate scroll_derive;
145 
146 #[cfg(feature = "derive")]
147 #[doc(hidden)]
148 pub use scroll_derive::*;
149 
150 #[cfg(feature = "std")]
151 extern crate core;
152 
153 pub mod ctx;
154 mod pread;
155 mod pwrite;
156 mod greater;
157 mod error;
158 mod endian;
159 mod leb128;
160 #[cfg(feature = "std")]
161 mod lesser;
162 
163 pub use endian::*;
164 pub use pread::*;
165 pub use pwrite::*;
166 pub use greater::*;
167 pub use error::*;
168 pub use leb128::*;
169 #[cfg(feature = "std")]
170 pub use lesser::*;
171 
172 #[doc(hidden)]
173 pub mod export {
174     pub use ::core::result;
175     pub use ::core::mem;
176 }
177 
178 #[cfg(test)]
179 mod tests {
180     #[allow(overflowing_literals)]
181     use super::{LE};
182 
183     #[test]
test_measure_with_bytes()184     fn test_measure_with_bytes() {
185         use super::ctx::MeasureWith;
186         let bytes: [u8; 4] = [0xef, 0xbe, 0xad, 0xde];
187         assert_eq!(bytes.measure_with(&()), 4);
188     }
189 
190     #[test]
test_measurable()191     fn test_measurable() {
192         use super::ctx::SizeWith;
193         assert_eq!(8, u64::size_with(&LE));
194     }
195 
196     //////////////////////////////////////////////////////////////
197     // begin pread_with
198     //////////////////////////////////////////////////////////////
199 
200     macro_rules! pwrite_test {
201         ($write:ident, $read:ident, $deadbeef:expr) => {
202             #[test]
203             fn $write() {
204                 use super::{Pwrite, Pread, BE};
205                 let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
206                 let b = &mut bytes[..];
207                 b.pwrite_with::<$read>($deadbeef, 0, LE).unwrap();
208                 assert_eq!(b.pread_with::<$read>(0, LE).unwrap(), $deadbeef);
209                 b.pwrite_with::<$read>($deadbeef, 0, BE).unwrap();
210                 assert_eq!(b.pread_with::<$read>(0, BE).unwrap(), $deadbeef);
211             }
212         }
213     }
214 
215     pwrite_test!(pwrite_and_pread_roundtrip_u16, u16, 0xbeef);
216     pwrite_test!(pwrite_and_pread_roundtrip_i16, i16, 0x7eef);
217     pwrite_test!(pwrite_and_pread_roundtrip_u32, u32, 0xbeefbeef);
218     pwrite_test!(pwrite_and_pread_roundtrip_i32, i32, 0x7eefbeef);
219     pwrite_test!(pwrite_and_pread_roundtrip_u64, u64, 0xbeefbeef7eef7eef);
220     pwrite_test!(pwrite_and_pread_roundtrip_i64, i64, 0x7eefbeef7eef7eef);
221 
222     #[test]
pread_with_be()223     fn pread_with_be() {
224         use super::{Pread};
225         let bytes: [u8; 2] = [0x7e, 0xef];
226         let b = &bytes[..];
227         let byte: u16 = b.pread_with(0, super::BE).unwrap();
228         assert_eq!(0x7eef, byte);
229         let bytes: [u8; 2] = [0xde, 0xad];
230         let dead: u16 = bytes.pread_with(0, super::BE).unwrap();
231         assert_eq!(0xdead, dead);
232     }
233 
234     #[test]
pread()235     fn pread() {
236         use super::{Pread};
237         let bytes: [u8; 2] = [0x7e, 0xef];
238         let b = &bytes[..];
239         let byte: u16 = b.pread(0).unwrap();
240         #[cfg(target_endian = "little")]
241         assert_eq!(0xef7e, byte);
242         #[cfg(target_endian = "big")]
243         assert_eq!(0x7eef, byte);
244     }
245 
246     #[test]
pread_slice()247     fn pread_slice() {
248         use super::{Pread};
249         use super::ctx::StrCtx;
250         let bytes: [u8; 2] = [0x7e, 0xef];
251         let b = &bytes[..];
252         let iserr: Result<&str, _>  = b.pread_with(0, StrCtx::Length(3));
253         assert!(iserr.is_err());
254         // let bytes2: &[u8]  = b.pread_with(0, 2).unwrap();
255         // assert_eq!(bytes2.len(), bytes[..].len());
256         // for i in 0..bytes2.len() {
257         //     assert_eq!(bytes2[i], bytes[i])
258         // }
259     }
260 
261     #[test]
pread_str()262     fn pread_str() {
263         use super::Pread;
264         use super::ctx::*;
265         let bytes: [u8; 2] = [0x2e, 0x0];
266         let b = &bytes[..];
267         let s: &str  = b.pread(0).unwrap();
268         println!("str: {}", s);
269         assert_eq!(s.len(), bytes[..].len() - 1);
270         let bytes: &[u8] = b"hello, world!\0some_other_things";
271         let hello_world: &str = bytes.pread_with(0, StrCtx::Delimiter(NULL)).unwrap();
272         println!("{:?}", &hello_world);
273         assert_eq!(hello_world.len(), 13);
274         let hello: &str = bytes.pread_with(0, StrCtx::Delimiter(SPACE)).unwrap();
275         println!("{:?}", &hello);
276         assert_eq!(hello.len(), 6);
277         // this could result in underflow so we just try it
278         let _error = bytes.pread_with::<&str>(6, StrCtx::Delimiter(SPACE));
279         let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE));
280         println!("{:?}", &error);
281         assert!(error.is_ok());
282     }
283 
284     #[test]
pread_str_weird()285     fn pread_str_weird() {
286         use super::Pread;
287         use super::ctx::*;
288         let bytes: &[u8] = b"";
289         let hello_world = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL));
290         println!("1 {:?}", &hello_world);
291         assert_eq!(hello_world.is_err(), true);
292         let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE));
293         println!("2 {:?}", &error);
294         assert!(error.is_err());
295         let bytes: &[u8] = b"\0";
296         let null  = bytes.pread::<&str>(0).unwrap();
297         println!("3 {:?}", &null);
298         assert_eq!(null.len(), 0);
299     }
300 
301     #[test]
pwrite_str_and_bytes()302     fn pwrite_str_and_bytes() {
303         use super::{Pread, Pwrite};
304         use super::ctx::*;
305         let astring: &str = "lol hello_world lal\0ala imabytes";
306         let mut buffer = [0u8; 33];
307         buffer.pwrite(astring, 0).unwrap();
308         {
309             let hello_world = buffer.pread_with::<&str>(4, StrCtx::Delimiter(SPACE)).unwrap();
310             assert_eq!(hello_world, "hello_world");
311         }
312         let bytes: &[u8] = b"more\0bytes";
313         buffer.pwrite(bytes, 0).unwrap();
314         let more = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)).unwrap();
315         assert_eq!(more, "more");
316         let bytes = bytes.pread_with::<&str>(more.len() + 1, StrCtx::Delimiter(NULL)).unwrap();
317         assert_eq!(bytes, "bytes");
318     }
319 
320     use std::error;
321     use std::fmt::{self, Display};
322 
323     #[derive(Debug)]
324     pub struct ExternalError {}
325 
326     impl Display for ExternalError {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result327         fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
328             write!(fmt, "ExternalError")
329         }
330     }
331 
332     impl error::Error for ExternalError {
description(&self) -> &str333         fn description(&self) -> &str {
334             "ExternalError"
335         }
cause(&self) -> Option<&error::Error>336         fn cause(&self) -> Option<&error::Error> { None}
337     }
338 
339     impl From<super::Error> for ExternalError {
from(err: super::Error) -> Self340         fn from(err: super::Error) -> Self {
341             //use super::Error::*;
342             match err {
343                 _ => ExternalError{},
344             }
345         }
346     }
347 
348     #[derive(Debug, PartialEq, Eq)]
349     pub struct Foo(u16);
350 
351     impl super::ctx::TryIntoCtx<super::Endian> for Foo {
352         type Error = ExternalError;
353         type Size = usize;
try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result<Self::Size, Self::Error>354         fn try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result<Self::Size, Self::Error> {
355             use super::Pwrite;
356             if this.len() < 2 { return Err((ExternalError {}).into()) }
357             this.pwrite_with(self.0, 0, le)?;
358             Ok(2)
359         }
360     }
361 
362     impl<'a> super::ctx::TryFromCtx<'a, super::Endian> for Foo {
363         type Error = ExternalError;
364         type Size = usize;
try_from_ctx(this: &'a [u8], le: super::Endian) -> Result<(Self, Self::Size), Self::Error>365         fn try_from_ctx(this: &'a [u8], le: super::Endian) -> Result<(Self, Self::Size), Self::Error> {
366             use super::Pread;
367             if this.len() > 2 { return Err((ExternalError {}).into()) }
368             let n = this.pread_with(0, le)?;
369             Ok((Foo(n), 2))
370         }
371     }
372 
373     #[test]
pread_with_iter_bytes()374     fn pread_with_iter_bytes() {
375         use super::{Pread};
376         let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
377         let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
378         let bytes_to = &mut bytes_to[..];
379         let bytes_from = &bytes_from[..];
380         for i in 0..bytes_from.len() {
381             bytes_to[i] = bytes_from.pread(i).unwrap();
382         }
383         assert_eq!(bytes_to, bytes_from);
384     }
385 
386     //////////////////////////////////////////////////////////////
387     // end pread_with
388     //////////////////////////////////////////////////////////////
389 
390     //////////////////////////////////////////////////////////////
391     // begin gread_with
392     //////////////////////////////////////////////////////////////
393     macro_rules! g_test {
394         ($read:ident, $deadbeef:expr, $typ:ty) => {
395             #[test]
396             fn $read() {
397                 use super::Pread;
398                 let bytes: [u8; 8] = [0xf, 0xe, 0xe, 0xb, 0xd, 0xa, 0xe, 0xd];
399                 let mut offset = 0;
400                 let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap();
401                 assert_eq!(deadbeef, $deadbeef as $typ);
402                 assert_eq!(offset, ::std::mem::size_of::<$typ>());
403             }
404         }
405     }
406 
407     g_test!(simple_gread_u16, 0xe0f, u16);
408     g_test!(simple_gread_u32, 0xb0e0e0f, u32);
409     g_test!(simple_gread_u64, 0xd0e0a0d0b0e0e0f, u64);
410     g_test!(simple_gread_i64, 940700423303335439, i64);
411 
412     macro_rules! simple_float_test {
413         ($read:ident, $deadbeef:expr, $typ:ty) => {
414             #[test]
415             fn $read() {
416                 use super::Pread;
417                 let bytes: [u8; 8] = [0u8, 0, 0, 0, 0, 0, 224, 63];
418                 let mut offset = 0;
419                 let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap();
420                 assert_eq!(deadbeef, $deadbeef as $typ);
421                 assert_eq!(offset, ::std::mem::size_of::<$typ>());
422             }
423         };
424     }
425 
426     simple_float_test!(gread_f32, 0.0, f32);
427     simple_float_test!(gread_f64, 0.5, f64);
428 
429     macro_rules! g_read_write_test {
430         ($read:ident, $val:expr, $typ:ty) => {
431             #[test]
432             fn $read() {
433                 use super::{LE, BE, Pread, Pwrite};
434                 let mut buffer = [0u8; 16];
435                 let offset = &mut 0;
436                 buffer.gwrite_with($val.clone(), offset, LE).unwrap();
437                 let o2 = &mut 0;
438                 let val: $typ = buffer.gread_with(o2, LE).unwrap();
439                 assert_eq!(val, $val);
440                 assert_eq!(*offset, ::std::mem::size_of::<$typ>());
441                 assert_eq!(*o2, ::std::mem::size_of::<$typ>());
442                 assert_eq!(*o2, *offset);
443                 buffer.gwrite_with($val.clone(), offset, BE).unwrap();
444                 let val: $typ = buffer.gread_with(o2, BE).unwrap();
445                 assert_eq!(val, $val);
446             }
447         };
448     }
449 
450     g_read_write_test!(gread_gwrite_f64_1, 0.25f64, f64);
451     g_read_write_test!(gread_gwrite_f64_2, 0.5f64, f64);
452     g_read_write_test!(gread_gwrite_f64_3, 0.064, f64);
453 
454     g_read_write_test!(gread_gwrite_f32_1, 0.25f32, f32);
455     g_read_write_test!(gread_gwrite_f32_2, 0.5f32, f32);
456     g_read_write_test!(gread_gwrite_f32_3, 0.0f32, f32);
457 
458     g_read_write_test!(gread_gwrite_i64_1, 0i64, i64);
459     g_read_write_test!(gread_gwrite_i64_2, -1213213211111i64, i64);
460     g_read_write_test!(gread_gwrite_i64_3, -3000i64, i64);
461 
462     g_read_write_test!(gread_gwrite_i32_1, 0i32, i32);
463     g_read_write_test!(gread_gwrite_i32_2, -1213213232, i32);
464     g_read_write_test!(gread_gwrite_i32_3, -3000i32, i32);
465 
466     // useful for ferreting out problems with impls
467     #[test]
gread_with_iter_bytes()468     fn gread_with_iter_bytes() {
469         use super::{Pread};
470         let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
471         let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
472         let bytes_to = &mut bytes_to[..];
473         let bytes_from = &bytes_from[..];
474         let mut offset = &mut 0;
475         for i in 0..bytes_from.len() {
476             bytes_to[i] = bytes_from.gread(&mut offset).unwrap();
477         }
478         assert_eq!(bytes_to, bytes_from);
479         assert_eq!(*offset, bytes_to.len());
480     }
481 
482     #[test]
gread_inout()483     fn gread_inout() {
484         use super::{Pread};
485         let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
486         let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
487         let bytes = &bytes_from[..];
488         let offset = &mut 0;
489         bytes.gread_inout(offset, &mut bytes_to[..]).unwrap();
490         assert_eq!(bytes_to, bytes_from);
491         assert_eq!(*offset, bytes_to.len());
492     }
493 
494     #[test]
gread_with_byte()495     fn gread_with_byte() {
496         use super::{Pread};
497         let bytes: [u8; 1] = [0x7f];
498         let b = &bytes[..];
499         let offset = &mut 0;
500         let byte: u8 = b.gread(offset).unwrap();
501         assert_eq!(0x7f, byte);
502         assert_eq!(*offset, 1);
503     }
504 
505     #[test]
gread_slice()506     fn gread_slice() {
507         use super::{Pread};
508         use super::ctx::{StrCtx};
509         let bytes: [u8; 2] = [0x7e, 0xef];
510         let b = &bytes[..];
511         let offset = &mut 0;
512         let res = b.gread_with::<&str>(offset, StrCtx::Length(3));
513         assert!(res.is_err());
514         *offset = 0;
515         let astring: [u8; 3] = [0x45, 042, 0x44];
516         let string = astring.gread_with::<&str>(offset, StrCtx::Length(2));
517         match &string {
518             &Ok(_) => {},
519             &Err(ref err) => {println!("{}", &err); panic!();}
520         }
521         assert_eq!(string.unwrap(), "E*");
522         *offset = 0;
523         let bytes2: &[u8]  = b.gread_with(offset, 2).unwrap();
524         assert_eq!(*offset, 2);
525         assert_eq!(bytes2.len(), bytes[..].len());
526         for i in 0..bytes2.len() {
527             assert_eq!(bytes2[i], bytes[i])
528         }
529     }
530 
531     /////////////////////////////////////////////////////////////////
532     // end gread_with
533     /////////////////////////////////////////////////////////////////
534 }
535