diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2022-06-25 05:09:26 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-25 05:09:26 +0000 |
| commit | 7d3e57763e32ac9cf94180b1c714704cfbc3034d (patch) | |
| tree | 2dcfe72bf09a42f6614f9dc988dc0254162ea0bf /azalea-buf | |
| parent | 69c47eda4c496b13dadd80976bffd2fab7ea5894 (diff) | |
| parent | ca7067e173129f3044ebc8c77634f06da29a086e (diff) | |
| download | azalea-drasl-7d3e57763e32ac9cf94180b1c714704cfbc3034d.tar.xz | |
Merge pull request #10 from mat-1/azalea-entity
azalea-entity
Diffstat (limited to 'azalea-buf')
| -rw-r--r-- | azalea-buf/Cargo.toml | 12 | ||||
| -rw-r--r-- | azalea-buf/README.md | 3 | ||||
| -rw-r--r-- | azalea-buf/buf-macros/Cargo.toml | 13 | ||||
| -rw-r--r-- | azalea-buf/buf-macros/src/lib.rs | 177 | ||||
| -rw-r--r-- | azalea-buf/src/definitions.rs | 56 | ||||
| -rw-r--r-- | azalea-buf/src/lib.rs | 193 | ||||
| -rw-r--r-- | azalea-buf/src/read.rs | 380 | ||||
| -rw-r--r-- | azalea-buf/src/serializable_uuid.rs | 76 | ||||
| -rw-r--r-- | azalea-buf/src/write.rs | 286 |
9 files changed, 1196 insertions, 0 deletions
diff --git a/azalea-buf/Cargo.toml b/azalea-buf/Cargo.toml new file mode 100644 index 00000000..4321dace --- /dev/null +++ b/azalea-buf/Cargo.toml @@ -0,0 +1,12 @@ +[package] +edition = "2021" +name = "azalea-buf" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +buf-macros = {path = "./buf-macros"} +byteorder = "1.4.3" +tokio = {version = "^1.19.2", features = ["io-util", "net", "macros"]} +uuid = "1.1.2" diff --git a/azalea-buf/README.md b/azalea-buf/README.md new file mode 100644 index 00000000..c988bcdb --- /dev/null +++ b/azalea-buf/README.md @@ -0,0 +1,3 @@ +# Azalea Buf + +An implementation of Minecraft's FriendlyByteBuf. This is used frequently in the game for serialization and deserialization of data. diff --git a/azalea-buf/buf-macros/Cargo.toml b/azalea-buf/buf-macros/Cargo.toml new file mode 100644 index 00000000..fecf64ed --- /dev/null +++ b/azalea-buf/buf-macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +edition = "2021" +name = "buf-macros" +version = "0.1.0" + +[lib] +proc-macro = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = "^1.0.36" +quote = "^1.0.10" +syn = "^1.0.82" diff --git a/azalea-buf/buf-macros/src/lib.rs b/azalea-buf/buf-macros/src/lib.rs new file mode 100644 index 00000000..4cc397a2 --- /dev/null +++ b/azalea-buf/buf-macros/src/lib.rs @@ -0,0 +1,177 @@ +use proc_macro::TokenStream; +use quote::{quote, ToTokens}; +use syn::{self, parse_macro_input, Data, DeriveInput, FieldsNamed, Ident}; + +fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { + match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => { + let FieldsNamed { named, .. } = match fields { + syn::Fields::Named(f) => f, + _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), + }; + + let read_fields = named + .iter() + .map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(_) => { + if f.attrs.iter().any(|a| a.path.is_ident("var")) { + quote! { + let #field_name = azalea_buf::McBufVarReadable::var_read_into(buf)?; + } + } else { + quote! { + let #field_name = azalea_buf::McBufReadable::read_into(buf)?; + } + } + } + _ => panic!( + "Error reading field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::<Vec<_>>(); + let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>(); + + quote! { + impl azalea_buf::McBufReadable for #ident { + fn read_into(buf: &mut impl std::io::Read) -> Result<Self, String> { + #(#read_fields)* + Ok(#ident { + #(#read_field_names: #read_field_names),* + }) + } + } + } + } + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let mut match_contents = quote!(); + let mut variant_discrim: u32 = 0; + for variant in variants { + let variant_name = &variant.ident; + match &variant.discriminant.as_ref() { + Some(d) => { + variant_discrim = match &d.1 { + syn::Expr::Lit(e) => match &e.lit { + syn::Lit::Int(i) => i.base10_parse().unwrap(), + _ => panic!("Error parsing enum discriminant"), + }, + _ => panic!("Error parsing enum discriminant"), + } + } + None => { + variant_discrim += 1; + } + } + match_contents.extend(quote! { + #variant_discrim => Ok(Self::#variant_name), + }); + } + + quote! { + impl azalea_buf::McBufReadable for #ident { + fn read_into(buf: &mut impl std::io::Read) -> Result<Self, String> + { + let id = azalea_buf::McBufVarReadable::var_read_into(buf)?; + match id { + #match_contents + _ => Err(format!("Unknown enum variant {}", id)), + } + } + } + } + } + _ => panic!("#[derive(McBuf)] can only be used on structs"), + } +} + +fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { + match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => { + let FieldsNamed { named, .. } = match fields { + syn::Fields::Named(f) => f, + _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), + }; + + let write_fields = named + .iter() + .map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(_) => { + if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { + quote! { + azalea_buf::McBufVarWritable::var_write_into(&self.#field_name, buf)?; + } + } else { + quote! { + azalea_buf::McBufWritable::write_into(&self.#field_name, buf)?; + } + } + } + _ => panic!( + "Error writing field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::<Vec<_>>(); + + quote! { + impl azalea_buf::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + #(#write_fields)* + Ok(()) + } + } + } + } + syn::Data::Enum(syn::DataEnum { .. }) => { + quote! { + impl azalea_buf::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + azalea_buf::Writable::write_varint(buf, *self as i32) + } + } + } + } + _ => panic!("#[derive(McBuf)] can only be used on structs"), + } +} + +#[proc_macro_derive(McBufReadable, attributes(var))] +pub fn derive_mcbufreadable(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, .. } = parse_macro_input!(input); + + create_impl_mcbufreadable(&ident, &data).into() +} + +#[proc_macro_derive(McBufWritable, attributes(var))] +pub fn derive_mcbufwritable(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, .. } = parse_macro_input!(input); + + create_impl_mcbufwritable(&ident, &data).into() +} + +#[proc_macro_derive(McBuf, attributes(var))] +pub fn derive_mcbuf(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, .. } = parse_macro_input!(input); + + let writable = create_impl_mcbufwritable(&ident, &data); + let readable = create_impl_mcbufreadable(&ident, &data); + quote! { + #writable + #readable + } + .into() +} diff --git a/azalea-buf/src/definitions.rs b/azalea-buf/src/definitions.rs new file mode 100644 index 00000000..921fb63d --- /dev/null +++ b/azalea-buf/src/definitions.rs @@ -0,0 +1,56 @@ +use crate::{McBufReadable, McBufWritable}; +use std::{ + io::{Read, Write}, + ops::Deref, +}; + +/// A Vec<u8> that isn't prefixed by a VarInt with the size. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UnsizedByteArray(Vec<u8>); + +impl Deref for UnsizedByteArray { + type Target = Vec<u8>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<Vec<u8>> for UnsizedByteArray { + fn from(vec: Vec<u8>) -> Self { + Self(vec) + } +} + +impl From<&str> for UnsizedByteArray { + fn from(s: &str) -> Self { + Self(s.as_bytes().to_vec()) + } +} + +/// Represents Java's BitSet, a list of bits. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BitSet { + data: Vec<u64>, +} + +// the Index trait requires us to return a reference, but we can't do that +impl BitSet { + pub fn index(&self, index: usize) -> bool { + (self.data[index / 64] & (1u64 << (index % 64))) != 0 + } +} + +impl McBufReadable for BitSet { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + Ok(Self { + data: Vec::<u64>::read_into(buf)?, + }) + } +} + +impl McBufWritable for BitSet { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.data.write_into(buf) + } +} diff --git a/azalea-buf/src/lib.rs b/azalea-buf/src/lib.rs new file mode 100644 index 00000000..cd72f22e --- /dev/null +++ b/azalea-buf/src/lib.rs @@ -0,0 +1,193 @@ +//! Utilities for reading and writing for the Minecraft protocol + +#![feature(min_specialization)] +#![feature(arbitrary_enum_discriminant)] + +mod definitions; +mod read; +mod serializable_uuid; +mod write; + +pub use buf_macros::*; +pub use definitions::*; +pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable}; +pub use serializable_uuid::*; +pub use write::{McBufVarWritable, McBufWritable, Writable}; + +// const DEFAULT_NBT_QUOTA: u32 = 2097152; +const MAX_STRING_LENGTH: u16 = 32767; +// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + #[test] + fn test_write_varint() { + let mut buf = Vec::new(); + buf.write_varint(0).unwrap(); + assert_eq!(buf, vec![0]); + + let mut buf = Vec::new(); + buf.write_varint(1).unwrap(); + assert_eq!(buf, vec![1]); + + let mut buf = Vec::new(); + buf.write_varint(2).unwrap(); + assert_eq!(buf, vec![2]); + + let mut buf = Vec::new(); + buf.write_varint(127).unwrap(); + assert_eq!(buf, vec![127]); + + let mut buf = Vec::new(); + buf.write_varint(128).unwrap(); + assert_eq!(buf, vec![128, 1]); + + let mut buf = Vec::new(); + buf.write_varint(255).unwrap(); + assert_eq!(buf, vec![255, 1]); + + let mut buf = Vec::new(); + buf.write_varint(25565).unwrap(); + assert_eq!(buf, vec![221, 199, 1]); + + let mut buf = Vec::new(); + buf.write_varint(2097151).unwrap(); + assert_eq!(buf, vec![255, 255, 127]); + + let mut buf = Vec::new(); + buf.write_varint(2147483647).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 7]); + + let mut buf = Vec::new(); + buf.write_varint(-1).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 15]); + + let mut buf = Vec::new(); + buf.write_varint(-2147483648).unwrap(); + assert_eq!(buf, vec![128, 128, 128, 128, 8]); + } + + #[test] + fn test_read_varint() { + let mut buf = Cursor::new(vec![0]); + assert_eq!(buf.read_varint().unwrap(), 0); + assert_eq!(buf.get_varint_size(0), 1); + + let mut buf = Cursor::new(vec![1]); + assert_eq!(buf.read_varint().unwrap(), 1); + assert_eq!(buf.get_varint_size(1), 1); + + let mut buf = Cursor::new(vec![2]); + assert_eq!(buf.read_varint().unwrap(), 2); + assert_eq!(buf.get_varint_size(2), 1); + + let mut buf = Cursor::new(vec![127]); + assert_eq!(buf.read_varint().unwrap(), 127); + assert_eq!(buf.get_varint_size(127), 1); + + let mut buf = Cursor::new(vec![128, 1]); + assert_eq!(buf.read_varint().unwrap(), 128); + assert_eq!(buf.get_varint_size(128), 2); + + let mut buf = Cursor::new(vec![255, 1]); + assert_eq!(buf.read_varint().unwrap(), 255); + assert_eq!(buf.get_varint_size(255), 2); + + let mut buf = Cursor::new(vec![221, 199, 1]); + assert_eq!(buf.read_varint().unwrap(), 25565); + assert_eq!(buf.get_varint_size(25565), 3); + + let mut buf = Cursor::new(vec![255, 255, 127]); + assert_eq!(buf.read_varint().unwrap(), 2097151); + assert_eq!(buf.get_varint_size(2097151), 3); + + let mut buf = Cursor::new(vec![255, 255, 255, 255, 7]); + assert_eq!(buf.read_varint().unwrap(), 2147483647); + assert_eq!(buf.get_varint_size(2147483647), 5); + + let mut buf = Cursor::new(vec![255, 255, 255, 255, 15]); + assert_eq!(buf.read_varint().unwrap(), -1); + assert_eq!(buf.get_varint_size(-1), 5); + + let mut buf = Cursor::new(vec![128, 128, 128, 128, 8]); + assert_eq!(buf.read_varint().unwrap(), -2147483648); + assert_eq!(buf.get_varint_size(-2147483648), 5); + } + + #[test] + fn test_read_varint_longer() { + let mut buf = Cursor::new(vec![138, 56, 0, 135, 56, 123]); + assert_eq!(buf.read_varint().unwrap(), 7178); + } + + #[test] + fn test_list() { + let mut buf = Vec::new(); + buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s)) + .unwrap(); + + // there's no read_list because idk how to do it in rust + let mut buf = Cursor::new(buf); + + let mut result = Vec::new(); + let length = buf.read_varint().unwrap(); + for _ in 0..length { + result.push(buf.read_utf().unwrap()); + } + + assert_eq!(result, vec!["a", "bc", "def"]); + } + + #[test] + fn test_int_id_list() { + let mut buf = Vec::new(); + buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i)) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let result = buf.read_int_id_list().unwrap(); + assert_eq!(result, vec![1, 2, 3]); + } + + #[test] + fn test_map() { + let mut buf = Vec::new(); + buf.write_map( + vec![("a", 1), ("bc", 23), ("def", 456)], + Vec::write_utf, + Vec::write_varint, + ) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let mut result = Vec::new(); + let length = buf.read_varint().unwrap(); + for _ in 0..length { + result.push((buf.read_utf().unwrap(), buf.read_varint().unwrap())); + } + + assert_eq!( + result, + vec![ + ("a".to_string(), 1), + ("bc".to_string(), 23), + ("def".to_string(), 456) + ] + ); + } + + #[test] + fn test_long() { + let mut buf = Vec::new(); + buf.write_long(123456).unwrap(); + + let mut buf = Cursor::new(buf); + + assert_eq!(buf.read_long().unwrap(), 123456); + } +} diff --git a/azalea-buf/src/read.rs b/azalea-buf/src/read.rs new file mode 100644 index 00000000..569a5b1d --- /dev/null +++ b/azalea-buf/src/read.rs @@ -0,0 +1,380 @@ +use super::{UnsizedByteArray, MAX_STRING_LENGTH}; +use byteorder::{ReadBytesExt, BE}; +use std::{collections::HashMap, hash::Hash, io::Read}; +use tokio::io::{AsyncRead, AsyncReadExt}; + +// TODO: get rid of Readable and use McBufReadable everywhere + +pub trait Readable { + fn read_int_id_list(&mut self) -> Result<Vec<i32>, String>; + fn read_varint(&mut self) -> Result<i32, String>; + fn get_varint_size(&mut self, value: i32) -> u8; + fn get_varlong_size(&mut self, value: i32) -> u8; + fn read_byte_array(&mut self) -> Result<Vec<u8>, String>; + fn read_bytes_with_len(&mut self, n: usize) -> Result<Vec<u8>, String>; + fn read_bytes(&mut self) -> Result<Vec<u8>, String>; + fn read_utf(&mut self) -> Result<String, String>; + fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String>; + fn read_byte(&mut self) -> Result<u8, String>; + fn read_int(&mut self) -> Result<i32, String>; + fn read_boolean(&mut self) -> Result<bool, String>; + fn read_long(&mut self) -> Result<i64, String>; + fn read_short(&mut self) -> Result<i16, String>; + fn read_float(&mut self) -> Result<f32, String>; + fn read_double(&mut self) -> Result<f64, String>; +} + +impl<R> Readable for R +where + R: Read, +{ + fn read_int_id_list(&mut self) -> Result<Vec<i32>, String> { + let len = self.read_varint()?; + let mut list = Vec::with_capacity(len as usize); + for _ in 0..len { + list.push(self.read_varint()?); + } + Ok(list) + } + + // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 + /// Read a single varint from the reader and return the value, along with the number of bytes read + fn read_varint(&mut self) -> Result<i32, String> { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..5 { + self.read_exact(&mut buffer) + .map_err(|_| "Invalid VarInt".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); + if buffer[0] & 0b1000_0000 == 0 { + return Ok(ans); + } + } + Ok(ans) + } + + fn get_varint_size(&mut self, value: i32) -> u8 { + for i in 1..5 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 5 + } + + fn get_varlong_size(&mut self, value: i32) -> u8 { + for i in 1..10 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 10 + } + + fn read_byte_array(&mut self) -> Result<Vec<u8>, String> { + let length = self.read_varint()? as usize; + self.read_bytes_with_len(length) + } + + fn read_bytes_with_len(&mut self, n: usize) -> Result<Vec<u8>, String> { + let mut buffer = vec![0; n]; + self.read_exact(&mut buffer) + .map_err(|_| "Error reading bytes".to_string())?; + Ok(buffer) + } + + fn read_bytes(&mut self) -> Result<Vec<u8>, String> { + // read to end of the buffer + let mut bytes = vec![]; + self.read_to_end(&mut bytes) + .map_err(|_| "Error reading bytes".to_string())?; + Ok(bytes) + } + + fn read_utf(&mut self) -> Result<String, String> { + self.read_utf_with_len(MAX_STRING_LENGTH.into()) + } + + fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String> { + let length = self.read_varint()?; + // i don't know why it's multiplied by 4 but it's like that in mojang's code so + if length < 0 { + return Err( + "The received encoded string buffer length is less than zero! Weird string!" + .to_string(), + ); + } + if length as u32 > max_length * 4 { + return Err(format!( + "The received encoded string buffer length is longer than maximum allowed ({} > {})", + length, + max_length * 4 + )); + } + + // this is probably quite inefficient, idk how to do it better + let mut string = String::new(); + let mut buffer = vec![0; length as usize]; + self.read_exact(&mut buffer) + .map_err(|_| "Invalid UTF-8".to_string())?; + string.push_str(std::str::from_utf8(&buffer).unwrap()); + if string.len() > length as usize { + return Err(format!( + "The received string length is longer than maximum allowed ({} > {})", + length, max_length + )); + } + + Ok(string) + } + + /// Read a single byte from the reader + fn read_byte(&mut self) -> Result<u8, String> { + self.read_u8().map_err(|_| "Error reading byte".to_string()) + } + + fn read_int(&mut self) -> Result<i32, String> { + match self.read_i32::<BE>() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading int".to_string()), + } + } + + fn read_boolean(&mut self) -> Result<bool, String> { + match self.read_byte()? { + 0 => Ok(false), + 1 => Ok(true), + _ => Err("Error reading boolean".to_string()), + } + } + + fn read_long(&mut self) -> Result<i64, String> { + match self.read_i64::<BE>() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading long".to_string()), + } + } + + fn read_short(&mut self) -> Result<i16, String> { + match self.read_i16::<BE>() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading short".to_string()), + } + } + + fn read_float(&mut self) -> Result<f32, String> { + match self.read_f32::<BE>() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading float".to_string()), + } + } + + fn read_double(&mut self) -> Result<f64, String> { + match self.read_f64::<BE>() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading double".to_string()), + } + } +} + +// fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 +/// Read a single varint from the reader and return the value, along with the number of bytes read +pub async fn read_varint_async(reader: &mut (dyn AsyncRead + Unpin + Send)) -> Result<i32, String> { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..5 { + reader + .read_exact(&mut buffer) + .await + .map_err(|_| "Invalid VarInt".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); + if buffer[0] & 0b1000_0000 == 0 { + return Ok(ans); + } + } + Ok(ans) +} + +pub trait McBufReadable +where + Self: Sized, +{ + fn read_into(buf: &mut impl Read) -> Result<Self, String>; +} + +pub trait McBufVarReadable +where + Self: Sized, +{ + fn var_read_into(buf: &mut impl Read) -> Result<Self, String>; +} + +impl McBufReadable for i32 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + Readable::read_int(buf) + } +} + +impl McBufVarReadable for i32 { + fn var_read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_varint() + } +} + +impl McBufVarReadable for i64 { + // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L54 + fn var_read_into(buf: &mut impl Read) -> Result<Self, String> { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..8 { + buf.read_exact(&mut buffer) + .map_err(|_| "Invalid VarLong".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i64) << 7 * i; + if buffer[0] & 0b1000_0000 == 0 { + break; + } + } + Ok(ans) + } +} +impl McBufVarReadable for u64 { + fn var_read_into(buf: &mut impl Read) -> Result<Self, String> { + i64::var_read_into(buf).map(|i| i as u64) + } +} + +impl McBufReadable for UnsizedByteArray { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + Ok(buf.read_bytes()?.into()) + } +} + +impl<T: McBufReadable + Send> McBufReadable for Vec<T> { + default fn read_into(buf: &mut impl Read) -> Result<Self, String> { + let length = buf.read_varint()? as usize; + let mut contents = Vec::with_capacity(length); + for _ in 0..length { + contents.push(T::read_into(buf)?); + } + Ok(contents) + } +} + +impl<K: McBufReadable + Send + Eq + Hash, V: McBufReadable + Send> McBufReadable for HashMap<K, V> { + default fn read_into(buf: &mut impl Read) -> Result<Self, String> { + let length = buf.read_varint()? as usize; + let mut contents = HashMap::with_capacity(length); + for _ in 0..length { + contents.insert(K::read_into(buf)?, V::read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for Vec<u8> { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_byte_array() + } +} + +impl McBufReadable for String { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_utf() + } +} + +impl McBufReadable for u32 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + Readable::read_int(buf).map(|i| i as u32) + } +} + +impl McBufVarReadable for u32 { + fn var_read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_varint().map(|i| i as u32) + } +} + +impl McBufReadable for u16 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_short().map(|i| i as u16) + } +} + +impl McBufReadable for i16 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_short() + } +} + +impl McBufVarReadable for u16 { + fn var_read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_varint().map(|i| i as u16) + } +} + +impl<T: McBufVarReadable> McBufVarReadable for Vec<T> { + fn var_read_into(buf: &mut impl Read) -> Result<Self, String> { + let length = buf.read_varint()? as usize; + let mut contents = Vec::with_capacity(length); + for _ in 0..length { + contents.push(T::var_read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for i64 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_long() + } +} + +impl McBufReadable for u64 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + i64::read_into(buf).map(|i| i as u64) + } +} + +impl McBufReadable for bool { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_boolean() + } +} + +impl McBufReadable for u8 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_byte() + } +} + +impl McBufReadable for i8 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_byte().map(|i| i as i8) + } +} + +impl McBufReadable for f32 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_float() + } +} + +impl McBufReadable for f64 { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + buf.read_double() + } +} + +impl<T: McBufReadable> McBufReadable for Option<T> { + default fn read_into(buf: &mut impl Read) -> Result<Self, String> { + let present = buf.read_boolean()?; + Ok(if present { + Some(T::read_into(buf)?) + } else { + None + }) + } +} diff --git a/azalea-buf/src/serializable_uuid.rs b/azalea-buf/src/serializable_uuid.rs new file mode 100644 index 00000000..10921aa6 --- /dev/null +++ b/azalea-buf/src/serializable_uuid.rs @@ -0,0 +1,76 @@ +use crate::{McBufReadable, McBufWritable, Readable}; +use std::io::{Read, Write}; +use uuid::Uuid; + +pub trait SerializableUuid { + fn to_int_array(&self) -> [u32; 4]; + fn from_int_array(array: [u32; 4]) -> Self; +} + +fn least_most_to_int_array(most: u64, least: u64) -> [u32; 4] { + [ + (most >> 32) as u32, + most as u32, + (least >> 32) as u32, + least as u32, + ] +} + +impl SerializableUuid for Uuid { + fn to_int_array(&self) -> [u32; 4] { + let most_significant_bits = (self.as_u128() >> 64) as u64; + let least_significant_bits = (self.as_u128() & 0xffffffffffffffff) as u64; + + least_most_to_int_array(most_significant_bits, least_significant_bits) + } + + fn from_int_array(array: [u32; 4]) -> Self { + let most = ((array[0] as u64) << 32) | ((array[1] as u64) & 0xFFFFFFFF); + let least = ((array[2] as u64) << 32) | ((array[3] as u64) & 0xFFFFFFFF); + + Uuid::from_u128(((most as u128) << 64) | least as u128) + } +} + +impl McBufReadable for Uuid { + fn read_into(buf: &mut impl Read) -> Result<Self, String> { + Ok(Uuid::from_int_array([ + Readable::read_int(buf)? as u32, + Readable::read_int(buf)? as u32, + Readable::read_int(buf)? as u32, + Readable::read_int(buf)? as u32, + ])) + } +} + +impl McBufWritable for Uuid { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let [a, b, c, d] = self.to_int_array(); + a.write_into(buf)?; + b.write_into(buf)?; + c.write_into(buf)?; + d.write_into(buf)?; + Ok(()) + } +} + +// TODO: add a test for Uuid in McBuf +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn to_int_array() { + let u = Uuid::parse_str("6536bfed-8695-48fd-83a1-ecd24cf2a0fd").unwrap(); + assert_eq!( + u.to_int_array(), + [0x6536bfed, 0x869548fd, 0x83a1ecd2, 0x4cf2a0fd] + ); + } + + #[test] + fn from_int_array() { + let u = Uuid::from_int_array([0x6536bfed, 0x869548fd, 0x83a1ecd2, 0x4cf2a0fd]); + assert_eq!(u.to_string(), "6536bfed-8695-48fd-83a1-ecd24cf2a0fd"); + } +} 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(()) + } +} |
