Add bitfield decoder

This commit is contained in:
Vladislavs Golubs 2021-02-23 17:23:06 +03:00
parent 5a2b7e81fa
commit 2d8174678b
7 changed files with 476 additions and 18 deletions

View File

@ -16,3 +16,7 @@ proc-macro = true
proc-macro2 = "1.0" proc-macro2 = "1.0"
syn = "1.0" syn = "1.0"
quote = "1.0" quote = "1.0"
[dev-dependencies]
minecraft-protocol = { version = "0.1.0", path = "../protocol" }
byteorder = "1"

View File

@ -5,7 +5,7 @@ use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
use quote::{quote, TokenStreamExt}; use quote::{quote, TokenStreamExt};
use std::iter::FromIterator; 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))] #[proc_macro_derive(Packet, attributes(packet))]
pub fn derive_packet(input: proc_macro::TokenStream) -> TokenStream1 { 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 encoder = impl_encoder_trait(name, fields);
let decoder = impl_decoder_trait(name, fields); let decoder = impl_decoder_trait(name, fields);
println!("{}", decoder);
TokenStream1::from(quote! { TokenStream1::from(quote! {
#encoder #encoder
@ -64,6 +66,10 @@ fn impl_encoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 {
} }
fn impl_decoder_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 decode = quote_field(fields, |field| {
let name = &field.ident; let name = &field.ident;
let ty = &field.ty; 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 unparsed_meta = get_packet_field_meta(field);
let parsed_meta = parse_packet_field_meta(&unparsed_meta); let parsed_meta = parse_packet_field_meta(&unparsed_meta);
// This is special case because max length are used only for strings. let mut result = quote!();
if let Some(max_length) = parsed_meta.max_length {
return quote! { // When we encounter a bitfield, we skip writing until we get
let #name = crate::DecoderReadExt::read_string(reader, #max_length)?; // 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 { // This is special case because max length are used only for strings.
Some(module) => { if let Some(max_length) = parsed_meta.max_length {
let module_ident = Ident::new(&module, Span::call_site()); 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)?; let #name = crate::#module_ident::decode(reader)?;
} })
} }
None => { None => {
quote! { result.append_all(quote! {
let #name = <#ty as crate::Decoder>::decode(reader)?; let #name = <#ty as crate::Decoder>::decode(reader)?;
} });
} }
} }
return result;
}); });
let create = quote_field(fields, |field| { let create = quote_field(fields, |field| {
@ -122,11 +158,148 @@ fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 {
struct PacketFieldMeta { struct PacketFieldMeta {
module: Option<String>, module: Option<String>,
max_length: Option<u16>, max_length: Option<u16>,
bitfield: Option<Bitfield>,
}
#[derive(Debug, Clone)]
struct Bitfield {
size: usize,
}
macro_rules! decode_bitmask_value (
($value: expr, $name: ident) => (
fn $name(
result: &mut TokenStream2,
name: &Option<Ident>,
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<NestedMeta>) -> PacketFieldMeta { fn parse_packet_field_meta(meta_list: &Vec<NestedMeta>) -> PacketFieldMeta {
let mut module = None; let mut module = None;
let mut max_length = None; let mut max_length = None;
let mut bitfield = None;
for meta in meta_list { for meta in meta_list {
match meta { match meta {
@ -147,14 +320,46 @@ fn parse_packet_field_meta(meta_list: &Vec<NestedMeta>) -> PacketFieldMeta {
}, },
path => panic!( path => panic!(
"Received unrecognized attribute : \"{}\"", "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::<usize>()
.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<NestedMeta> { fn get_packet_field_meta(field: &Field) -> Vec<NestedMeta> {
@ -171,7 +376,7 @@ fn get_packet_field_meta(field: &Field) -> Vec<NestedMeta> {
.collect() .collect()
} }
fn quote_field<F: Fn(&Field) -> TokenStream2>(fields: &Fields, func: F) -> TokenStream2 { fn quote_field<F: FnMut(&Field) -> TokenStream2>(fields: &Fields, mut func: F) -> TokenStream2 {
let mut output = quote!(); let mut output = quote!();
match fields { match fields {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -5,7 +5,7 @@ use num_traits::FromPrimitive;
use std::io::Read; use std::io::Read;
use uuid::Uuid; use uuid::Uuid;
trait Decoder { pub trait Decoder {
type Output; type Output;
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError>; fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError>;
@ -109,6 +109,14 @@ impl Decoder for u8 {
} }
} }
impl Decoder for i16 {
type Output = Self;
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
Ok(reader.read_i16::<BigEndian>()?)
}
}
impl Decoder for i32 { impl Decoder for i32 {
type Output = Self; type Output = Self;
@ -117,6 +125,14 @@ impl Decoder for i32 {
} }
} }
impl Decoder for u16 {
type Output = Self;
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
Ok(reader.read_u16::<BigEndian>()?)
}
}
impl Decoder for u32 { impl Decoder for u32 {
type Output = Self; type Output = Self;

View File

@ -5,7 +5,7 @@ use num_traits::ToPrimitive;
use std::io::Write; use std::io::Write;
use uuid::Uuid; use uuid::Uuid;
trait Encoder { pub trait Encoder {
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError>; fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError>;
} }
@ -103,12 +103,24 @@ impl Encoder for u8 {
} }
} }
impl Encoder for i16 {
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
Ok(writer.write_i16::<BigEndian>(*self)?)
}
}
impl Encoder for i32 { impl Encoder for i32 {
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> { fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
Ok(writer.write_i32::<BigEndian>(*self)?) Ok(writer.write_i32::<BigEndian>(*self)?)
} }
} }
impl Encoder for u16 {
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
Ok(writer.write_u16::<BigEndian>(*self)?)
}
}
impl Encoder for u32 { impl Encoder for u32 {
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> { fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
Ok(writer.write_u32::<BigEndian>(*self)?) Ok(writer.write_u32::<BigEndian>(*self)?)