From 2d8174678b353645cec76e6f8b61ec520878d2c5 Mon Sep 17 00:00:00 2001 From: Vladislavs Golubs Date: Tue, 23 Feb 2021 17:23:06 +0300 Subject: [PATCH] Add bitfield decoder --- protocol-derive/Cargo.toml | 4 + protocol-derive/src/lib.rs | 237 ++++++++++++++++++-- protocol-derive/tests/bitfield_abilities.rs | 60 +++++ protocol-derive/tests/bitfield_half.rs | 107 +++++++++ protocol-derive/tests/bitfield_position.rs | 54 +++++ protocol/src/decoder.rs | 18 +- protocol/src/encoder.rs | 14 +- 7 files changed, 476 insertions(+), 18 deletions(-) create mode 100644 protocol-derive/tests/bitfield_abilities.rs create mode 100644 protocol-derive/tests/bitfield_half.rs create mode 100644 protocol-derive/tests/bitfield_position.rs diff --git a/protocol-derive/Cargo.toml b/protocol-derive/Cargo.toml index 8225931..3b0de17 100644 --- a/protocol-derive/Cargo.toml +++ b/protocol-derive/Cargo.toml @@ -16,3 +16,7 @@ proc-macro = true proc-macro2 = "1.0" syn = "1.0" quote = "1.0" + +[dev-dependencies] +minecraft-protocol = { version = "0.1.0", path = "../protocol" } +byteorder = "1" diff --git a/protocol-derive/src/lib.rs b/protocol-derive/src/lib.rs index a497019..0a549a3 100644 --- a/protocol-derive/src/lib.rs +++ b/protocol-derive/src/lib.rs @@ -5,7 +5,7 @@ use proc_macro2::TokenStream as TokenStream2; use proc_macro2::{Ident, Span}; use quote::{quote, TokenStreamExt}; use std::iter::FromIterator; -use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, Lit, Meta, NestedMeta}; +use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, Lit, Meta, NestedMeta, Type}; #[proc_macro_derive(Packet, attributes(packet))] pub fn derive_packet(input: proc_macro::TokenStream) -> TokenStream1 { @@ -19,6 +19,8 @@ pub fn derive_packet(input: proc_macro::TokenStream) -> TokenStream1 { let encoder = impl_encoder_trait(name, fields); let decoder = impl_decoder_trait(name, fields); + println!("{}", decoder); + TokenStream1::from(quote! { #encoder @@ -64,6 +66,10 @@ fn impl_encoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 { } fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 { + let mut bitfields = vec![]; + let total_fields = fields.iter().count(); + let mut current = 0; + let decode = quote_field(fields, |field| { let name = &field.ident; let ty = &field.ty; @@ -71,27 +77,57 @@ fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 { let unparsed_meta = get_packet_field_meta(field); let parsed_meta = parse_packet_field_meta(&unparsed_meta); - // This is special case because max length are used only for strings. - if let Some(max_length) = parsed_meta.max_length { - return quote! { - let #name = crate::DecoderReadExt::read_string(reader, #max_length)?; + let mut result = quote!(); + + // When we encounter a bitfield, we skip writing until we get + // to the last bit field or the last field. + match &parsed_meta.bitfield { + Some(bitfield) => { + bitfields.push((field.clone(), bitfield.clone())); + } + None => { + result.append_all(quote_decoder_bitfield(&bitfields)); + bitfields.clear(); + } + } + + current += 1; + + if !bitfields.is_empty() { + return if current == total_fields { + result.append_all(quote_decoder_bitfield(&bitfields)); + bitfields.clear(); + result + } else { + quote!() }; } - match parsed_meta.module { - Some(module) => { - let module_ident = Ident::new(&module, Span::call_site()); + // This is special case because max length are used only for strings. + if let Some(max_length) = parsed_meta.max_length { + result.append_all(quote! { + let #name = crate::DecoderReadExt::read_string(reader, #max_length)?; + }); - quote! { + return result; + } + + match &parsed_meta.module { + Some(module) => { + let module_ident = Ident::new(module, Span::call_site()); + + result.append_all(quote! { let #name = crate::#module_ident::decode(reader)?; - } + }) } None => { - quote! { + result.append_all(quote! { let #name = <#ty as crate::Decoder>::decode(reader)?; - } + }); } } + + return result; }); let create = quote_field(fields, |field| { @@ -122,11 +158,148 @@ fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 { struct PacketFieldMeta { module: Option, max_length: Option, + bitfield: Option, +} + +#[derive(Debug, Clone)] +struct Bitfield { + size: usize, +} + +macro_rules! decode_bitmask_value ( + ($value: expr, $name: ident) => ( + fn $name( + result: &mut TokenStream2, + name: &Option, + ty: &Type, + bitfield_size: usize, + current: usize, + signed: bool, + ) { + let mask = (2 << (bitfield_size - 1)) - $value; + let bool = match ty { + Type::Path(type_path) => type_path + .path + .get_ident() + .expect("Failed to get ident") + .to_string() == "bool", + _ => false, + }; + + if bool { + if current > 0 { + result.append_all(quote! { + let mut #name = ((encoded >> #current) & #mask) != 0; + }); + } else { + result.append_all(quote! { + let mut #name = (encoded & #mask) != 0; + }); + } + } else { + if current > 0 { + result.append_all(quote! { + let mut #name = ((encoded >> #current) & #mask) as #ty; + }); + } else { + result.append_all(quote! { + let mut #name = (encoded & #mask) as #ty; + }); + } + } + + if signed { + let subtract = mask + 1; + let overflow = subtract >> 1; + + result.append_all(quote! { + if #name > #overflow as #ty { + #name -= #subtract as #ty; + } + }); + } + } + ) +); + +decode_bitmask_value!(1i64, decode_i64_bitmask_value); +decode_bitmask_value!(1i32, decode_i32_bitmask_value); +decode_bitmask_value!(1i16, decode_i16_bitmask_value); +decode_bitmask_value!(1i8, decode_i8_bitmask_value); + +fn quote_decoder_bitfield(bitfields: &Vec<(Field, Bitfield)>) -> TokenStream2 { + if bitfields.is_empty() { + return quote!(); + } + + let total_size: usize = bitfields.iter().map(|(_, b)| b.size).sum(); + + let mut result = read_decoder_bitfield_quote(total_size); + let mut current = total_size; + + for (field, bitfield) in bitfields { + current -= bitfield.size; + + let name = &field.ident; + let ty = &field.ty; + + let signed = match ty { + Type::Path(type_path) => type_path + .path + .get_ident() + .expect("Failed to get ident") + .to_string() + .starts_with("i"), + _ => panic!("Unexpected bitfield type"), + }; + + match total_size { + _ if total_size == 64 => { + decode_i64_bitmask_value(&mut result, name, ty, bitfield.size, current, signed) + } + _ if total_size == 32 => { + decode_i32_bitmask_value(&mut result, name, ty, bitfield.size, current, signed) + } + _ if total_size == 16 => { + decode_i16_bitmask_value(&mut result, name, ty, bitfield.size, current, signed) + } + _ => decode_i8_bitmask_value(&mut result, name, ty, bitfield.size, current, signed), + } + } + + return result; +} + +fn read_decoder_bitfield_quote(total_size: usize) -> TokenStream2 { + match total_size { + _ if total_size == 64 => { + quote! { + let encoded = ::byteorder::ReadBytesExt::read_i64::<::byteorder::BigEndian>(reader)?; + } + } + _ if total_size == 32 => { + quote! { + let encoded = ::byteorder::ReadBytesExt::read_i32::<::byteorder::BigEndian>(reader)?; + } + } + _ if total_size == 16 => { + quote! { + let encoded = ::byteorder::ReadBytesExt::read_i16::<::byteorder::BigEndian>(reader)?; + } + } + _ if total_size == 8 => { + quote! { + let encoded = ::byteorder::ReadBytesExt::read_i8(reader)?; + } + } + _ => panic!("Bitfield size must be aligned to 8, 16, 32 or 64"), + } } fn parse_packet_field_meta(meta_list: &Vec) -> PacketFieldMeta { let mut module = None; let mut max_length = None; + let mut bitfield = None; for meta in meta_list { match meta { @@ -147,14 +320,46 @@ fn parse_packet_field_meta(meta_list: &Vec) -> PacketFieldMeta { }, path => panic!( "Received unrecognized attribute : \"{}\"", - path.get_ident().unwrap() + path.get_ident().expect("Failed to get ident") ), }, - _ => panic!("Expected only named meta values"), + NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("bitfield") => { + let mut size = None; + + for meta in &meta_list.nested { + match meta { + NestedMeta::Meta(Meta::NameValue(named_meta)) => match &named_meta.path { + path if path.is_ident("size") => match &named_meta.lit { + Lit::Int(lit_int) => { + size = Some( + lit_int + .base10_parse::() + .expect("Failed to parse size attribute"), + ) + } + _ => panic!("\"size\" attribute value must be integer"), + }, + path => panic!( + "Received unrecognized attribute : \"{}\"", + path.get_ident().expect("Failed to get ident") + ), + }, + _ => panic!("Unexpected field meta"), + } + } + + let size = size.unwrap_or_else(|| panic!("Size must be specified in bitfield")); + bitfield = Some(Bitfield { size }) + } + _ => panic!("Unexpected field meta"), } } - PacketFieldMeta { module, max_length } + PacketFieldMeta { + module, + max_length, + bitfield, + } } fn get_packet_field_meta(field: &Field) -> Vec { @@ -171,7 +376,7 @@ fn get_packet_field_meta(field: &Field) -> Vec { .collect() } -fn quote_field TokenStream2>(fields: &Fields, func: F) -> TokenStream2 { +fn quote_field TokenStream2>(fields: &Fields, mut func: F) -> TokenStream2 { let mut output = quote!(); match fields { diff --git a/protocol-derive/tests/bitfield_abilities.rs b/protocol-derive/tests/bitfield_abilities.rs new file mode 100644 index 0000000..4fd7cb6 --- /dev/null +++ b/protocol-derive/tests/bitfield_abilities.rs @@ -0,0 +1,60 @@ +#[macro_use] +extern crate minecraft_protocol_derive; + +use minecraft_protocol::decoder::Decoder; +use minecraft_protocol::encoder::Encoder; +use minecraft_protocol::error::{DecodeError, EncodeError}; + +#[derive(Packet)] +pub struct Abilities { + #[packet(bitfield(size = 4))] + pub _unused: u8, + #[packet(bitfield(size = 1))] + pub creative_mode: bool, + #[packet(bitfield(size = 1))] + pub allow_flying: bool, + #[packet(bitfield(size = 1))] + pub flying: bool, + #[packet(bitfield(size = 1))] + pub invulnerable: bool, +} + +#[cfg(test)] +mod tests { + use crate::Abilities; + use minecraft_protocol::decoder::Decoder; + use minecraft_protocol::encoder::Encoder; + use minecraft_protocol::error::{DecodeError, EncodeError}; + use std::io::Cursor; + + #[test] + fn test_encode_abilities_i8_bitfield() { + let abilities = Abilities { + _unused: 0, + creative_mode: true, + allow_flying: true, + flying: true, + invulnerable: true, + }; + let mut vec = Vec::new(); + + abilities + .encode(&mut vec) + .expect("Failed to encode abilities"); + assert_eq!(vec, [15]); + } + + #[test] + fn test_decode_abilities_i8_bitfield() { + let value = 15i8; + + let vec = value.to_be_bytes().to_vec(); + let mut cursor = Cursor::new(vec); + + let abilities = Abilities::decode(&mut cursor).expect("Failed to decode abilities"); + assert!(abilities.invulnerable); + assert!(abilities.flying); + assert!(abilities.allow_flying); + assert!(abilities.creative_mode); + } +} diff --git a/protocol-derive/tests/bitfield_half.rs b/protocol-derive/tests/bitfield_half.rs new file mode 100644 index 0000000..5d4017e --- /dev/null +++ b/protocol-derive/tests/bitfield_half.rs @@ -0,0 +1,107 @@ +#[macro_use] +extern crate minecraft_protocol_derive; + +use minecraft_protocol::decoder::Decoder; +use minecraft_protocol::encoder::Encoder; +use minecraft_protocol::error::{DecodeError, EncodeError}; + +#[derive(Packet)] +pub struct HalfLong { + #[packet(bitfield(size = 32))] + pub _unused: u32, + #[packet(bitfield(size = 32))] + pub value: u32, +} + +#[derive(Packet)] +pub struct HalfInt { + #[packet(bitfield(size = 16))] + pub _unused: u16, + #[packet(bitfield(size = 16))] + pub value: u16, +} + +#[derive(Packet)] +pub struct HalfShort { + #[packet(bitfield(size = 8))] + pub _unused: u8, + #[packet(bitfield(size = 8))] + pub value: u8, +} + +#[cfg(test)] +mod tests { + use crate::{HalfInt, HalfLong, HalfShort}; + use minecraft_protocol::decoder::Decoder; + use minecraft_protocol::encoder::Encoder; + use minecraft_protocol::error::{DecodeError, EncodeError}; + use std::io::Cursor; + + #[test] + fn test_encode_half_long_u64_bitfield() { + let half = HalfLong { + _unused: 0, + value: u32::MAX, + }; + let mut vec = Vec::new(); + + half.encode(&mut vec).expect("Failed to encode half"); + assert_eq!(vec, u32::MAX.to_be_bytes().to_vec()); + } + + #[test] + fn test_decode_half_long_u64_bitfield() { + let value = u32::MAX as u64; + + let vec = value.to_be_bytes().to_vec(); + let mut cursor = Cursor::new(vec); + + let half = HalfLong::decode(&mut cursor).expect("Failed to decode half"); + assert_eq!(half.value, u32::MAX); + } + + #[test] + fn test_encode_half_int_u32_bitfield() { + let half = HalfInt { + _unused: 0, + value: u16::MAX, + }; + let mut vec = Vec::new(); + + half.encode(&mut vec).expect("Failed to encode half"); + assert_eq!(vec, u16::MAX.to_be_bytes().to_vec()); + } + + #[test] + fn test_decode_half_int_u32_bitfield() { + let value = u16::MAX as u32; + + let vec = value.to_be_bytes().to_vec(); + let mut cursor = Cursor::new(vec); + + let half = HalfInt::decode(&mut cursor).expect("Failed to decode half"); + assert_eq!(half.value, u16::MAX); + } + + #[test] + fn test_encode_half_short_u8_bitfield() { + let half = HalfShort { + _unused: 0, + value: u8::MAX, + }; + let mut vec = Vec::new(); + + half.encode(&mut vec).expect("Failed to encode half"); + assert_eq!(vec, u8::MAX.to_be_bytes().to_vec()); + } + + #[test] + fn test_decode_half_short_u8_bitfield() { + let value = u8::MAX as u16; + let vec = value.to_be_bytes().to_vec(); + let mut cursor = Cursor::new(vec); + + let half = HalfShort::decode(&mut cursor).expect("Failed to decode half"); + assert_eq!(half.value, u8::MAX); + } +} diff --git a/protocol-derive/tests/bitfield_position.rs b/protocol-derive/tests/bitfield_position.rs new file mode 100644 index 0000000..71ca606 --- /dev/null +++ b/protocol-derive/tests/bitfield_position.rs @@ -0,0 +1,54 @@ +#[macro_use] +extern crate minecraft_protocol_derive; + +use minecraft_protocol::decoder::Decoder; +use minecraft_protocol::encoder::Encoder; +use minecraft_protocol::error::{DecodeError, EncodeError}; + +#[derive(Packet)] +pub struct Position { + #[packet(bitfield(size = 26))] + pub x: i32, + #[packet(bitfield(size = 26))] + pub z: i32, + #[packet(bitfield(size = 12))] + pub y: u16, +} + +#[cfg(test)] +mod tests { + use crate::Position; + use minecraft_protocol::decoder::Decoder; + use minecraft_protocol::encoder::Encoder; + use minecraft_protocol::error::{DecodeError, EncodeError}; + use std::io::Cursor; + + #[test] + fn test_encode_position_i64_bitfield() { + let position = Position { + x: 1000, + y: 64, + z: -1000, + }; + + let mut vec = Vec::new(); + + position + .encode(&mut vec) + .expect("Failed to encode position"); + + assert_eq!(vec, 275152780755008i64.to_be_bytes().to_vec()); + } + + #[test] + fn test_decode_position_i64_bitfield() { + let value = -137164079660992i64; + let vec = value.to_be_bytes().to_vec(); + let mut cursor = Cursor::new(vec); + let position = Position::decode(&mut cursor).expect("Failed to decode position"); + + assert_eq!(position.x, -500); + assert_eq!(position.y, 64); + assert_eq!(position.z, -1000); + } +} diff --git a/protocol/src/decoder.rs b/protocol/src/decoder.rs index 0af4536..6ca25ea 100644 --- a/protocol/src/decoder.rs +++ b/protocol/src/decoder.rs @@ -5,7 +5,7 @@ use num_traits::FromPrimitive; use std::io::Read; use uuid::Uuid; -trait Decoder { +pub trait Decoder { type Output; fn decode(reader: &mut R) -> Result; @@ -109,6 +109,14 @@ impl Decoder for u8 { } } +impl Decoder for i16 { + type Output = Self; + + fn decode(reader: &mut R) -> Result { + Ok(reader.read_i16::()?) + } +} + impl Decoder for i32 { type Output = Self; @@ -117,6 +125,14 @@ impl Decoder for i32 { } } +impl Decoder for u16 { + type Output = Self; + + fn decode(reader: &mut R) -> Result { + Ok(reader.read_u16::()?) + } +} + impl Decoder for u32 { type Output = Self; diff --git a/protocol/src/encoder.rs b/protocol/src/encoder.rs index d317d93..40fb0f4 100644 --- a/protocol/src/encoder.rs +++ b/protocol/src/encoder.rs @@ -5,7 +5,7 @@ use num_traits::ToPrimitive; use std::io::Write; use uuid::Uuid; -trait Encoder { +pub trait Encoder { fn encode(&self, writer: &mut W) -> Result<(), EncodeError>; } @@ -103,12 +103,24 @@ impl Encoder for u8 { } } +impl Encoder for i16 { + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + Ok(writer.write_i16::(*self)?) + } +} + impl Encoder for i32 { fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { Ok(writer.write_i32::(*self)?) } } +impl Encoder for u16 { + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + Ok(writer.write_u16::(*self)?) + } +} + impl Encoder for u32 { fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { Ok(writer.write_u32::(*self)?)