use std::io::{Read, Write};
use byteorder::{ReadBytesExt, WriteBytesExt};
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Error)]
pub enum Error {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("string conversion error: {0}")]
Utf8(#[from] std::string::FromUtf8Error),
#[error("uleb error")]
UlebOverflow,
#[error("invalid string status char: {0}")]
InvalidStringStatusChar(u8),
}
pub trait ReadBytesOsu: Read {
fn read_uleb128(&mut self) -> Result<u128> {
let mut buf = [0];
let mut byte_index = 0;
self.read_exact(&mut buf)?;
let mut total = (buf[0] & 0b01111111) as u128;
while (buf[0] & 0b10000000) == 0b10000000 {
byte_index += 1;
if byte_index > 9 {
return Err(Error::UlebOverflow);
}
self.read_exact(&mut buf)?;
total += ((buf[0] & 0b01111111) as u128) << (7 * byte_index)
}
Ok(total)
}
fn read_uleb128_string(&mut self) -> Result<String> {
match self.read_u8()? {
0x0 => Ok(String::new()),
0xb => {
let len = self.read_uleb128()?;
if len == 0 {
return Ok(String::new());
}
let mut buf = vec![0; len as usize];
self.read_exact(&mut buf)?;
let string = String::from_utf8(buf)?;
Ok(string)
}
v => Err(Error::InvalidStringStatusChar(v)),
}
}
}
impl<R: Read + ?Sized> ReadBytesOsu for R {}
pub trait WriteBytesOsu: Write {
fn write_uleb128(&mut self, mut n: u128) -> Result<()> {
while n > 0 {
let mut byte = (n & 0x7fu128) as u8;
n >>= 7;
if n > 0 {
byte |= 1 << 7;
}
self.write_u8(byte)?;
}
Ok(())
}
fn write_uleb128_string(&mut self, string: impl AsRef<str>) -> Result<()> {
let string = string.as_ref();
if string.is_empty() {
self.write_u8(0x0)?;
return Ok(());
}
self.write_u8(0xb)?;
self.write_uleb128(string.len() as u128)?;
self.write_all(string.as_bytes())?;
Ok(())
}
}
impl<W: Write> WriteBytesOsu for W {}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
use std::io::Cursor;
#[test]
fn test_read_uleb128() -> Result<()> {
let mut num = Cursor::new([0xE5, 0x8E, 0x26]);
assert_eq!(num.read_uleb128()?, 624485);
Ok(())
}
#[test]
fn test_write_uleb128() -> Result<()> {
let mut buf = Vec::new();
let mut curs = Cursor::new(&mut buf);
curs.write_uleb128(624485)?;
assert_eq!(buf, [0xe5, 0x8e, 0x26]);
Ok(())
}
#[test]
fn test_read_uleb128_string() -> Result<()> {
let text = "Hello World";
let mut replay_string = vec![0x0Bu8];
replay_string.push(text.len() as u8);
replay_string.extend(text.bytes());
let mut reader = Cursor::new(replay_string);
assert_eq!(reader.read_uleb128_string()?, text.to_string());
let mut reader_empty = Cursor::new(vec![0x00]);
assert_eq!(reader_empty.read_uleb128_string()?, String::new());
let mut reader_0_len = Cursor::new(vec![0x0B, 0x00]);
assert_eq!(reader_0_len.read_uleb128_string()?, String::new());
Ok(())
}
#[test]
fn test_write_uleb128_string() -> Result<()> {
let mut buf = Vec::new();
let mut curs = Cursor::new(&mut buf);
curs.write_uleb128_string("Hello, world!")?;
assert_eq!(buf, b"\x0b\x0dHello, world!");
Ok(())
}
}