diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/enum-map-derive/src | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/enum-map-derive/src')
-rw-r--r-- | third_party/rust/enum-map-derive/src/derive_enum.rs | 216 | ||||
-rw-r--r-- | third_party/rust/enum-map-derive/src/derive_struct.rs | 129 | ||||
-rw-r--r-- | third_party/rust/enum-map-derive/src/lib.rs | 178 |
3 files changed, 523 insertions, 0 deletions
diff --git a/third_party/rust/enum-map-derive/src/derive_enum.rs b/third_party/rust/enum-map-derive/src/derive_enum.rs new file mode 100644 index 0000000000..8dce3de984 --- /dev/null +++ b/third_party/rust/enum-map-derive/src/derive_enum.rs @@ -0,0 +1,216 @@ +// SPDX-FileCopyrightText: 2021 - 2023 Kamila Borowska <kamila@borowska.pw> +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann <brunoczim@gmail.com> +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::type_length; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{DataEnum, Fields, FieldsNamed, FieldsUnnamed, Ident, Variant}; + +pub fn generate(name: Ident, data_enum: DataEnum) -> TokenStream { + let mut generator = EnumGenerator::empty(); + for variant in &data_enum.variants { + generator.handle_variant(variant); + } + generator.finish(&name) +} + +#[derive(Debug)] +struct Length { + units: usize, + opaque: TokenStream, +} + +impl ToTokens for Length { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { units, opaque } = self; + tokens.extend(quote! { (#units + #opaque) }); + } +} + +/// Total length is the sum of each variant's length. To represent a variant, its number is added to +/// the sum of previous variant lengths. +#[derive(Debug)] +struct EnumGenerator { + length: Length, + from_usize_arms: TokenStream, + into_usize_arms: TokenStream, +} + +impl EnumGenerator { + fn empty() -> Self { + Self { + length: Length { + units: 0, + opaque: quote! { 0 }, + }, + from_usize_arms: quote! {}, + into_usize_arms: quote! {}, + } + } + + fn finish(&self, name: &Ident) -> TokenStream { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + + quote! { + #[automatically_derived] + impl ::enum_map::Enum for #name { + const LENGTH: ::enum_map::usize = #length; + + #[inline] + fn from_usize(value: ::enum_map::usize) -> Self { + #from_usize_arms { + ::enum_map::out_of_bounds() + } + } + + #[inline] + fn into_usize(self) -> ::enum_map::usize { + match self { + #into_usize_arms + } + } + } + + #[automatically_derived] + impl<V> ::enum_map::EnumArray<V> for #name { + type Array = [V; #length]; + } + } + } + + fn handle_variant(&mut self, variant: &Variant) { + match &variant.fields { + Fields::Unit => self.handle_unit_variant(&variant.ident), + Fields::Unnamed(fields) => self.handle_unnamed_variant(&variant.ident, fields), + Fields::Named(fields) => self.handle_named_variant(&variant.ident, fields), + } + } + + /// Becomes simply `1` in counting, since this is the size of the unit. + fn handle_unit_variant(&mut self, variant: &Ident) { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + *into_usize_arms = quote! { #into_usize_arms Self::#variant => #length, }; + *from_usize_arms = quote! { + #from_usize_arms if value == #length { + Self::#variant + } else + }; + self.length.units += 1; + } + + /// Its size is the product of the sizes of its members. To represent this variant, one can + /// think of this as representing a little-endian number. First member is simply added, but + /// next members are multiplied before being added. + fn handle_unnamed_variant(&mut self, variant: &Ident, fields: &FieldsUnnamed) { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + let mut expr_into = quote! { #length }; + let mut fields_length = quote! { 1usize }; + let mut params_from = quote! {}; + for (i, field) in fields.unnamed.iter().enumerate() { + let ident = format_ident!("p{}", i); + let ty = &field.ty; + let field_length = type_length(ty); + + expr_into = quote! { + (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident)) + }; + + params_from = quote! { + #params_from <#ty as ::enum_map::Enum>::from_usize( + (value - #length) / #fields_length % #field_length + ), + }; + + fields_length = quote! { (#fields_length * #field_length) }; + } + + *length = Length { + units: 0, + opaque: quote! { (#length + #fields_length) }, + }; + + let from_arms = &from_usize_arms; + *from_usize_arms = quote! { + #from_arms if value < #length { + Self::#variant(#params_from) + } else + }; + + let mut params_into = quote! {}; + for i in 0..fields.unnamed.len() { + let ident = format_ident!("p{}", i); + params_into = quote! { #params_into #ident, }; + } + + *into_usize_arms = quote! { + #into_usize_arms Self::#variant(#params_into) => #expr_into, + }; + } + + /// Its size is the product of the sizes of its members. To represent this variant, one can + /// think of this as representing a little-endian number. First member is simply added, but + /// next members are multiplied before being added. + fn handle_named_variant(&mut self, variant: &Ident, fields: &FieldsNamed) { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + let mut expr_into = quote! { #length }; + let mut fields_length = quote! { 1usize }; + let mut params_from = quote! {}; + + for field in fields.named.iter() { + let ident = field.ident.as_ref().unwrap(); + let ty = &field.ty; + let field_length = type_length(ty); + + expr_into = quote! { + (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident)) + }; + + params_from = quote! { + #params_from #ident: <#ty as ::enum_map::Enum>::from_usize( + (value - #length) / #fields_length % #field_length + ), + }; + + fields_length = quote! { (#fields_length * #field_length) }; + } + + *length = Length { + units: 0, + opaque: quote! { (#length + #fields_length) }, + }; + + *from_usize_arms = quote! { + #from_usize_arms if value < #length { + Self::#variant { #params_from } + } else + }; + + let mut params_into = quote! {}; + for field in fields.named.iter() { + let ident = field.ident.as_ref().unwrap(); + params_into = quote! { #params_into #ident, }; + } + + *into_usize_arms = quote! { + #into_usize_arms Self::#variant { #params_into } => #expr_into, + }; + } +} diff --git a/third_party/rust/enum-map-derive/src/derive_struct.rs b/third_party/rust/enum-map-derive/src/derive_struct.rs new file mode 100644 index 0000000000..94f3f97250 --- /dev/null +++ b/third_party/rust/enum-map-derive/src/derive_struct.rs @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2021 - 2022 Kamila Borowska <kamila@borowska.pw> +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann <brunoczim@gmail.com> +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::type_length; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{DataStruct, Fields, FieldsNamed, FieldsUnnamed, Ident, Index}; + +pub fn generate(name: Ident, data_struct: DataStruct) -> TokenStream { + StructGenerator::from_fields(&data_struct.fields).finish(&name) +} + +/// Total length is the product of each member's length. To represent a struct, one can +/// think of this as representing a little-endian number. First member is simply added, but +/// next members are multiplied before being added. +#[derive(Debug)] +struct StructGenerator { + length: TokenStream, + from_usize: TokenStream, + into_usize: TokenStream, +} + +impl StructGenerator { + fn from_fields(fields: &Fields) -> Self { + match fields { + Fields::Unit => Self::from_unit_fields(), + Fields::Unnamed(fields_data) => Self::from_unnamed_fields(fields_data), + Fields::Named(fields_data) => Self::from_named_fields(fields_data), + } + } + + fn from_unit_fields() -> Self { + Self { + length: quote! { 1usize }, + from_usize: quote! { Self }, + into_usize: quote! { 0usize }, + } + } + + fn from_unnamed_fields(fields: &FieldsUnnamed) -> Self { + let mut params_from = quote! {}; + let mut into_usize = quote! { 0usize }; + let mut length = quote! { 1usize }; + for (i, field) in fields.unnamed.iter().enumerate() { + let ty = &field.ty; + let index_ident = Index::from(i); + let field_length = type_length(ty); + + into_usize = quote! { + (#into_usize + #length * ::enum_map::Enum::into_usize(self.#index_ident)) + }; + + params_from = quote! { + #params_from <#ty as ::enum_map::Enum>::from_usize( + value / #length % #field_length + ), + }; + + length = quote! { (#length * #field_length) }; + } + + let from_usize = quote! { Self(#params_from) }; + Self { + length, + from_usize, + into_usize, + } + } + + fn from_named_fields(fields: &FieldsNamed) -> Self { + let mut params_from = quote! {}; + let mut into_usize = quote! { 0usize }; + let mut length = quote! { 1usize }; + for field in fields.named.iter() { + let ty = &field.ty; + let ident = field.ident.as_ref().unwrap(); + let field_length = type_length(ty); + + into_usize = quote! { + (#into_usize + #length * ::enum_map::Enum::into_usize(self.#ident)) + }; + + params_from = quote! { + #params_from #ident: <#ty as ::enum_map::Enum>::from_usize( + value / #length % #field_length + ), + }; + + length = quote! { (#field_length * #length) }; + } + + let from_usize = quote! { Self { #params_from } }; + Self { + length, + from_usize, + into_usize, + } + } + + fn finish(&self, name: &Ident) -> TokenStream { + let length = &self.length; + let from_usize = &self.from_usize; + let into_usize = &self.into_usize; + + quote! { + #[automatically_derived] + impl ::enum_map::Enum for #name { + const LENGTH: ::enum_map::usize = #length; + + #[inline] + fn from_usize(value: ::enum_map::usize) -> Self { + #from_usize + } + + #[inline] + fn into_usize(self) -> ::enum_map::usize { + #into_usize + } + } + + #[automatically_derived] + impl<V> ::enum_map::EnumArray<V> for #name { + type Array = [V; #length]; + } + } + } +} diff --git a/third_party/rust/enum-map-derive/src/lib.rs b/third_party/rust/enum-map-derive/src/lib.rs new file mode 100644 index 0000000000..0776559b28 --- /dev/null +++ b/third_party/rust/enum-map-derive/src/lib.rs @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: 2017 - 2022 Kamila Borowska <kamila@borowska.pw> +// SPDX-FileCopyrightText: 2018 hcpl <hcpl.prog@gmail.com> +// SPDX-FileCopyrightText: 2019 mara <vmedea@protonmail.com> +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann <brunoczim@gmail.com> +// SPDX-FileCopyrightText: 2021 Dietrich <dietrich@teilgedanken.de> +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Procedural macro implementing `#[derive(Enum)]` +//! +//! This is supposed to used with `enum-map` crate, which provides the +//! actual usage documentation. + +mod derive_enum; +mod derive_struct; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Data, DeriveInput, Type}; + +/// Derive macro generating an implementation of trait `Enum`. +/// +/// When using a derive, enum maps are maintained in the order in which +/// enum variants are declared. This is reflected in the value returned +/// by `Enum::into_usize`, iterators of enum map as well as +/// `EnumMap::as_slice` method. +/// +/// # Examples +/// +/// ## Enums Without Payload +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// assert_eq!(A::B.into_usize(), 0); +/// assert_eq!(A::C.into_usize(), 1); +/// assert_eq!(A::D.into_usize(), 2); +/// +/// assert_eq!(A::from_usize(0), A::B); +/// assert_eq!(A::from_usize(1), A::C); +/// assert_eq!(A::from_usize(2), A::D); +/// ``` +/// +/// ## Enums With Payload +/// +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum X { +/// Y, +/// Z, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum Foo { +/// Bar(bool, A), +/// Empty, +/// Baz { fa: A, fx: X }, +/// } +/// +/// assert_eq!(Foo::Bar(false, A::B).into_usize(), 0); +/// assert_eq!(Foo::Bar(false, A::D).into_usize(), 4); +/// assert_eq!(Foo::Bar(true, A::B).into_usize(), 1); +/// assert_eq!(Foo::Bar(true, A::C).into_usize(), 3); +/// assert_eq!(Foo::Empty.into_usize(), 6); +/// assert_eq!(Foo::Baz { fa: A::B, fx: X::Y }.into_usize(), 7); +/// assert_eq!(Foo::Baz { fa: A::B, fx: X::Z }.into_usize(), 10); +/// assert_eq!(Foo::Baz { fa: A::D, fx: X::Y }.into_usize(), 9); +/// +/// assert_eq!(Foo::from_usize(0), Foo::Bar(false, A::B)); +/// assert_eq!(Foo::from_usize(4), Foo::Bar(false, A::D)); +/// assert_eq!(Foo::from_usize(1), Foo::Bar(true, A::B)); +/// assert_eq!(Foo::from_usize(3), Foo::Bar(true, A::C)); +/// assert_eq!(Foo::from_usize(6), Foo::Empty); +/// assert_eq!(Foo::from_usize(7), Foo::Baz { fa: A::B, fx: X::Y }); +/// assert_eq!(Foo::from_usize(10), Foo::Baz { fa: A::B, fx: X::Z }); +/// assert_eq!(Foo::from_usize(9), Foo::Baz { fa: A::D, fx: X::Y }); +/// +/// ``` +/// +/// ## Structs +/// +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum X { +/// Y, +/// Z, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// struct Foo { +/// bar: bool, +/// baz: A, +/// end: X, +/// } +/// +/// assert_eq!(Foo { bar: false, baz: A::B, end: X::Y }.into_usize(), 0); +/// assert_eq!(Foo { bar: true, baz: A::B, end: X::Y }.into_usize(), 1); +/// assert_eq!(Foo { bar: false, baz: A::D, end: X::Y }.into_usize(), 4); +/// assert_eq!(Foo { bar: true, baz: A::C, end: X::Z }.into_usize(), 9); +/// +/// assert_eq!(Foo::from_usize(0), Foo { bar: false, baz: A::B, end: X::Y }); +/// assert_eq!(Foo::from_usize(1), Foo { bar: true, baz: A::B, end: X::Y }); +/// assert_eq!(Foo::from_usize(4), Foo { bar: false, baz: A::D, end: X::Y }); +/// assert_eq!(Foo::from_usize(9), Foo { bar: true, baz: A::C, end: X::Z }); +/// ``` +/// +/// ## Tuple Structs +/// +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum X { +/// Y, +/// Z, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// struct Foo(bool, A, X); +/// +/// assert_eq!(Foo(false, A::B, X::Y ).into_usize(), 0); +/// assert_eq!(Foo(true, A::B, X::Y ).into_usize(), 1); +/// assert_eq!(Foo(false, A::D, X::Y ).into_usize(), 4); +/// assert_eq!(Foo(true, A::C, X::Z ).into_usize(), 9); +/// +/// assert_eq!(Foo::from_usize(0), Foo(false, A::B, X::Y)); +/// assert_eq!(Foo::from_usize(1), Foo(true, A::B, X::Y)); +/// assert_eq!(Foo::from_usize(4), Foo(false, A::D, X::Y)); +/// assert_eq!(Foo::from_usize(9), Foo(true, A::C, X::Z)); +#[proc_macro_derive(Enum)] +pub fn derive_enum_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: DeriveInput = syn::parse(input).unwrap(); + + let result = match input.data { + Data::Enum(data_enum) => derive_enum::generate(input.ident, data_enum), + Data::Struct(data_struct) => derive_struct::generate(input.ident, data_struct), + _ => quote! { compile_error! {"#[derive(Enum)] is only defined for enums and structs"} }, + }; + + result.into() +} + +fn type_length(ty: &Type) -> TokenStream { + quote! { + <#ty as ::enum_map::Enum>::LENGTH + } +} |