infinite_rs/common/
extensions.rs

1//! Extensions to [`BufRead`] for reading fixed-length strings and enumerable types.
2//!
3//! This module provides two main extensions to the standard [`BufRead`]:
4//!
5//! * [`read_fixed_string`](`BufReaderExt::read_fixed_string`): Reads a fixed number of bytes and converts them to a UTF-8 string.
6//!   Special handling is included for sequences of `0xFF` bytes which are treated as empty strings.
7//!
8//! * [`read_enumerable`](`BufReaderExt::read_enumerable`): Generic method for reading a sequence of items that implement the
9//!   [`Enumerable`] trait. Reads the specified type `count` times and collects the results into a [`Vec`].
10//!
11//! These extensions are implemented as traits and require the reader to implement both
12//! [`Read`] and [`Seek`] traits.
13//!
14
15use std::io::{BufRead, BufReader, Read, Seek};
16
17use crate::Result;
18
19/// Trait for types that can be read sequentially from a buffered reader.
20///
21/// Types implementing this trait can be read using the [`read_enumerable`](`BufReaderExt::read_enumerable`) method
22/// from [`BufReaderExt`].
23pub trait Enumerable {
24    /// Reads data from the given reader and updates the implementing type.
25    ///
26    /// # Arguments
27    ///
28    /// * `reader` - A mutable reference to any type that implements `BufReaderExt`
29    ///
30    /// # Errors
31    /// - If the reader fails to read the exact number of bytes [`ReadError`](`crate::Error::ReadError`)
32    fn read<R: BufReaderExt>(&mut self, reader: &mut R) -> Result<()>;
33}
34
35/// Extension trait for [`BufRead`] to add custom reading methods.
36pub trait BufReaderExt: BufRead + Seek {
37    /// Reads a fixed-length UTF-8 encoded string from the reader.
38    ///
39    /// This function reads exactly `length` bytes and converts them to a String.
40    /// If the bytes read are all 0xFF, an empty string is returned.
41    ///
42    /// # Arguments
43    ///
44    /// * `length` - The exact number of bytes to read
45    ///
46    /// # Errors
47    /// - If the reader fails to read the exact number of bytes [`ReadError`](`crate::Error::ReadError`)
48    /// - If the bytes read are not valid UTF-8 [`Utf8ReadingError`](`crate::Error::Utf8ReadingError`)
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use std::io::Cursor;
54    /// use std::io::BufReader;
55    /// use infinite_rs::common::extensions::BufReaderExt;
56    ///
57    /// let data = b"I love cats!";
58    /// let mut reader = BufReader::new(Cursor::new(data));
59    /// let string = reader.read_fixed_string(data.len()).unwrap();
60    /// assert_eq!(string, "I love cats!");
61    /// ```
62    fn read_fixed_string(&mut self, length: usize) -> Result<String> {
63        let mut buffer = vec![0; length];
64        self.read_exact(&mut buffer)?;
65
66        if buffer == [255, 255, 255, 255] {
67            return Ok(String::new()); // Return empty string if all bytes are 0xFF
68        }
69
70        let string = String::from_utf8(buffer)?;
71
72        Ok(string)
73    }
74
75    /// Reads a null-terminated string from the reader.
76    ///
77    /// This function reads bytes in a reader until it hits `0x00` and converts them to a String.
78    /// The null terminator is removed from the final output.
79    ///
80    /// # Errors
81    /// - If the reader fails to read the exact number of bytes [`ReadError`](`crate::Error::ReadError`)
82    /// - If the bytes read are not valid UTF-8 [`Utf8ReadingError`](`crate::Error::Utf8ReadingError`)
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// use std::io::Cursor;
88    /// use std::io::BufReader;
89    /// use infinite_rs::common::extensions::BufReaderExt;
90    ///
91    /// let data = [0x49, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x20, 0x63, 0x61, 0x74, 0x73, 0x21, 0x00];
92    /// let mut reader = BufReader::new(Cursor::new(data));
93    /// let string = reader.read_null_terminated_string().unwrap();
94    /// assert_eq!(string, "I love cats!");
95    /// ```
96    fn read_null_terminated_string(&mut self) -> Result<String> {
97        let mut buffer = Vec::with_capacity(150); // Pre-allocate around 150 bytes (typical
98        // filename size)
99        self.read_until(0x00, &mut buffer)?;
100        buffer.pop(); // remove null terminator
101
102        let string = String::from_utf8(buffer)?;
103
104        Ok(string)
105    }
106
107    /// Reads multiple instances of an enumerable type into a vector.
108    ///
109    /// Creates a vector of type T by reading the type `count` times from the buffer.
110    /// Type T must implement both [`Default`] and [`Enumerable`]    traits.
111    ///
112    /// # Type Parameters
113    ///
114    /// * `T` - The type to read, must implement `Default + Enumerable`
115    ///
116    /// # Arguments
117    ///
118    /// * `count` - Number of instances to read
119    ///
120    /// # Errors
121    /// - If the reader fails to read the exact number of bytes [`ReadError`](`crate::Error::ReadError`)
122    ///
123    /// # Examples
124    ///
125    /// ```
126    /// use std::io::{Cursor, BufReader,};
127    /// use infinite_rs::common::extensions::{BufReaderExt, Enumerable};
128    /// use infinite_rs::common::errors::Error;
129    /// use byteorder::{ReadBytesExt, LE};
130    ///
131    /// #[derive(Default)]
132    /// struct TestType {
133    ///     value: u32,
134    /// }
135    ///
136    /// impl Enumerable for TestType {
137    ///     fn read<R: BufReaderExt>(&mut self, reader: &mut R) -> Result<(), Error> {
138    ///         self.value = reader.read_u32::<LE>()?;
139    ///         Ok(())
140    ///     }
141    /// }
142    ///
143    /// let data = b"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00";
144    /// let mut reader = BufReader::new(Cursor::new(data));
145    /// let enumerables = reader.read_enumerable::<TestType>(3).unwrap();
146    /// assert_eq!(enumerables.len(), 3);
147    /// assert_eq!(enumerables[0].value, 1);
148    /// assert_eq!(enumerables[1].value, 2);
149    /// assert_eq!(enumerables[2].value, 3);
150    /// ```
151    fn read_enumerable<T: Default + Enumerable>(&mut self, count: u64) -> Result<Vec<T>>
152    where
153        Self: Sized,
154        Vec<T>: FromIterator<T>,
155    {
156        let mut enumerables = vec![];
157        enumerables.reserve_exact(usize::try_from(count)? + 1);
158        for _ in 0..count {
159            let mut enumerable = T::default();
160            enumerable.read(self)?;
161            enumerables.push(enumerable);
162        }
163        Ok(enumerables)
164    }
165}
166
167impl<R: Read + Seek> BufReaderExt for BufReader<R> {}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172    use std::io::Cursor;
173
174    #[test]
175    /// Verifies that reading 0xFFFFFFFF returns an empty string, which is used
176    /// to handle empty `tag_group` entries in module files.
177    fn test_read_fixed_string_empty() {
178        let data = [255, 255, 255, 255];
179        let mut reader = BufReader::new(Cursor::new(&data));
180        let string = reader.read_fixed_string(data.len()).unwrap();
181        assert_eq!(string, "");
182    }
183}