binrw/
helpers.rs

1//! Helper functions for reading and writing data.
2
3use crate::{
4    io::{self, Read, Seek},
5    BinRead, BinResult, Endian, Error,
6};
7#[cfg(not(feature = "std"))]
8use alloc::vec::Vec;
9use core::iter::from_fn;
10
11/// Creates a parser that reads items into a collection until a condition is
12/// met. The terminal item is added to the collection.
13///
14/// This helper can be used to read into any collection type that implements
15/// [`FromIterator`].
16///
17/// # Examples
18///
19/// ```
20/// # use binrw::{BinRead, helpers::until, io::Cursor, BinReaderExt};
21/// #[derive(BinRead)]
22/// struct NullTerminated {
23///     #[br(parse_with = until(|&byte| byte == 0))]
24///     data: Vec<u8>,
25/// }
26///
27/// # let mut x = Cursor::new(b"\x01\x02\x03\x04\0");
28/// # let x: NullTerminated = x.read_be().unwrap();
29/// # assert_eq!(x.data, &[1, 2, 3, 4, 0]);
30/// ```
31pub fn until<Reader, T, CondFn, Arg, Ret>(
32    cond: CondFn,
33) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
34where
35    T: for<'a> BinRead<Args<'a> = Arg>,
36    Reader: Read + Seek,
37    CondFn: Fn(&T) -> bool,
38    Arg: Clone,
39    Ret: FromIterator<T>,
40{
41    until_with(cond, T::read_options)
42}
43
44/// Creates a parser that uses a given function to read items into a collection
45/// until a condition is met. The terminal item is added to the collection.
46///
47/// The given `read` function should return one item each time it is called.
48///
49/// This helper can be used to read into any collection type that implements
50/// [`FromIterator`].
51///
52/// # Examples
53///
54/// Reading a two-dimensional `VecDeque` by combining [`until_with`] and
55/// [`count`]:
56///
57/// ```
58/// # use binrw::{BinRead, helpers::{until, until_with, count}, io::Cursor, BinReaderExt};
59/// # use std::collections::VecDeque;
60/// #[derive(BinRead)]
61/// struct NullTerminated {
62///     #[br(parse_with = until_with(|bytes| bytes == &[0, 0], count(2)))]
63///     data: VecDeque<VecDeque<u8>>,
64/// }
65///
66/// # let mut x = Cursor::new(b"\x01\x02\x03\x04\0\0");
67/// # let x: NullTerminated = x.read_be().unwrap();
68/// # assert_eq!(x.data, &[[1, 2], [3, 4], [0, 0]]);
69/// ```
70pub fn until_with<Reader, T, CondFn, Arg, ReadFn, Ret>(
71    cond: CondFn,
72    read: ReadFn,
73) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
74where
75    Reader: Read + Seek,
76    CondFn: Fn(&T) -> bool,
77    Arg: Clone,
78    ReadFn: Fn(&mut Reader, Endian, Arg) -> BinResult<T>,
79    Ret: FromIterator<T>,
80{
81    move |reader, endian, args| {
82        let mut last = false;
83        from_fn(|| {
84            if last {
85                None
86            } else {
87                match read(reader, endian, args.clone()) {
88                    Ok(value) => {
89                        if cond(&value) {
90                            last = true;
91                        }
92                        Some(Ok(value))
93                    }
94                    err => Some(err),
95                }
96            }
97        })
98        .fuse()
99        .collect()
100    }
101}
102
103/// Creates a parser that reads items into a collection until a condition is
104/// met. The terminal item is discarded.
105///
106/// This helper can be used to read into any collection type that implements
107/// [`FromIterator`].
108///
109/// # Examples
110///
111/// ```
112/// # use binrw::{BinRead, helpers::until_exclusive, io::Cursor, BinReaderExt};
113/// #[derive(BinRead)]
114/// struct NullTerminated {
115///     #[br(parse_with = until_exclusive(|&byte| byte == 0))]
116///     data: Vec<u8>,
117/// }
118///
119/// # let mut x = Cursor::new(b"\x01\x02\x03\x04\0");
120/// # let x: NullTerminated = x.read_be().unwrap();
121/// # assert_eq!(x.data, &[1, 2, 3, 4]);
122/// ```
123pub fn until_exclusive<Reader, T, CondFn, Arg, Ret>(
124    cond: CondFn,
125) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
126where
127    T: for<'a> BinRead<Args<'a> = Arg>,
128    Reader: Read + Seek,
129    CondFn: Fn(&T) -> bool,
130    Arg: Clone,
131    Ret: FromIterator<T>,
132{
133    until_exclusive_with(cond, T::read_options)
134}
135
136/// Creates a parser that uses a given function to read items into a collection
137/// until a condition is met. The terminal item is discarded.
138///
139/// The given `read` function should return one item each time it is called.
140///
141/// This helper can be used to read into any collection type that implements
142/// [`FromIterator`].
143///
144/// # Examples
145///
146/// Reading a two-dimensional `VecDeque` by combining [`until_exclusive_with`]
147/// and [`count`]:
148///
149/// ```
150/// # use binrw::{BinRead, helpers::{until_exclusive, until_exclusive_with, count}, io::Cursor, BinReaderExt};
151/// # use std::collections::VecDeque;
152/// #[derive(BinRead)]
153/// struct NullTerminated {
154///     #[br(parse_with = until_exclusive_with(|bytes| bytes == &[0, 0], count(2)))]
155///     data: VecDeque<VecDeque<u8>>,
156/// }
157///
158/// # let mut x = Cursor::new(b"\x01\x02\x03\x04\0\0");
159/// # let x: NullTerminated = x.read_be().unwrap();
160/// # assert_eq!(x.data, &[[1, 2], [3, 4]]);
161/// ```
162pub fn until_exclusive_with<Reader, T, CondFn, Arg, ReadFn, Ret>(
163    cond: CondFn,
164    read: ReadFn,
165) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
166where
167    Reader: Read + Seek,
168    CondFn: Fn(&T) -> bool,
169    Arg: Clone,
170    ReadFn: Fn(&mut Reader, Endian, Arg) -> BinResult<T>,
171    Ret: FromIterator<T>,
172{
173    move |reader, endian, args| {
174        from_fn(|| match read(reader, endian, args.clone()) {
175            Ok(value) => {
176                if cond(&value) {
177                    None
178                } else {
179                    Some(Ok(value))
180                }
181            }
182            err => Some(err),
183        })
184        .fuse()
185        .collect()
186    }
187}
188
189/// Creates a parser that reads items into a collection until the end of the
190/// input stream.
191///
192/// This helper can be used to read into any collection type that implements
193/// [`FromIterator`].
194///
195/// # Errors
196///
197/// If reading fails for a reason other than reaching the end of the input, an
198/// [`Error`] variant will be returned.
199///
200/// # Examples
201///
202/// ```
203/// # use binrw::{BinRead, helpers::until_eof, io::Cursor, BinReaderExt};
204/// #[derive(BinRead)]
205/// struct EntireFile {
206///     #[br(parse_with = until_eof)]
207///     data: Vec<u8>,
208/// }
209///
210/// # let mut x = Cursor::new(b"\x01\x02\x03\x04");
211/// # let x: EntireFile = x.read_be().unwrap();
212/// # assert_eq!(x.data, &[1, 2, 3, 4]);
213/// ```
214pub fn until_eof<Reader, T, Arg, Ret>(
215    reader: &mut Reader,
216    endian: Endian,
217    args: Arg,
218) -> BinResult<Ret>
219where
220    T: for<'a> BinRead<Args<'a> = Arg>,
221    Reader: Read + Seek,
222    Arg: Clone,
223    Ret: FromIterator<T>,
224{
225    until_eof_with(T::read_options)(reader, endian, args)
226}
227
228/// Creates a parser that uses a given function to read items into a collection
229/// until the end of the input stream.
230///
231/// The given `read` function should return one item each time it is called.
232///
233/// This helper can be used to read into any collection type that implements
234/// [`FromIterator`].
235///
236/// # Errors
237///
238/// If reading fails for a reason other than reaching the end of the input, an
239/// [`Error`] variant will be returned.
240///
241/// # Examples
242///
243/// Reading a two-dimensional `VecDeque` by combining [`until_eof_with`] and
244/// [`count`]:
245///
246/// ```
247/// # use binrw::{BinRead, helpers::{until_eof, until_eof_with, count}, io::Cursor, BinReaderExt};
248/// # use std::collections::VecDeque;
249/// #[derive(BinRead)]
250/// struct EntireFile {
251///     #[br(parse_with = until_eof_with(count(2)))]
252///     data: VecDeque<VecDeque<u8>>,
253/// }
254///
255/// # let mut x = Cursor::new(b"\x01\x02\x03\x04");
256/// # let x: EntireFile = x.read_be().unwrap();
257/// # assert_eq!(x.data, &[[1, 2], [3, 4]]);
258/// ```
259pub fn until_eof_with<Reader, T, Arg, ReadFn, Ret>(
260    read: ReadFn,
261) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
262where
263    Reader: Read + Seek,
264    Arg: Clone,
265    ReadFn: Fn(&mut Reader, Endian, Arg) -> BinResult<T>,
266    Ret: FromIterator<T>,
267{
268    move |reader, endian, args| {
269        from_fn(|| match read(reader, endian, args.clone()) {
270            ok @ Ok(_) => Some(ok),
271            Err(err) if err.is_eof() => None,
272            err => Some(err),
273        })
274        .fuse()
275        .collect()
276    }
277}
278
279/// Creates a parser that builds a collection using items from the given
280/// iterable object as arguments for the parser.
281///
282/// This helper can be used to read into any collection type that implements
283/// [`FromIterator`].
284///
285/// # Examples
286///
287/// Reading an object containing header data followed by body data:
288///
289/// ```
290/// # use binrw::{args, BinRead, BinReaderExt, helpers::args_iter, io::Cursor};
291/// #[derive(BinRead)]
292/// #[br(big)]
293/// struct Header {
294///     count: u16,
295///
296///     #[br(args { count: count.into() })]
297///     sizes: Vec<u16>,
298/// }
299///
300/// #[derive(BinRead)]
301/// #[br(big)]
302/// struct Object {
303///     header: Header,
304///     #[br(parse_with = args_iter(header.sizes.iter().map(|&size| -> <Vec<u8> as BinRead>::Args<'_> {
305///         args! { count: size.into() }
306///     })))]
307///     segments: Vec<Vec<u8>>,
308/// }
309///
310/// # let mut x = Cursor::new(b"\0\x02\0\x01\0\x02\x03\x04\x05");
311/// # let x = Object::read(&mut x).unwrap();
312/// # assert_eq!(x.segments, &[vec![3], vec![4, 5]]);
313/// ```
314pub fn args_iter<R, T, Arg, Ret, It>(it: It) -> impl FnOnce(&mut R, Endian, ()) -> BinResult<Ret>
315where
316    T: for<'a> BinRead<Args<'a> = Arg>,
317    R: Read + Seek,
318    Arg: Clone,
319    Ret: FromIterator<T>,
320    It: IntoIterator<Item = Arg>,
321{
322    args_iter_with(it, T::read_options)
323}
324
325/// Creates a parser that uses a given function to build a collection, using
326/// items from the given iterable object as arguments for the function.
327///
328/// The given `read` function should return one item each time it is called.
329///
330/// This helper can be used to read into any collection type that implements
331/// [`FromIterator`].
332///
333/// # Examples
334///
335/// Reading an object containing header data followed by body data:
336///
337/// ```
338/// # use binrw::{args, BinRead, BinReaderExt, helpers::args_iter_with, io::Cursor};
339/// #[derive(BinRead)]
340/// #[br(big)]
341/// struct Header {
342///     count: u16,
343///
344///     #[br(args { count: count.into() })]
345///     sizes: Vec<u16>,
346/// }
347///
348/// #[derive(BinRead)]
349/// #[br(big)]
350/// struct Object {
351///     header: Header,
352///     #[br(parse_with = args_iter_with(&header.sizes, |reader, options, &size| {
353///         Vec::<u8>::read_options(reader, options, args! { count: size.into() })
354///     }))]
355///     segments: Vec<Vec<u8>>,
356/// }
357///
358/// # let mut x = Cursor::new(b"\0\x02\0\x01\0\x02\x03\x04\x05");
359/// # let x = Object::read(&mut x).unwrap();
360/// # assert_eq!(x.segments, &[vec![3], vec![4, 5]]);
361/// ```
362pub fn args_iter_with<Reader, T, Arg, Ret, It, ReadFn>(
363    it: It,
364    read: ReadFn,
365) -> impl FnOnce(&mut Reader, Endian, ()) -> BinResult<Ret>
366where
367    Reader: Read + Seek,
368    Arg: Clone,
369    Ret: FromIterator<T>,
370    It: IntoIterator<Item = Arg>,
371    ReadFn: Fn(&mut Reader, Endian, Arg) -> BinResult<T>,
372{
373    move |reader, options, ()| {
374        it.into_iter()
375            .map(|arg| read(reader, options, arg))
376            .collect()
377    }
378}
379
380/// Creates a parser that reads N items into a collection.
381///
382/// This helper is similar to using `#[br(count = N)]` with [`Vec`], but is more
383/// generic so can be used to read into any collection type that implements
384/// [`FromIterator`].
385///
386/// # Examples
387///
388/// ```
389/// # use binrw::{BinRead, helpers::count, io::Cursor, BinReaderExt};
390/// # use std::collections::VecDeque;
391/// #[derive(BinRead)]
392/// struct CountBytes {
393///     len: u8,
394///
395///     #[br(parse_with = count(len as usize))]
396///     data: VecDeque<u8>,
397/// }
398///
399/// # let mut x = Cursor::new(b"\x03\x01\x02\x03");
400/// # let x: CountBytes = x.read_be().unwrap();
401/// # assert_eq!(x.data, &[1, 2, 3]);
402/// ```
403pub fn count<R, T, Arg, Ret>(n: usize) -> impl Fn(&mut R, Endian, Arg) -> BinResult<Ret>
404where
405    T: for<'a> BinRead<Args<'a> = Arg>,
406    R: Read + Seek,
407    Arg: Clone,
408    Ret: FromIterator<T> + 'static,
409{
410    count_with(n, T::read_options)
411}
412
413/// Creates a parser that uses a given function to read N items into a
414/// collection.
415///
416/// The given `read` function should return one item each time it is called.
417///
418/// This helper is similar to using `#[br(count = N)]` with [`Vec`], but is more
419/// generic so can be used to read into any collection type that implements
420/// [`FromIterator`].
421///
422/// # Examples
423///
424/// Reading a two-dimensional `VecDeque` by combining [`count_with`] and
425/// [`count`]:
426///
427/// ```
428/// # use binrw::{BinRead, helpers::count, helpers::count_with, io::Cursor, BinReaderExt};
429/// # use std::collections::VecDeque;
430/// #[derive(BinRead)]
431/// struct CountBytes {
432///     len: u8,
433///
434///     #[br(parse_with = count_with(len as usize, count(2)))]
435///     data: VecDeque<VecDeque<u8>>,
436/// }
437///
438/// # let mut x = Cursor::new(b"\x02\x01\x02\x03\x04");
439/// # let x: CountBytes = x.read_be().unwrap();
440/// # assert_eq!(x.data, &[[1, 2], [3, 4]]);
441/// ```
442pub fn count_with<R, T, Arg, ReadFn, Ret>(
443    n: usize,
444    read: ReadFn,
445) -> impl Fn(&mut R, Endian, Arg) -> BinResult<Ret>
446where
447    R: Read + Seek,
448    Arg: Clone,
449    ReadFn: Fn(&mut R, Endian, Arg) -> BinResult<T>,
450    Ret: FromIterator<T> + 'static,
451{
452    move |reader, endian, args| {
453        let mut container = core::iter::empty::<T>().collect::<Ret>();
454
455        vec_fast_int!(try (i8 i16 u16 i32 u32 i64 u64 i128 u128) using (container, reader, endian, n) else {
456            // This extra branch for `Vec<u8>` makes it faster than
457            // `vec_fast_int`, but *only* because `vec_fast_int` is not allowed
458            // to use unsafe code to eliminate the unnecessary zero-fill.
459            // Otherwise, performance would be identical and it could be
460            // deleted.
461            if let Some(bytes) = <dyn core::any::Any>::downcast_mut::<Vec<u8>>(&mut container) {
462                bytes.reserve_exact(n);
463                let byte_count = reader
464                    .take(n.try_into().map_err(not_enough_bytes)?)
465                    .read_to_end(bytes)?;
466
467                if byte_count == n {
468                    Ok(container)
469                } else {
470                    Err(not_enough_bytes(()))
471                }
472            } else {
473                core::iter::repeat_with(|| read(reader, endian, args.clone()))
474                .take(n)
475                .collect()
476            }
477        })
478    }
479}
480
481/// Reads a 24-bit unsigned integer.
482///
483/// # Errors
484///
485/// If reading fails, an [`Error`](crate::Error) variant will be returned.
486///
487/// # Examples
488///
489/// ```
490/// # use binrw::{prelude::*, io::Cursor};
491/// #[derive(BinRead)]
492/// # #[derive(Debug, PartialEq)]
493/// struct Test {
494///     flags: u8,
495///     #[br(parse_with = binrw::helpers::read_u24)]
496///     value: u32,
497/// }
498/// #
499/// # assert_eq!(
500/// #     Test::read_be(&mut Cursor::new(b"\x01\x02\x03\x04")).unwrap(),
501/// #     Test { flags: 1, value: 0x20304 }
502/// # );
503/// # assert_eq!(
504/// #     Test::read_le(&mut Cursor::new(b"\x01\x04\x03\x02")).unwrap(),
505/// #     Test { flags: 1, value: 0x20304 }
506/// # );
507/// ```
508#[binrw::parser(reader, endian)]
509pub fn read_u24() -> binrw::BinResult<u32> {
510    type ConvFn = fn([u8; 4]) -> u32;
511    let mut buf = [0u8; 4];
512    let (conv, out): (ConvFn, &mut [u8]) = match endian {
513        Endian::Little => (u32::from_le_bytes, &mut buf[..3]),
514        Endian::Big => (u32::from_be_bytes, &mut buf[1..]),
515    };
516    reader.read_exact(out)?;
517    Ok(conv(buf))
518}
519
520/// Writes a 24-bit unsigned integer.
521///
522/// # Errors
523///
524/// If writing fails, an [`Error`](crate::Error) variant will be returned.
525///
526/// # Examples
527///
528/// ```
529/// # use binrw::{prelude::*, io::Cursor};
530/// #[derive(BinWrite)]
531/// # #[derive(Debug, PartialEq)]
532/// struct Test {
533///     flags: u8,
534///     #[bw(write_with = binrw::helpers::write_u24)]
535///     value: u32,
536/// }
537/// #
538/// # let mut data = Cursor::new(vec![]);
539/// # Test { flags: 1, value: 0x20304 }.write_be(&mut data).unwrap();
540/// # assert_eq!(
541/// #     data.get_ref(),
542/// #     &[1, 2, 3, 4]
543/// # );
544/// # let mut data = Cursor::new(vec![]);
545/// # Test { flags: 1, value: 0x20304 }.write_le(&mut data).unwrap();
546/// # assert_eq!(
547/// #     data.get_ref(),
548/// #     &[1, 4, 3, 2]
549/// # );
550/// ```
551#[binrw::writer(writer, endian)]
552pub fn write_u24(value: &u32) -> binrw::BinResult<()> {
553    let (buf, range) = match endian {
554        Endian::Little => (value.to_le_bytes(), 0..3),
555        Endian::Big => (value.to_be_bytes(), 1..4),
556    };
557    writer.write_all(&buf[range]).map_err(Into::into)
558}
559
560fn not_enough_bytes<T>(_: T) -> Error {
561    Error::Io(io::Error::new(
562        io::ErrorKind::UnexpectedEof,
563        "not enough bytes in reader",
564    ))
565}
566
567macro_rules! vec_fast_int {
568    (try ($($Ty:ty)+) using ($list:expr, $reader:expr, $endian:expr, $count:expr) else { $($else:tt)* }) => {
569        $(if let Some(list) = <dyn core::any::Any>::downcast_mut::<Vec<$Ty>>(&mut $list) {
570            let mut start = 0;
571            let mut remaining = $count;
572            // Allocating and reading from the source in chunks is done to keep
573            // a bad `count` from causing huge memory allocations that are
574            // doomed to fail
575            while remaining != 0 {
576                // Using a similar strategy as std `default_read_to_end` to
577                // leverage the memory growth strategy of the underlying Vec
578                // implementation (in std this will be exponential) using a
579                // minimum byte allocation
580                const GROWTH: usize = 32 / core::mem::size_of::<$Ty>();
581                list.reserve(remaining.min(GROWTH.max(1)));
582
583                let items_to_read = remaining.min(list.capacity() - start);
584                let end = start + items_to_read;
585
586                // In benchmarks, this resize decreases performance by 27–40%
587                // relative to using `unsafe` to write directly to uninitialised
588                // memory, but nobody ever got fired for buying IBM
589                list.resize(end, 0);
590                $reader.read_exact(&mut bytemuck::cast_slice_mut::<_, u8>(&mut list[start..end]))?;
591
592                remaining -= items_to_read;
593                start += items_to_read;
594            }
595
596            if
597                core::mem::size_of::<$Ty>() != 1
598                && (
599                    (cfg!(target_endian = "big") && $endian == crate::Endian::Little)
600                    || (cfg!(target_endian = "little") && $endian == crate::Endian::Big)
601                )
602            {
603                for value in list.iter_mut() {
604                    *value = value.swap_bytes();
605                }
606            }
607            Ok($list)
608        } else)* {
609            $($else)*
610        }
611    }
612}
613
614use vec_fast_int;