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}