@@ -6,89 +6,66 @@ extern crate syn;
6
6
use proc_macro:: TokenStream ;
7
7
use proc_macro2:: TokenStream as TokenStream2 ;
8
8
use quote:: quote;
9
- use std:: { collections :: HashMap , ops :: Range , str:: FromStr } ;
9
+ use std:: str:: FromStr ;
10
10
use syn:: { parse_macro_input, Data , DeriveInput , Ident } ;
11
11
12
12
struct PacNumberEnum {
13
13
name : Ident ,
14
- valid_ranges : Vec < Range < usize > > ,
14
+ numbers : Vec < ( Ident , usize ) > ,
15
15
}
16
16
17
17
impl PacNumberEnum {
18
18
fn new ( input : & DeriveInput ) -> Self {
19
+ let name = input. ident . clone ( ) ;
20
+
19
21
let variants = match & input. data {
20
22
Data :: Enum ( data) => & data. variants ,
21
23
_ => panic ! ( "Input is not an enum" ) ,
22
24
} ;
23
-
24
- // Collect the variants and their associated number discriminants
25
- let mut var_map = HashMap :: new ( ) ;
26
- let mut numbers = Vec :: new ( ) ;
27
- for variant in variants {
28
- let ident = & variant. ident ;
29
- let value = match & variant. discriminant {
30
- Some ( d) => match & d. 1 {
31
- syn:: Expr :: Lit ( expr_lit) => match & expr_lit. lit {
32
- syn:: Lit :: Int ( lit_int) => match lit_int. base10_parse :: < usize > ( ) {
33
- Ok ( num) => num,
34
- Err ( _) => panic ! ( "All variant discriminants must be unsigned integers" ) ,
25
+ let numbers = variants
26
+ . iter ( )
27
+ . map ( |variant| {
28
+ let ident = & variant. ident ;
29
+ let value = match & variant. discriminant {
30
+ Some ( d) => match & d. 1 {
31
+ syn:: Expr :: Lit ( expr_lit) => match & expr_lit. lit {
32
+ syn:: Lit :: Int ( lit_int) => match lit_int. base10_parse :: < usize > ( ) {
33
+ Ok ( num) => num,
34
+ Err ( _) => {
35
+ panic ! ( "All variant discriminants must be unsigned integers" )
36
+ }
37
+ } ,
38
+ _ => panic ! ( "All variant discriminants must be unsigned integers" ) ,
35
39
} ,
36
40
_ => panic ! ( "All variant discriminants must be unsigned integers" ) ,
37
41
} ,
38
- _ => panic ! ( "All variant discriminants must be unsigned integers" ) ,
39
- } ,
40
- _ => panic ! ( "Variant must have a discriminant" ) ,
41
- } ;
42
- var_map. insert ( value, ident) ;
43
- numbers. push ( value) ;
44
- }
42
+ _ => panic ! ( "Variant must have a discriminant" ) ,
43
+ } ;
44
+ ( ident. clone ( ) , value)
45
+ } )
46
+ . collect ( ) ;
45
47
46
- // sort the number discriminants and generate a list of valid ranges
47
- numbers. sort_unstable ( ) ;
48
- let mut valid_ranges = Vec :: new ( ) ;
49
- let mut start = numbers[ 0 ] ;
50
- let mut end = start;
51
- for & number in & numbers[ 1 ..] {
52
- if number == end + 1 {
53
- end = number;
54
- } else {
55
- valid_ranges. push ( start..end + 1 ) ;
56
- start = number;
57
- end = start;
58
- }
59
- }
60
- valid_ranges. push ( start..end + 1 ) ;
61
-
62
- Self {
63
- name : input. ident . clone ( ) ,
64
- valid_ranges,
65
- }
66
- }
67
-
68
- fn valid_condition ( & self ) -> TokenStream2 {
69
- let mut arms = Vec :: new ( ) ;
70
- for range in & self . valid_ranges {
71
- let ( start, end) = ( range. start , range. end ) ;
72
- if end - start == 1 {
73
- arms. push ( TokenStream2 :: from_str ( & format ! ( "number == {start}" ) ) . unwrap ( ) ) ;
74
- } else {
75
- arms. push (
76
- TokenStream2 :: from_str ( & format ! ( "({start}..{end}).contains(&number)" ) ) . unwrap ( ) ,
77
- ) ;
78
- }
79
- }
80
- quote ! { #( #arms) || * }
48
+ Self { name, numbers }
81
49
}
82
50
83
51
fn max_discriminant ( & self ) -> TokenStream2 {
84
- let max_discriminant = self . valid_ranges . last ( ) . expect ( "invalid range" ) . end - 1 ;
52
+ let max_discriminant = self . numbers . iter ( ) . map ( | ( _ , num ) | num ) . max ( ) . unwrap ( ) ;
85
53
TokenStream2 :: from_str ( & format ! ( "{max_discriminant}" ) ) . unwrap ( )
86
54
}
87
55
56
+ fn valid_matches ( & self ) -> Vec < TokenStream2 > {
57
+ self . numbers
58
+ . iter ( )
59
+ . map ( |( ident, num) | {
60
+ TokenStream2 :: from_str ( & format ! ( "{num} => Ok(Self::{ident})" ) ) . unwrap ( )
61
+ } )
62
+ . collect ( )
63
+ }
64
+
88
65
fn quote ( & self , trait_name : & str , num_type : & str , const_name : & str ) -> TokenStream2 {
89
66
let name = & self . name ;
90
67
let max_discriminant = self . max_discriminant ( ) ;
91
- let valid_condition = self . valid_condition ( ) ;
68
+ let valid_matches = self . valid_matches ( ) ;
92
69
93
70
let trait_name = TokenStream2 :: from_str ( trait_name) . unwrap ( ) ;
94
71
let num_type = TokenStream2 :: from_str ( num_type) . unwrap ( ) ;
@@ -105,11 +82,9 @@ impl PacNumberEnum {
105
82
106
83
#[ inline]
107
84
fn from_number( number: #num_type) -> Result <Self , #num_type> {
108
- if #valid_condition {
109
- // SAFETY: The number is valid for this enum
110
- Ok ( unsafe { core:: mem:: transmute:: <#num_type, Self >( number) } )
111
- } else {
112
- Err ( number)
85
+ match number {
86
+ #( #valid_matches, ) *
87
+ _ => Err ( number) ,
113
88
}
114
89
}
115
90
}
@@ -125,20 +100,6 @@ impl PacNumberEnum {
125
100
/// The trait name must be one of `ExceptionNumber`, `InterruptNumber`, `PriorityNumber`, or `HartIdNumber`.
126
101
/// Marker traits `CoreInterruptNumber` and `ExternalInterruptNumber` cannot be implemented using this macro.
127
102
///
128
- /// # Note
129
- ///
130
- /// To implement number-to-enum operation, the macro works with ranges of valid discriminant numbers.
131
- /// If the number is within any of the valid ranges, the number is transmuted to the enum variant.
132
- /// In this way, the macro achieves better performance for enums with a large number of consecutive variants.
133
- /// Thus, the enum must comply with the following requirements:
134
- ///
135
- /// - All the enum variants must have a valid discriminant number (i.e., a number that is within the valid range of the enum).
136
- /// - For the `ExceptionNumber`, `InterruptNumber`, and `HartIdNumber` traits, the enum must be annotated as `#[repr(u16)]`
137
- /// - For the `PriorityNumber` trait, the enum must be annotated as `#[repr(u8)]`
138
- ///
139
- /// If the enum does not meet these requirements, you will have to implement the traits manually (e.g., `riscv::mcause::Interrupt`).
140
- /// For enums with a small number of consecutive variants, it might be better to implement the traits manually.
141
- ///
142
103
/// # Safety
143
104
///
144
105
/// The struct to be implemented must comply with the requirements of the specified trait.
0 commit comments