1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
//! Type definitions for wrappers which parse interleaved data.
use crate::{BinRead, BinResult, VecArgs};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::fmt;
/// A parser for data which consists of values of type `T` interleaved with
/// other values of type `P`.
///
/// To use this parser, you must specify the parsing strategy by selecting
/// either [`separated()`] or [`separated_trailing()`] using [`parse_with`].
///
/// [`separated()`]: Self::separated
/// [`separated_trailing()`]: Self::separated_trailing
/// [`parse_with`]: crate::docs::attribute#custom-parserswriters
///
/// Consider using a `Vec<(T, P)>` or `(Vec<(T, P)>, Option<T>>)` instead if you
/// do not need the parsed data to be transformed into a structure of arrays.
///
/// # Examples
///
/// ```
/// # use binrw::{prelude::*, io::Cursor};
/// use binrw::punctuated::Punctuated;
///
/// #[derive(BinRead)]
/// struct MyList {
/// #[br(parse_with = Punctuated::separated)]
/// #[br(count = 3)]
/// x: Punctuated<u16, u8>,
/// }
///
/// # let mut x = Cursor::new(b"\0\x03\0\0\x02\x01\0\x01");
/// # let y: MyList = x.read_be().unwrap();
/// # assert_eq!(*y.x, vec![3, 2, 1]);
/// # assert_eq!(y.x.separators, vec![0, 1]);
/// ```
pub struct Punctuated<T: BinRead, P: BinRead> {
/// The data values.
data: Vec<T>,
/// The separator values.
pub separators: Vec<P>,
}
impl<T, P> Punctuated<T, P>
where
T: BinRead,
P: for<'a> BinRead<Args<'a> = ()>,
{
/// Parses values of type `T` separated by values of type `P` without a
/// trailing separator value.
///
/// Requires a count to be passed via `#[br(count)]`.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
///
/// # Example
///
/// ```
/// # use binrw::{prelude::*, io::Cursor};
/// use binrw::punctuated::Punctuated;
///
/// #[derive(BinRead)]
/// struct MyList {
/// #[br(parse_with = Punctuated::separated)]
/// #[br(count = 3)]
/// x: Punctuated<u16, u8>,
/// }
///
/// # let mut x = Cursor::new(b"\0\x03\0\0\x02\x01\0\x01");
/// # let y: MyList = x.read_be().unwrap();
/// # assert_eq!(*y.x, vec![3, 2, 1]);
/// # assert_eq!(y.x.separators, vec![0, 1]);
/// ```
#[crate::parser(reader, endian)]
pub fn separated<'a>(args: VecArgs<T::Args<'a>>, ...) -> BinResult<Self>
where
T::Args<'a>: Clone,
{
let mut data = Vec::with_capacity(args.count);
let mut separators = Vec::with_capacity(args.count.max(1) - 1);
for i in 0..args.count {
data.push(T::read_options(reader, endian, args.inner.clone())?);
if i + 1 != args.count {
separators.push(P::read_options(reader, endian, ())?);
}
}
Ok(Self { data, separators })
}
/// Parses values of type `T` interleaved with values of type `P`, including
/// a trailing `P`.
///
/// Requires a count to be passed via `#[br(count)]`.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[crate::parser(reader, endian)]
pub fn separated_trailing<'a>(args: VecArgs<T::Args<'a>>, ...) -> BinResult<Self>
where
T::Args<'a>: Clone,
{
let mut data = Vec::with_capacity(args.count);
let mut separators = Vec::with_capacity(args.count);
for _ in 0..args.count {
data.push(T::read_options(reader, endian, args.inner.clone())?);
separators.push(P::read_options(reader, endian, ())?);
}
Ok(Self { data, separators })
}
/// Consumes this object, returning the data values while dropping the
/// separator values.
///
/// If you never use the separator values, consider using the [`pad_after`]
/// directive to skip over data while parsing instead of reading it into
/// memory and then discarding it.
///
/// [`pad_after`]: crate::docs::attribute#padding-and-alignment
#[must_use]
pub fn into_values(self) -> Vec<T> {
self.data
}
}
impl<T: BinRead + fmt::Debug, P: BinRead> fmt::Debug for Punctuated<T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.data.fmt(f)
}
}
impl<T: BinRead, P: BinRead> core::ops::Deref for Punctuated<T, P> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: BinRead, P: BinRead> core::ops::DerefMut for Punctuated<T, P> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}