binary_reader/
lib.rs

1//! A Binary reader for step by step.
2//! It's a minimal [`byteorder`] wrapper for read bytes.
3//!
4//! # Example
5//!
6//! ```
7//! extern crate binary_reader;
8//!
9//! use binary_reader::{Endian, BinaryReader};
10//!
11//! fn main() {
12//!     let vector: Vec<u8> = vec![0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x0B, 0x77];
13//!     let mut binary = BinaryReader::from_vec(&vector);
14//!     binary.set_endian(Endian::Big);
15//!
16//!     assert_eq!("Hello, World!", binary.read_cstr().unwrap());
17//!     assert_eq!(2_935, binary.read_i16().unwrap());
18//! }
19//! ```
20
21extern crate byteorder;
22
23use byteorder::{BigEndian, LittleEndian, NativeEndian, ReadBytesExt};
24use std::io::{prelude::*, Error, ErrorKind};
25
26/// An Enums for set Endian.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Endian {
29    /// See [`byteorder::BigEndian`].
30    Big,
31    /// See [`byteorder::LittleEndian`].
32    Little,
33    /// See [`byteorder::NativeEndian`].
34    /// This endian varies with the platform.
35    Native,
36    /// See [`byteorder::NetworkEndian`].
37    /// This endian is alias of [`BigEndian`](byteorder::BigEndian).
38    Network,
39}
40
41#[doc(hidden)]
42/// This macro does a generate read functions.
43/// As there's too much duplicate code, I put it in a macro.
44macro_rules! read_number {
45    ($name:ident, $ty: ty, $bytes: expr, $doc: expr) => {
46        #[doc = $doc]
47        pub fn $name(&mut self) -> std::io::Result<$ty> {
48            let endianness = self.endian;
49            let mut data = self.read_bytes($bytes)?;
50            match endianness {
51                Endian::Big | Endian::Network => data.$name::<BigEndian>(),
52                Endian::Little => data.$name::<LittleEndian>(),
53                Endian::Native => data.$name::<NativeEndian>(),
54            }
55        }
56    };
57
58    ($name:ident, $ty: ty, $doc: expr) => {
59        #[doc = $doc]
60        pub fn $name(&mut self) -> std::io::Result<$ty> {
61            let mut data = self.read_bytes(1)?;
62            data.$name()
63        }
64    };
65}
66/// Binary reader.
67#[derive(Debug, Clone)]
68pub struct BinaryReader {
69    /// The buffer data.
70    pub data: Vec<u8>,
71    /// The current position.
72    pub pos: usize,
73    /// The length of the buffer.
74    pub length: usize,
75    /// The endian of the buffer.
76    pub endian: Endian,
77}
78
79impl BinaryReader {
80    #[doc(hidden)]
81    fn initialize() -> BinaryReader {
82        BinaryReader {
83            data: Vec::new(),
84            pos: 0,
85            length: 0,
86            endian: Endian::Big,
87        }
88    }
89
90    /// Initialize BinaryReader from [`u8`] slice.
91    pub fn from_u8(get: &[u8]) -> BinaryReader {
92        let mut a = BinaryReader::initialize();
93        a.data = get.to_vec();
94        a.length = get.len();
95        a
96    }
97
98    #[allow(clippy::ptr_arg)] // leave this for bypass clippy warning.
99    /// Initialize BinaryReader from [`u8`] [`Vector`](std::vec::Vec).
100    pub fn from_vec(vec: &Vec<u8>) -> BinaryReader {
101        let mut a = BinaryReader::initialize();
102        a.data = vec.to_vec();
103        a.length = vec.len();
104        a
105    }
106
107    /// Initialize BinaryReader from [`std::fs::File`].
108    pub fn from_file(file: &mut std::fs::File) -> BinaryReader {
109        let mut a = BinaryReader::initialize();
110        let mut v: Vec<u8> = Vec::new();
111        a.length = file.read_to_end(&mut v).unwrap();
112        a.data = v;
113        a
114    }
115
116    /// Set endian for read method.
117    pub fn set_endian(&mut self, endian: Endian) {
118        self.endian = endian
119    }
120
121    /// Jump position.
122    pub fn jmp(&mut self, pos: usize) {
123        self.pos = pos
124    }
125
126    pub fn adv(&mut self, size: usize) {
127        self.pos += size
128    }
129
130    pub fn align(&mut self, size: usize) {
131        self.pos = (self.pos + size - 1) / size * size
132    }
133
134    /// Read provided length size bytes.
135    pub fn read(&mut self, size: usize) -> Option<&[u8]> {
136        let data = self.data.get(self.pos..self.pos + size);
137        self.pos += size;
138        data
139    }
140
141    /// Read provided length size bytes.
142    /// Similar to [`BinaryReader::read`] but this returns [`std::io::Result`] instead of [`Option`].
143    pub fn read_bytes(&mut self, bytes: usize) -> std::io::Result<&[u8]> {
144        let data = self.data.get(self.pos..self.pos + bytes).ok_or_else(|| {
145            Error::new(
146                ErrorKind::UnexpectedEof,
147                format!("failed to read {} bytes from offset {}", bytes, self.pos),
148            )
149        })?;
150        self.pos += bytes;
151
152        Ok(data)
153    }
154
155    #[doc(hidden)]
156    /// Read to end of data from current offset and Return reversed data.
157    /// This is used for cstr read functions.
158    fn read_cstr_post(&self) -> std::io::Result<Vec<u8>> {
159        // "abc" "null" "def"
160        let mut data = self
161            .data
162            .clone()
163            .get(self.pos..self.length)
164            .ok_or_else(|| {
165                Error::new(
166                    ErrorKind::UnexpectedEof,
167                    format!(
168                        "failed to read {} bytes from offset {}",
169                        self.length - self.pos,
170                        self.pos
171                    ),
172                )
173            })?
174            .to_vec();
175        data.reverse();
176        Ok(data)
177    }
178
179    /// Read cstr strings until `null`(aka `0x00`) using [`std::string::String::from_utf8`].
180    pub fn read_cstr(&mut self) -> std::io::Result<String> {
181        let mut data = self.read_cstr_post()?;
182        let mut vec: Vec<u8> = Vec::new();
183        loop {
184            let a = data.pop().unwrap();
185            if a == 0x00 {
186                self.pos += vec.len() + 1;
187                return String::from_utf8(vec).map_err(|err| {
188                    Error::new(
189                        ErrorKind::InvalidData,
190                        format!("failed to convert to string: {:?}", err),
191                    )
192                });
193            } else {
194                vec.push(a);
195            }
196        }
197    }
198
199    /// Read cstr strings until `null`(aka `0x00`) using [`std::string::String::from_utf8_lossy`].
200    pub fn read_cstr_lossy(&mut self) -> std::io::Result<String> {
201        let mut data = self.read_cstr_post()?;
202        let mut vec: Vec<u8> = Vec::new();
203        loop {
204            let a = data.pop().unwrap();
205            if a == 0x00 {
206                self.pos += vec.len() + 1;
207                return Ok(String::from_utf8_lossy(&vec).to_string());
208            } else {
209                vec.push(a);
210            }
211        }
212    }
213
214    /// Read boolean.
215    /// Note that anything other than `0x00` is considered `true`.
216    pub fn read_bool(&mut self) -> std::io::Result<bool> {
217        let data = self.read_bytes(1)?;
218        Ok(data[0] != 0)
219    }
220
221    // Signed integers
222    read_number!(read_i8, i8, "Read signed 8 bit integer.");
223    read_number!(read_i16, i16, 2, "Read signed 16 bit integer.");
224    read_number!(
225        read_i24,
226        i32,
227        3,
228        "Read signed 24 bit integer. Stored in i32."
229    );
230    read_number!(read_i32, i32, 4, "Read signed 32 bit integer.");
231    read_number!(
232        read_i48,
233        i64,
234        6,
235        "Read signed 48 bit integer. Stored in i64."
236    );
237    read_number!(read_i64, i64, 8, "Read signed 64 bit integer.");
238    read_number!(read_i128, i128, 16, "Read signed 128 bit integer.");
239
240    // Unsigned integers
241    read_number!(read_u8, u8, "Read unsigned 8 bit integer.");
242    read_number!(read_u16, u16, 2, "Read unsigned 16 bit integer.");
243    read_number!(
244        read_u24,
245        u32,
246        3,
247        "Read unsigned 24 bit integer. Stored in u32."
248    );
249    read_number!(read_u32, u32, 4, "Read unsigned 32 bit integer.");
250    read_number!(
251        read_u48,
252        u64,
253        6,
254        "Read unsigned 48 bit integer. Stored in u64."
255    );
256    read_number!(read_u64, u64, 8, "Read unsigned 64 bit integer.");
257    read_number!(read_u128, u128, 16, "Read unsigned 128 bit integer.");
258
259    // Floating point
260    read_number!(read_f32, f32, 4, "Read 32 bit floating point number.");
261    read_number!(read_f64, f64, 8, "Read 64 bit floating point number.");
262}
263
264#[cfg(test)]
265mod tests;