1#![deny(clippy::all, clippy::nursery)]
2#![warn(clippy::pedantic)]
3#![allow(clippy::cast_possible_truncation)]
4
5use std::io;
13use std::io::{Read, Write};
14
15pub trait ReadExt: Read {
16 fn read_cstring(&mut self) -> io::Result<String>;
21 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 fn write_cstring<S: AsRef<[u8]>>(&mut self, s: S) -> io::Result<()>;
61 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}