use tokio::io::AsyncWriteExt;
use crate::Segment;
pub struct VarWriter {
data: Vec<Segment>,
}
impl VarWriter {
pub fn new() -> VarWriter {
VarWriter {
data: Vec::new(),
}
}
pub fn add(&mut self, segment: Segment) {
self.data.push(segment);
}
pub fn add_string<S: Into<String>>(&mut self, string: S) {
self.add(Segment::from(string.into()))
}
pub fn add_raw(&mut self, raw: &[u8]) {
self.data.push(Segment::from(raw));
}
pub async fn send<W: AsyncWriteExt + Unpin>(&mut self, stream: &mut W) -> std::io::Result<()> {
self.send_without_clearing(stream).await?;
self.clear();
Ok(())
}
pub async fn send_without_clearing<W: AsyncWriteExt + Unpin>(&mut self, stream: &mut W) -> std::io::Result<()> {
let total_size: usize = self.data.iter().map(|segment| segment.len() + 4).sum();
self.write_varint(stream, total_size).await?;
for segment in &self.data {
self.write_u32(stream, segment.len() as u32).await?;
stream.write_all(segment.as_ref()).await?;
}
Ok(())
}
async fn write_varint<W: AsyncWriteExt + Unpin>(&self, writer: &mut W, mut value: usize) -> std::io::Result<()> {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
writer.write_all(&[byte]).await?;
if value == 0 {
break;
}
}
Ok(())
}
#[cfg(not(feature = "big-endian"))]
async fn write_u32<W: AsyncWriteExt + Unpin>(&self, writer: &mut W, value: u32) -> std::io::Result<()> {
writer.write_all(&[
(value & 0xFF) as u8,
((value >> 8) & 0xFF) as u8,
((value >> 16) & 0xFF) as u8,
((value >> 24) & 0xFF) as u8,
]).await?;
Ok(())
}
#[cfg(feature = "big-endian")]
async fn write_u32<W: AsyncWriteExt + Unpin>(&self, writer: &mut W, value: u32) -> std::io::Result<()> {
writer.write_all(&[
((value >> 24) & 0xFF) as u8,
((value >> 16) & 0xFF) as u8,
((value >> 8) & 0xFF) as u8,
(value & 0xFF) as u8,
]).await?;
Ok(())
}
pub fn clear(&mut self) {
self.data.clear();
}
}
impl Default for VarWriter {
fn default() -> Self {
Self::new()
}
}