hemtt_io/
lib.rs

1#![deny(clippy::all, clippy::nursery)]
2#![warn(clippy::pedantic)]
3#![allow(clippy::cast_possible_truncation)]
4
5// Parts of the following code is derivative work of the code from the armake2 project by KoffeinFlummi,
6// which is licensed GPLv2. This code therefore is also licensed under the terms
7// of the GNU Public License, verison 2.
8
9// The original code can be found here:
10// https://github.com/KoffeinFlummi/armake2/blob/4b736afc8c615cf49a0d1adce8f6b9a8ae31d90f/src/io.rs
11
12use std::io;
13use std::io::{Read, Write};
14
15pub trait ReadExt: Read {
16    /// Read a null-terminated string from the input.
17    ///
18    /// # Errors
19    /// If the input fails to read.
20    fn read_cstring(&mut self) -> io::Result<String>;
21    /// Reads a compressed `u32` from the input.
22    ///
23    /// # Errors
24    /// If the input fails to read.
25    fn read_compressed_int(&mut self) -> io::Result<u32>;
26}
27
28impl<T: Read> ReadExt for T {
29    fn read_cstring(&mut self) -> io::Result<String> {
30        let mut bytes: Vec<u8> = Vec::new();
31        for byte in self.bytes() {
32            let b = byte?;
33            if b == 0 {
34                break;
35            }
36            bytes.push(b);
37        }
38
39        Ok(String::from_utf8(bytes).unwrap())
40    }
41
42    fn read_compressed_int(&mut self) -> io::Result<u32> {
43        let mut result: u32 = 0;
44        for (i, byte) in self.bytes().enumerate() {
45            let b: u32 = byte?.into();
46            result |= (b & 0x7f) << (i * 7);
47            if b < 0x80 {
48                break;
49            }
50        }
51        Ok(result)
52    }
53}
54
55pub trait WriteExt: Write {
56    /// Writes a null-terminated string to the output.
57    ///
58    /// # Errors
59    /// If the output fails to write.
60    fn write_cstring<S: AsRef<[u8]>>(&mut self, s: S) -> io::Result<()>;
61    /// Writes a compressed `u32` to the output.
62    ///
63    /// # Errors
64    /// If the output fails to write.
65    fn write_compressed_int(&mut self, x: u32) -> io::Result<usize>;
66}
67
68impl<T: Write> WriteExt for T {
69    fn write_cstring<S: AsRef<[u8]>>(&mut self, s: S) -> io::Result<()> {
70        self.write_all(s.as_ref())?;
71        self.write_all(b"\0")?;
72        Ok(())
73    }
74
75    fn write_compressed_int(&mut self, x: u32) -> io::Result<usize> {
76        let mut temp = x;
77        let mut len = 0;
78
79        while temp > 0x7f {
80            self.write_all(&[(0x80 | temp & 0x7f) as u8])?;
81            len += 1;
82            temp &= !0x7f;
83            temp >>= 7;
84        }
85
86        self.write_all(&[temp as u8])?;
87        Ok(len + 1)
88    }
89}
90
91#[must_use]
92pub const fn compressed_int_len(x: u32) -> usize {
93    let mut temp = x;
94    let mut len = 0;
95
96    while temp > 0x7f {
97        len += 1;
98        temp &= !0x7f;
99        temp >>= 7;
100    }
101
102    len + 1
103}