diff options
Diffstat (limited to 'bzipper_macros/src')
-rw-r--r-- | bzipper_macros/src/discriminant/mod.rs | 88 | ||||
-rw-r--r-- | bzipper_macros/src/discriminant_iter/mod.rs | 71 | ||||
-rw-r--r-- | bzipper_macros/src/generic_name/mod.rs | 39 | ||||
-rw-r--r-- | bzipper_macros/src/impls/decode_enum.rs | 67 | ||||
-rw-r--r-- | bzipper_macros/src/impls/decode_struct.rs | 55 | ||||
-rw-r--r-- | bzipper_macros/src/impls/deserialise_enum.rs | 68 | ||||
-rw-r--r-- | bzipper_macros/src/impls/deserialise_struct.rs | 61 | ||||
-rw-r--r-- | bzipper_macros/src/impls/encode_enum.rs | 77 | ||||
-rw-r--r-- | bzipper_macros/src/impls/encode_struct.rs | 46 | ||||
-rw-r--r-- | bzipper_macros/src/impls/mod.rs | 18 | ||||
-rw-r--r-- | bzipper_macros/src/impls/serialise_enum.rs | 101 | ||||
-rw-r--r-- | bzipper_macros/src/impls/serialise_struct.rs | 69 | ||||
-rw-r--r-- | bzipper_macros/src/impls/sized_encode_enum.rs | 54 | ||||
-rw-r--r-- | bzipper_macros/src/impls/sized_encode_struct.rs (renamed from bzipper_macros/src/closure/mod.rs) | 34 | ||||
-rw-r--r-- | bzipper_macros/src/lib.rs | 94 |
15 files changed, 518 insertions, 424 deletions
diff --git a/bzipper_macros/src/discriminant/mod.rs b/bzipper_macros/src/discriminant/mod.rs index 21a835a..a5d6a95 100644 --- a/bzipper_macros/src/discriminant/mod.rs +++ b/bzipper_macros/src/discriminant/mod.rs @@ -1,98 +1,60 @@ // Copyright 2024 Gabriel Bjørnager Jensen. // -// This file is part of bzipper. +// This file is part of bZipper. // -// bzipper is free software: you can redistribute +// bZipper is free software: you can redistribute // it and/or modify it under the terms of the GNU // Lesser General Public License as published by // the Free Software Foundation, either version 3 // of the License, or (at your option) any later // version. // -// bzipper is distributed in the hope that it will +// bZipper is distributed in the hope that it will // be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If +// er General Public License along with bZipper. If // not, see <https://www.gnu.org/licenses/>. use proc_macro2::TokenStream; use quote::ToTokens; +use syn::{Expr, Lit}; /// An enumeration discriminant. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] #[repr(transparent)] -pub struct Discriminant(u32); +pub struct Discriminant(pub isize); impl Discriminant { - /// Constructs a new discriminant. - #[inline(always)] - #[must_use] - pub const fn new(value: u32) -> Self { Self(value) } - - /// Retrieves the raw discriminant value. - #[inline(always)] - #[must_use] - pub const fn get(self) -> u32 { self.0 } - - /// Unwraps the given value as a discriminant. + /// Parses the expression as a discriminant value. /// /// # Panics /// - /// If the given value cannot be represented as an `u32`, this function will panic. - #[inline(always)] + /// This constructor will panic if the provided expression is not a valid `isize` literal. + #[inline] #[must_use] - pub fn unwrap_from<T: TryInto<Self>>(value: T) -> Self { - value - .try_into() - .unwrap_or_else(|_| panic!("enumeration discriminants must be representable in `u32`")) - } - - /// Unsafely unwraps the given value as a discriminant. - /// - /// This function assumes that this conversion is infallible for the given value. - /// If this is a false guarantee, the [`unwrap_from`](Self::unwrap_from) function should be used instead. - /// - /// # Safety - /// - /// Behaviour is undefined if the given value cannot be represented as an object of `u32`. - #[inline(always)] - #[must_use] - pub unsafe fn unwrap_from_unchecked<T: TryInto<Self>>(value: T) -> Self { - value - .try_into() - .unwrap_unchecked() - } -} - -impl ToTokens for Discriminant { - #[inline(always)] - fn to_tokens(&self, tokens: &mut TokenStream) { self.0.to_tokens(tokens) } -} + pub fn parse(expr: &Expr) -> Self { + let Expr::Lit(ref expr) = *expr else { + panic!("expected literal expression for discriminant value"); + }; -impl From<u32> for Discriminant { - #[inline(always)] - fn from(value: u32) -> Self { Self(value) } -} + let Lit::Int(ref expr) = expr.lit else { + panic!("expected integer literal for discriminant value"); + }; -impl TryFrom<usize> for Discriminant { - type Error = <u32 as TryFrom<usize>>::Error; + let value = expr.base10_parse::<isize>() + .expect("expected `isize` literal for discriminant value"); - #[inline(always)] - fn try_from(value: usize) -> Result<Self, Self::Error> { value.try_into().map(Self) } -} - -impl From<Discriminant> for u32 { - #[inline(always)] - fn from(value: Discriminant) -> Self { value.0 } + Self(value) + } } -impl TryFrom<Discriminant> for usize { - type Error = <Self as TryFrom<u32>>::Error; - +impl ToTokens for Discriminant { #[inline(always)] - fn try_from(value: Discriminant) -> Result<Self, Self::Error> { value.0.try_into() } + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } } diff --git a/bzipper_macros/src/discriminant_iter/mod.rs b/bzipper_macros/src/discriminant_iter/mod.rs new file mode 100644 index 0000000..ea34f6d --- /dev/null +++ b/bzipper_macros/src/discriminant_iter/mod.rs @@ -0,0 +1,71 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bZipper. +// +// bZipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bZipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bZipper. If +// not, see <https://www.gnu.org/licenses/>. + +use crate::Discriminant; + +use std::borrow::Borrow; +use syn::Variant; + +pub struct DiscriminantIter<I: IntoIterator<Item: Borrow<Variant>>> { + variants: I::IntoIter, + prev: Option<Discriminant>, +} + +impl<I: IntoIterator<Item: Borrow<Variant>>> DiscriminantIter<I> { + #[inline(always)] + #[must_use] + pub fn new(variants: I) -> Self { + Self { + variants: variants.into_iter(), + prev: None, + } + } +} + +impl<I: IntoIterator<Item: Borrow<Variant>>> Iterator for DiscriminantIter<I> { + type Item = (Discriminant, I::Item); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let variant = self.variants.next()?; + + let discriminant = if let Some((_, ref discriminant)) = variant.borrow().discriminant { + Discriminant::parse(discriminant) + } else if let Some(discriminant) = self.prev { + let value = discriminant.0 + .checked_add(0x1) + .unwrap_or_else(|| panic!("overflow following discriminant `{discriminant:?}`")); + + Discriminant(value) + } else { + Default::default() + }; + + self.prev = Some(discriminant); + + Some((discriminant, variant)) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option<usize>) { + self.variants.size_hint() + } +} diff --git a/bzipper_macros/src/generic_name/mod.rs b/bzipper_macros/src/generic_name/mod.rs index a0c51f5..6fc0bc9 100644 --- a/bzipper_macros/src/generic_name/mod.rs +++ b/bzipper_macros/src/generic_name/mod.rs @@ -1,26 +1,28 @@ // Copyright 2024 Gabriel Bjørnager Jensen. // -// This file is part of bzipper. +// This file is part of bZipper. // -// bzipper is free software: you can redistribute +// bZipper is free software: you can redistribute // it and/or modify it under the terms of the GNU // Lesser General Public License as published by // the Free Software Foundation, either version 3 // of the License, or (at your option) any later // version. // -// bzipper is distributed in the hope that it will +// bZipper is distributed in the hope that it will // be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If +// er General Public License along with bZipper. If // not, see <https://www.gnu.org/licenses/>. use proc_macro2::TokenStream; use quote::ToTokens; +use std::fmt; +use std::fmt::{Debug, Formatter}; use syn::{ GenericParam, Generics, @@ -33,9 +35,14 @@ use syn::{ /// A name of a genric. #[derive(Clone)] pub enum GenericName { - Const( Ident), + /// Denotes a generic constant. + Const(Ident), + + /// Denotes a generic lifetime. Lifetime(Lifetime), - Type( Ident), + + /// Denotes a generic type. + Ty(Ident), } impl GenericName { @@ -48,14 +55,28 @@ impl GenericName { let name = match *generic { GenericParam::Const( ref param) => Self::Const( param.ident.clone()), GenericParam::Lifetime(ref param) => Self::Lifetime(param.lifetime.clone()), - GenericParam::Type( ref param) => Self::Type( param.ident.clone()), + GenericParam::Type( ref param) => Self::Ty( param.ident.clone()), }; names.push(name); } names - } + } +} + +impl Debug for GenericName { + #[inline] + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let ident = match *self { + | Self::Const(ref ident) + | Self::Lifetime(Lifetime { ref ident, .. }) + | Self::Ty(ref ident) + => ident, + }; + + Debug::fmt(ident, f) + } } impl ToTokens for GenericName { @@ -65,7 +86,7 @@ impl ToTokens for GenericName { match *self { | Const(ref ident) - | Type( ref ident) + | Ty( ref ident) => ident.to_tokens(tokens), Lifetime(ref lifetime) => lifetime.to_tokens(tokens), diff --git a/bzipper_macros/src/impls/decode_enum.rs b/bzipper_macros/src/impls/decode_enum.rs new file mode 100644 index 0000000..c773de2 --- /dev/null +++ b/bzipper_macros/src/impls/decode_enum.rs @@ -0,0 +1,67 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bZipper. +// +// bZipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bZipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bZipper. If +// not, see <https://www.gnu.org/licenses/>. + +use crate::DiscriminantIter; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{DataEnum, Fields, Token, Variant}; +use syn::punctuated::Punctuated; + +#[must_use] +pub fn decode_enum(data: &DataEnum) -> TokenStream { + let mut match_arms = Punctuated::<TokenStream, Token![,]>::new(); + + for (discriminant, variant) in DiscriminantIter::new(&data.variants) { + let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new(); + + for field in &variant.fields { + let command = field.ident + .as_ref() + .map_or_else( + || quote! { ::bzipper::Decode::decode(stream)? }, + |field_name| quote! { #field_name: ::bzipper::Decode::decode(stream)? }, + ); + + chain_commands.push(command); + } + + let value = match *variant { + Variant { ident: ref variant_name, fields: Fields::Named( ..), .. } => quote! { Self::#variant_name { #chain_commands } }, + Variant { ident: ref variant_name, fields: Fields::Unnamed(..), .. } => quote! { Self::#variant_name(#chain_commands) }, + Variant { ident: ref variant_name, fields: Fields::Unit, .. } => quote! { Self::#variant_name }, + }; + + match_arms.push(quote! { #discriminant => #value }); + } + + match_arms.push(quote! { + value => return ::core::result::Result::Err(::bzipper::error::DecodeError::InvalidDiscriminant(value)) + }); + + quote! { + #[inline] + fn decode(stream: &mut ::bzipper::IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> { + let value = match (<isize as ::bzipper::Decode>::decode(stream)?) { #match_arms }; + Ok(value) + } + } +} diff --git a/bzipper_macros/src/impls/decode_struct.rs b/bzipper_macros/src/impls/decode_struct.rs new file mode 100644 index 0000000..9688e91 --- /dev/null +++ b/bzipper_macros/src/impls/decode_struct.rs @@ -0,0 +1,55 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bZipper. +// +// bZipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bZipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bZipper. If +// not, see <https://www.gnu.org/licenses/>. + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{DataStruct, Fields, Token}; +use syn::punctuated::Punctuated; + +#[must_use] +pub fn decode_struct(data: &DataStruct) -> TokenStream { + let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new(); + + for field in &data.fields { + let command = field.ident + .as_ref() + .map_or_else( + || quote! { ::bzipper::Decode::decode(stream)? }, + |field_name| quote! { #field_name: ::bzipper::Decode::decode(stream)? }, + ); + + chain_commands.push(command); + } + + let value = match data.fields { + Fields::Named( ..) => quote! { Self { #chain_commands } }, + Fields::Unnamed(..) => quote! { Self(#chain_commands) }, + Fields::Unit => quote! { Self }, + }; + + quote! { + #[inline] + fn decode(stream: &mut ::bzipper::IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> { + let value = #value; + Ok(value) + } + } +} diff --git a/bzipper_macros/src/impls/deserialise_enum.rs b/bzipper_macros/src/impls/deserialise_enum.rs deleted file mode 100644 index 4c88a41..0000000 --- a/bzipper_macros/src/impls/deserialise_enum.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2024 Gabriel Bjørnager Jensen. -// -// This file is part of bzipper. -// -// bzipper is free software: you can redistribute -// it and/or modify it under the terms of the GNU -// Lesser General Public License as published by -// the Free Software Foundation, either version 3 -// of the License, or (at your option) any later -// version. -// -// bzipper is distributed in the hope that it will -// be useful, but WITHOUT ANY WARRANTY; without -// even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If -// not, see <https://www.gnu.org/licenses/>. - -use crate::Discriminant; - -use proc_macro2::TokenStream; -use quote::quote; -use syn::{DataEnum, Fields, Token}; -use syn::punctuated::Punctuated; - -#[must_use] -pub fn deserialise_enum(data: &DataEnum) -> TokenStream { - let mut match_arms = Punctuated::<TokenStream, Token![,]>::new(); - - for (index, variant) in data.variants.iter().enumerate() { - let variant_name = &variant.ident; - - let discriminant = Discriminant::unwrap_from(index); - - let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new(); - - for field in &variant.fields { - let command = field.ident - .as_ref() - .map_or_else( - || quote! { Deserialise::deserialise(stream)? }, - |field_name| quote! { #field_name: Deserialise::deserialise(stream)? } - ); - - chain_commands.push(command); - } - - let value = match variant.fields { - Fields::Named( ..) => quote! { Self::#variant_name { #chain_commands } }, - Fields::Unnamed(..) => quote! { Self::#variant_name(#chain_commands) }, - Fields::Unit => quote! { Self::#variant_name }, - }; - - match_arms.push(quote! { #discriminant => #value }); - } - - match_arms.push(quote! { value => return Err(::bzipper::Error::InvalidDiscriminant(value)) }); - - quote! { - fn deserialise(stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> { - let value = match (<u32 as ::bzipper::Deserialise>::deserialise(stream)?) { #match_arms }; - Ok(value) - } - } -} diff --git a/bzipper_macros/src/impls/deserialise_struct.rs b/bzipper_macros/src/impls/deserialise_struct.rs deleted file mode 100644 index f8c167b..0000000 --- a/bzipper_macros/src/impls/deserialise_struct.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024 Gabriel Bjørnager Jensen. -// -// This file is part of bzipper. -// -// bzipper is free software: you can redistribute -// it and/or modify it under the terms of the GNU -// Lesser General Public License as published by -// the Free Software Foundation, either version 3 -// of the License, or (at your option) any later -// version. -// -// bzipper is distributed in the hope that it will -// be useful, but WITHOUT ANY WARRANTY; without -// even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If -// not, see <https://www.gnu.org/licenses/>. - -use proc_macro2::TokenStream; -use quote::quote; -use syn::{DataStruct, Fields, Token}; -use syn::punctuated::Punctuated; - -#[must_use] -pub fn deserialise_struct(data: &DataStruct) -> TokenStream { - if matches!(data.fields, Fields::Unit) { - quote! { - #[inline(always)] - fn deserialise(_stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> { Ok(Self) } - } - } else { - let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new(); - - for field in &data.fields { - let command = field.ident - .as_ref() - .map_or_else( - || quote! { Deserialise::deserialise(stream)? }, - |field_name| quote! { #field_name: Deserialise::deserialise(stream)? } - ); - - chain_commands.push(command); - } - - let value = if let Fields::Named(..) = data.fields { - quote! { Self { #chain_commands } } - } else { - quote! { Self(#chain_commands) } - }; - - quote! { - fn deserialise(stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> { - let value = #value; - Ok(value) - } - } - } -} diff --git a/bzipper_macros/src/impls/encode_enum.rs b/bzipper_macros/src/impls/encode_enum.rs new file mode 100644 index 0000000..37acd34 --- /dev/null +++ b/bzipper_macros/src/impls/encode_enum.rs @@ -0,0 +1,77 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bZipper. +// +// bZipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bZipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bZipper. If +// not, see <https://www.gnu.org/licenses/>. + +use crate::DiscriminantIter; + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{ + DataEnum, + Fields, + Ident, + Variant, +}; + +#[must_use] +pub fn encode_enum(data: &DataEnum) -> TokenStream { + let mut match_arms = Vec::new(); + + // Iterate over each variant and give it a unique + // encoding scheme. + for (discriminant, variant) in DiscriminantIter::new(&data.variants) { + // The original identifiers of the fields: + let mut field_names = Vec::new(); + + // The captured field identifiers: + let mut field_captures = Vec::new(); + + for (index, field) in variant.fields.iter().enumerate() { + let capture = Ident::new(&format!("v{index}"), Span::call_site()); + + field_names.push(&field.ident); + field_captures.push(capture); + } + + let pattern = match *variant { + Variant { ident: ref variant_name, fields: Fields::Named( ..), .. } => quote! { Self::#variant_name { #(#field_names: ref #field_captures, )* } }, + Variant { ident: ref variant_name, fields: Fields::Unnamed(..), .. } => quote! { Self::#variant_name(#(ref #field_captures)*) }, + Variant { ident: ref variant_name, fields: Fields::Unit, .. } => quote! { Self::#variant_name }, + }; + + match_arms.push(quote! { + #pattern => { + ::bzipper::Encode::encode(&#discriminant, stream)?; + #(::bzipper::Encode::encode(#field_captures, stream)?;)* + } + }); + } + + quote! { + #[inline] + fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> { + match *self { + #(#match_arms)* + } + + Ok(()) + } + } +} diff --git a/bzipper_macros/src/impls/encode_struct.rs b/bzipper_macros/src/impls/encode_struct.rs new file mode 100644 index 0000000..e853e44 --- /dev/null +++ b/bzipper_macros/src/impls/encode_struct.rs @@ -0,0 +1,46 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bZipper. +// +// bZipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bZipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bZipper. If +// not, see <https://www.gnu.org/licenses/>. + +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::{DataStruct, Index}; + +#[must_use] +pub fn encode_struct(data: &DataStruct) -> TokenStream { + let mut fields = Vec::new(); + + for (index, field) in data.fields.iter().enumerate() { + let name = field.ident + .as_ref() + .map_or_else(|| Index::from(index).to_token_stream(), ToTokens::to_token_stream); + + fields.push(name); + } + + quote! { + #[inline] + fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> { + #(::bzipper::Encode::encode(&self.#fields, stream)?;)* + + Ok(()) + } + } +} diff --git a/bzipper_macros/src/impls/mod.rs b/bzipper_macros/src/impls/mod.rs index d61cf90..cdbc9ac 100644 --- a/bzipper_macros/src/impls/mod.rs +++ b/bzipper_macros/src/impls/mod.rs @@ -1,26 +1,28 @@ // Copyright 2024 Gabriel Bjørnager Jensen. // -// This file is part of bzipper. +// This file is part of bZipper. // -// bzipper is free software: you can redistribute +// bZipper is free software: you can redistribute // it and/or modify it under the terms of the GNU // Lesser General Public License as published by // the Free Software Foundation, either version 3 // of the License, or (at your option) any later // version. // -// bzipper is distributed in the hope that it will +// bZipper is distributed in the hope that it will // be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If +// er General Public License along with bZipper. If // not, see <https://www.gnu.org/licenses/>. use crate::use_mod; -use_mod!(pub deserialise_enum); -use_mod!(pub deserialise_struct); -use_mod!(pub serialise_enum); -use_mod!(pub serialise_struct); +use_mod!(pub decode_enum); +use_mod!(pub decode_struct); +use_mod!(pub encode_enum); +use_mod!(pub encode_struct); +use_mod!(pub sized_encode_enum); +use_mod!(pub sized_encode_struct); diff --git a/bzipper_macros/src/impls/serialise_enum.rs b/bzipper_macros/src/impls/serialise_enum.rs deleted file mode 100644 index 825886c..0000000 --- a/bzipper_macros/src/impls/serialise_enum.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2024 Gabriel Bjørnager Jensen. -// -// This file is part of bzipper. -// -// bzipper is free software: you can redistribute -// it and/or modify it under the terms of the GNU -// Lesser General Public License as published by -// the Free Software Foundation, either version 3 -// of the License, or (at your option) any later -// version. -// -// bzipper is distributed in the hope that it will -// be useful, but WITHOUT ANY WARRANTY; without -// even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If -// not, see <https://www.gnu.org/licenses/>. - -use crate::Capture; - -use proc_macro2::{Span, TokenStream}; -use quote::quote; -use syn::{DataEnum, Fields, Ident, Token}; -use syn::punctuated::Punctuated; - -#[must_use] -pub fn serialise_enum(data: &DataEnum) -> TokenStream { - let mut sizes = Vec::new(); - - let mut match_arms = Punctuated::<TokenStream, Token![,]>::new(); - - for (index, variant) in data.variants.iter().enumerate() { - let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new(); - - let variant_name = &variant.ident; - - let discriminant = u32::try_from(index) - .expect("enumeration discriminants must be representable as `u32`"); - - // Discriminant size: - serialised_size.push(quote! { <u32 as ::bzipper::Serialise>::MAX_SERIALISED_SIZE }); - - let mut captures = Punctuated::<Capture, Token![,]>::new(); - - let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new(); - chain_commands.push(quote! { #discriminant.serialise(stream)? }); - - for (index, field) in variant.fields.iter().enumerate() { - let field_ty = &field.ty; - - let field_name = field.ident - .as_ref() - .map_or_else(|| Ident::new(&format!("v{index}"), Span::call_site()), Clone::clone); - - serialised_size.push(quote! { <#field_ty as ::bzipper::Serialise>::MAX_SERIALISED_SIZE }); - - captures.push(Capture { - ref_token: Token![ref](Span::call_site()), - ident: field_name.clone(), - }); - - chain_commands.push(quote! { #field_name.serialise(stream)? }); - } - - chain_commands.push_punct(Token![;](Span::call_site())); - - let arm = match variant.fields { - Fields::Named( ..) => quote! { Self::#variant_name { #captures } => { #chain_commands } }, - Fields::Unnamed(..) => quote! { Self::#variant_name(#captures) => { #chain_commands } }, - Fields::Unit => quote! { Self::#variant_name => { #chain_commands } }, - }; - - sizes.push(serialised_size); - match_arms.push(arm); - } - - let mut size_tests = Punctuated::<TokenStream, Token![else]>::new(); - - for size in &sizes { - let mut test = Punctuated::<TokenStream, Token![&&]>::new(); - - for other_size in &sizes { test.push(quote! { #size >= #other_size }) } - - size_tests.push(quote! { if #test { #size } }); - } - - size_tests.push(quote! { { core::unreachable!(); } }); - - quote! { - const MAX_SERIALISED_SIZE: usize = const { #size_tests }; - - fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> { - match (*self) { #match_arms } - - Ok(()) - } - } -} diff --git a/bzipper_macros/src/impls/serialise_struct.rs b/bzipper_macros/src/impls/serialise_struct.rs deleted file mode 100644 index bd81a39..0000000 --- a/bzipper_macros/src/impls/serialise_struct.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2024 Gabriel Bjørnager Jensen. -// -// This file is part of bzipper. -// -// bzipper is free software: you can redistribute -// it and/or modify it under the terms of the GNU -// Lesser General Public License as published by -// the Free Software Foundation, either version 3 -// of the License, or (at your option) any later -// version. -// -// bzipper is distributed in the hope that it will -// be useful, but WITHOUT ANY WARRANTY; without -// even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If -// not, see <https://www.gnu.org/licenses/>. - -use proc_macro2::{Span, TokenStream}; -use quote::{quote, ToTokens}; -use syn::{ - DataStruct, - Fields, - Index, - Token, - punctuated::Punctuated -}; - -#[must_use] -pub fn serialise_struct(data: &DataStruct) -> TokenStream { - if matches!(data.fields, Fields::Unit) { - quote! { - const MAX_SERIALISED_SIZE: usize = 0x0; - - #[inline(always)] - fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> { Ok(()) } - } - } else { - let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new(); - let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new(); - - for (index, field) in data.fields.iter().enumerate() { - let ty = &field.ty; - - let name = field.ident - .as_ref() - .map_or_else(|| Index::from(index).to_token_stream(), ToTokens::to_token_stream); - - serialised_size.push(quote! { <#ty as ::bzipper::Serialise>::MAX_SERIALISED_SIZE }); - - chain_commands.push(quote! { self.#name.serialise(stream)? }); - } - - chain_commands.push_punct(Token![;](Span::call_site())); - - quote! { - const MAX_SERIALISED_SIZE: usize = #serialised_size; - - fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> { - #chain_commands - - Ok(()) - } - } - } -} diff --git a/bzipper_macros/src/impls/sized_encode_enum.rs b/bzipper_macros/src/impls/sized_encode_enum.rs new file mode 100644 index 0000000..3bfc961 --- /dev/null +++ b/bzipper_macros/src/impls/sized_encode_enum.rs @@ -0,0 +1,54 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bZipper. +// +// bZipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bZipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bZipper. If +// not, see <https://www.gnu.org/licenses/>. + +use proc_macro2::TokenStream; +use quote::quote; +use syn::DataEnum; + +#[must_use] +pub fn sized_encode_enum(data: &DataEnum) -> TokenStream { + let mut sizes = Vec::new(); + + // Iterate over each variant and give it a unique + // encoding scheme. + for variant in &data.variants { + let mut field_tys = Vec::new(); + + for field in &variant.fields { + field_tys.push(&field.ty); + } + + sizes.push(quote! { + <isize as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE + #(+ <#field_tys as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE)* + }); + } + + quote! { + const MAX_ENCODED_SIZE: usize = const { + let mut max_encoded_size = 0x0usize; + + #(if #sizes > max_encoded_size { max_encoded_size = #sizes };)* + + max_encoded_size + }; + } +} diff --git a/bzipper_macros/src/closure/mod.rs b/bzipper_macros/src/impls/sized_encode_struct.rs index 86d19d4..e194e08 100644 --- a/bzipper_macros/src/closure/mod.rs +++ b/bzipper_macros/src/impls/sized_encode_struct.rs @@ -1,41 +1,37 @@ // Copyright 2024 Gabriel Bjørnager Jensen. // -// This file is part of bzipper. +// This file is part of bZipper. // -// bzipper is free software: you can redistribute +// bZipper is free software: you can redistribute // it and/or modify it under the terms of the GNU // Lesser General Public License as published by // the Free Software Foundation, either version 3 // of the License, or (at your option) any later // version. // -// bzipper is distributed in the hope that it will +// bZipper is distributed in the hope that it will // be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If +// er General Public License along with bZipper. If // not, see <https://www.gnu.org/licenses/>. use proc_macro2::TokenStream; -use quote::ToTokens; -use syn::{Ident, Token}; +use quote::quote; +use syn::DataStruct; -/// A field capture list. -/// -/// This is used for capturing fields of structures or enumeration variants. -#[derive(Clone)] -pub struct Capture { - pub ref_token: Token![ref], - pub ident: Ident, -} +#[must_use] +pub fn sized_encode_struct(data: &DataStruct) -> TokenStream { + let mut field_tys = Vec::new(); + + for field in &data.fields { + field_tys.push(&field.ty); + } -impl ToTokens for Capture { - #[inline(always)] - fn to_tokens(&self, tokens: &mut TokenStream) { - self.ref_token.to_tokens(tokens); - self.ident.to_tokens(tokens); + quote! { + const MAX_ENCODED_SIZE: usize = 0x0 #( + <#field_tys as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE)*; } } diff --git a/bzipper_macros/src/lib.rs b/bzipper_macros/src/lib.rs index f7979c8..9bb1480 100644 --- a/bzipper_macros/src/lib.rs +++ b/bzipper_macros/src/lib.rs @@ -1,33 +1,31 @@ // Copyright 2024 Gabriel Bjørnager Jensen. // -// This file is part of bzipper. +// This file is part of bZipper. // -// bzipper is free software: you can redistribute +// bZipper is free software: you can redistribute // it and/or modify it under the terms of the GNU // Lesser General Public License as published by // the Free Software Foundation, either version 3 // of the License, or (at your option) any later // version. // -// bzipper is distributed in the hope that it will +// bZipper is distributed in the hope that it will // be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If +// er General Public License along with bZipper. If // not, see <https://www.gnu.org/licenses/>. -#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg?ref_type=heads")] +#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg")] -//! Binary (de)serialisation. -//! -//! This crate implements macros for the [`bzipper`](https://crates.io/crates/bzipper/) crate. +//! This crate implements procedural macros for [`bzipper`](https://crates.io/crates/bzipper/). use proc_macro::TokenStream; use quote::quote; -use syn::{Data, DeriveInput, parse_macro_input}; +use syn::{parse_macro_input, Data, DeriveInput}; macro_rules! use_mod { ($vis:vis $name:ident) => { @@ -35,26 +33,26 @@ macro_rules! use_mod { $vis use $name::*; }; } -pub(in crate) use use_mod; +pub(crate) use use_mod; -use_mod!(closure); use_mod!(discriminant); +use_mod!(discriminant_iter); use_mod!(generic_name); mod impls; -#[proc_macro_derive(Deserialise)] -pub fn derive_deserialise(input: TokenStream) -> TokenStream { +#[proc_macro_derive(Decode)] +pub fn derive_decode(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let impl_body = match input.data { - Data::Enum( ref data) => impls::deserialise_enum( data), - Data::Struct(ref data) => impls::deserialise_struct(data), + Data::Enum( ref data) => impls::decode_enum( data), + Data::Struct(ref data) => impls::decode_struct(data), - Data::Union(..) => panic!("unions cannot derive `Deserialise`"), + Data::Union(..) => panic!("unions cannot derive `Decode`"), }; - let type_name = &input.ident; + let ty_name = &input.ident; let generic_params = &input.generics.params; let generic_where = &input.generics.where_clause; @@ -62,27 +60,29 @@ pub fn derive_deserialise(input: TokenStream) -> TokenStream { let generic_names = GenericName::extract_from(&input.generics); let output = quote! { - impl<#generic_params> ::bzipper::Deserialise for #type_name<#generic_names> + impl<#generic_params> ::bzipper::Decode for #ty_name<#generic_names> #generic_where { #impl_body } }; + //panic!("{output}"); + output.into() } -#[proc_macro_derive(Serialise)] -pub fn derive_serialise(input: TokenStream) -> TokenStream { +#[proc_macro_derive(Encode)] +pub fn derive_encode(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let impl_body = match input.data { - Data::Enum( ref data) => impls::serialise_enum( data), - Data::Struct(ref data) => impls::serialise_struct(data), + Data::Enum( ref data) => impls::encode_enum( data), + Data::Struct(ref data) => impls::encode_struct(data), - Data::Union(..) => panic!("unions cannot derive `Serialise`"), + Data::Union(..) => panic!("unions cannot derive `Encode`"), }; - let type_name = &input.ident; + let ty_name = &input.ident; let generic_params = &input.generics.params; let generic_where = &input.generics.where_clause; @@ -90,13 +90,55 @@ pub fn derive_serialise(input: TokenStream) -> TokenStream { let generic_names = GenericName::extract_from(&input.generics); let output = quote! { - impl<#generic_params> ::bzipper::Serialise for #type_name<#generic_names> + impl<#generic_params> ::bzipper::Encode for #ty_name<#generic_names> #generic_where { #impl_body } }; - //if let Data::Enum(..) = input.data { panic!("{output}") }; + //panic!("{output}"); + + output.into() +} + +#[proc_macro_derive(SizedEncode)] +pub fn derive_sized_encode(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let encode_impl_body = match input.data { + Data::Enum( ref data) => impls::encode_enum( data), + Data::Struct(ref data) => impls::encode_struct(data), + + Data::Union(..) => panic!("unions can neither derive `Encode` nor `SizedEncode`"), + }; + + let sized_encode_impl_body = match input.data { + Data::Enum( ref data) => impls::sized_encode_enum( data), + Data::Struct(ref data) => impls::sized_encode_struct(data), + + Data::Union(..) => unreachable!(), + }; + + let ty_name = &input.ident; + + let generic_params = &input.generics.params; + let generic_where = &input.generics.where_clause; + + let generic_names = GenericName::extract_from(&input.generics); + + let output = quote! { + impl<#generic_params> ::bzipper::Encode for #ty_name<#generic_names> + #generic_where { + #encode_impl_body + } + + unsafe impl<#generic_params> ::bzipper::SizedEncode for #ty_name<#generic_names> + #generic_where { + #sized_encode_impl_body + } + }; + + //panic!("{output}"); output.into() } |