aboutsummaryrefslogtreecommitdiff
path: root/azalea-buf/src/write.rs
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-buf/src/write.rs')
-rw-r--r--azalea-buf/src/write.rs286
1 files changed, 286 insertions, 0 deletions
diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs
new file mode 100644
index 00000000..38ddcf49
--- /dev/null
+++ b/azalea-buf/src/write.rs
@@ -0,0 +1,286 @@
+use super::{UnsizedByteArray, MAX_STRING_LENGTH};
+use byteorder::{BigEndian, WriteBytesExt};
+use std::{collections::HashMap, io::Write};
+
+// TODO: get rid of Writable and use McBufWritable everywhere
+
+pub trait Writable: Write {
+ fn write_list<F, T>(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error>
+ where
+ F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy,
+ {
+ self.write_varint(list.len() as i32)?;
+ for item in list {
+ writer(self, item)?;
+ }
+ Ok(())
+ }
+
+ fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> {
+ self.write_list(list, |buf, n| buf.write_varint(*n))
+ }
+
+ fn write_map<KF, VF, KT, VT>(
+ &mut self,
+ map: Vec<(KT, VT)>,
+ key_writer: KF,
+ value_writer: VF,
+ ) -> Result<(), std::io::Error>
+ where
+ KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy,
+ VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy,
+ {
+ self.write_varint(map.len() as i32)?;
+ for (key, value) in map {
+ key_writer(self, key)?;
+ value_writer(self, value)?;
+ }
+ Ok(())
+ }
+
+ fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_u8(self, n)
+ }
+
+ fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
+ self.write_all(bytes)?;
+ Ok(())
+ }
+
+ fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> {
+ let mut buffer = [0];
+ if value == 0 {
+ self.write_all(&buffer).unwrap();
+ }
+ while value != 0 {
+ buffer[0] = (value & 0b0111_1111) as u8;
+ value = (value >> 7) & (i32::max_value() >> 6);
+ if value != 0 {
+ buffer[0] |= 0b1000_0000;
+ }
+ self.write_all(&buffer)?;
+ }
+ Ok(())
+ }
+
+ fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> {
+ if string.len() > len {
+ panic!(
+ "String too big (was {} bytes encoded, max {})",
+ string.len(),
+ len
+ );
+ }
+ self.write_varint(string.len() as i32)?;
+ self.write_bytes(string.as_bytes())
+ }
+
+ fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> {
+ self.write_utf_with_len(string, MAX_STRING_LENGTH.into())
+ }
+
+ fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_i16::<BigEndian>(self, n)
+ }
+
+ fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
+ self.write_varint(bytes.len() as i32)?;
+ self.write_bytes(bytes)
+ }
+
+ fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_i32::<BigEndian>(self, n)
+ }
+
+ fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> {
+ self.write_byte(if b { 1 } else { 0 })
+ }
+
+ fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_i64::<BigEndian>(self, n)
+ }
+
+ fn write_float(&mut self, n: f32) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_f32::<BigEndian>(self, n)
+ }
+
+ fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_f64::<BigEndian>(self, n)
+ }
+}
+
+impl<W: Write + ?Sized> Writable for W {}
+
+pub trait McBufWritable {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>;
+}
+
+pub trait McBufVarWritable {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>;
+}
+
+impl McBufWritable for i32 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ Writable::write_int(buf, *self)
+ }
+}
+
+impl McBufVarWritable for i32 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_varint(*self)
+ }
+}
+
+impl McBufWritable for UnsizedByteArray {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_bytes(self)
+ }
+}
+
+impl<T: McBufWritable> McBufWritable for Vec<T> {
+ default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_list(self, |buf, i| T::write_into(i, buf))
+ }
+}
+
+impl<K: McBufWritable, V: McBufWritable> McBufWritable for HashMap<K, V> {
+ default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ u32::var_write_into(&(self.len() as u32), buf)?;
+ for (key, value) in self {
+ key.write_into(buf)?;
+ value.write_into(buf)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl McBufWritable for Vec<u8> {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_byte_array(self)
+ }
+}
+
+impl McBufWritable for String {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_utf(self)
+ }
+}
+
+impl McBufWritable for u32 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i16::write_into(&(*self as i16), buf)
+ }
+}
+
+impl McBufVarWritable for u32 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i32::var_write_into(&(*self as i32), buf)
+ }
+}
+
+impl McBufVarWritable for i64 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ let mut buffer = [0];
+ let mut value = *self;
+ while value != 0 {
+ buffer[0] = (value & 0b0111_1111) as u8;
+ value = (value >> 7) & (i64::max_value() >> 6);
+ if value != 0 {
+ buffer[0] |= 0b1000_0000;
+ }
+ // this only writes a single byte, so write_all isn't necessary
+ // the let _ = is so clippy doesn't complain
+ let _ = buf.write(&mut buffer)?;
+ }
+ Ok(())
+ }
+}
+
+impl McBufVarWritable for u64 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i64::var_write_into(&(*self as i64), buf)
+ }
+}
+
+impl McBufWritable for u16 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i16::write_into(&(*self as i16), buf)
+ }
+}
+
+impl McBufVarWritable for u16 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i32::var_write_into(&(*self as i32), buf)
+ }
+}
+
+impl<T: McBufVarWritable> McBufVarWritable for Vec<T> {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ u32::var_write_into(&(self.len() as u32), buf)?;
+ for i in self {
+ i.var_write_into(buf)?;
+ }
+ Ok(())
+ }
+}
+
+impl McBufWritable for u8 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_byte(*self)
+ }
+}
+
+impl McBufWritable for i16 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ Writable::write_short(buf, *self)
+ }
+}
+
+impl McBufWritable for i64 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ Writable::write_long(buf, *self)
+ }
+}
+
+impl McBufWritable for u64 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i64::write_into(&(*self as i64), buf)
+ }
+}
+
+impl McBufWritable for bool {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_boolean(*self)
+ }
+}
+
+impl McBufWritable for i8 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_byte(*self as u8)
+ }
+}
+
+impl McBufWritable for f32 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_float(*self)
+ }
+}
+
+impl McBufWritable for f64 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_double(*self)
+ }
+}
+
+impl<T: McBufWritable> McBufWritable for Option<T> {
+ default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ if let Some(s) = self {
+ buf.write_boolean(true)?;
+ s.write_into(buf)?;
+ } else {
+ buf.write_boolean(false)?;
+ };
+ Ok(())
+ }
+}