/*
* Copyright (c) 2024 Andrew Brower.
* This file is part of Crawlspace.
*
* Crawlspace is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* Crawlspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with Crawlspace. If not, see
* .
*/
use crate::{
ErrorKind::{self, InvalidData},
Read, Write,
};
use super::VarInt;
#[derive(Debug)]
pub struct Bounded(pub T);
impl Read for Bounded {
fn read(r: &mut impl std::io::Read) -> Result {
let len = VarInt::read(r)?.0;
if len < 0 {
return Err(InvalidData(
"tried to decode string with negative length".to_string(),
));
}
let len = len as usize;
let mut buf = vec![0; len];
r.read_exact(&mut buf)?;
let content = String::from_utf8(buf)
.map_err(|_| ErrorKind::InvalidData("invalid utf8 string data".to_string()))?;
let utf16_len = content.encode_utf16().count();
if utf16_len > BOUND {
return Err(InvalidData(format!(
"utf-16 encoded string exceeds {BOUND} chars (is {utf16_len})"
)));
}
Ok(Bounded(content))
}
}
impl<'a, const BOUND: usize> Write for Bounded {
fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> {
let len = self.0.encode_utf16().count();
if len > BOUND {
return Err(InvalidData(format!(
"length of string {len} exceeds bound {BOUND}"
)));
};
VarInt(self.0.len() as i32).write(w)?;
Ok(w.write_all(self.0.as_bytes())?)
}
}
impl Write for str {
fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> {
VarInt(self.len() as i32).write(w)?;
Ok(w.write_all(self.as_bytes())?)
}
}
#[derive(Debug)]
pub struct Rest(pub T);
impl<'a, const BOUND: usize> Read for Rest {
fn read(r: &mut impl std::io::Read) -> Result {
let mut buf = Vec::new();
r.read_to_end(&mut buf)?;
let content = String::from_utf8(buf)
.map_err(|_| InvalidData("Rest was not valid UTF-8".to_string()))?;
let utf16_len = content.encode_utf16().count();
if utf16_len > BOUND {
return Err(InvalidData(format!(
"utf-16 encoded string exceeds {BOUND} chars (is {utf16_len})"
)));
};
Ok(Rest(content))
}
}
impl<'a, const BOUND: usize> Write for Rest {
fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> {
let len = self.0.encode_utf16().count();
if len > BOUND {
return Err(InvalidData(format!(
"length of string {len} exceeds bound {BOUND}"
)));
};
Ok(w.write_all(self.0.as_bytes())?)
}
}