1
+ use std:: collections:: HashMap ;
2
+
1
3
use anyhow:: Result ;
2
4
use proc_macro2:: TokenStream ;
3
5
use proc_macro2:: { Ident , Span } ;
@@ -10,7 +12,9 @@ use super::sorted;
10
12
11
13
pub fn render ( _opts : & super :: Options , _ir : & IR , e : & Enum , path : & str ) -> Result < TokenStream > {
12
14
let span = Span :: call_site ( ) ;
13
- let mut items = TokenStream :: new ( ) ;
15
+
16
+ // For very "sparse" enums, generate a newtype wrapping the uX.
17
+ let newtype = e. bit_size > 8 || ( e. variants . len ( ) < 6 && e. bit_size > 4 ) ;
14
18
15
19
let ty = match e. bit_size {
16
20
1 ..=8 => quote ! ( u8 ) ,
@@ -20,30 +24,100 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
20
24
_ => panic ! ( "Invalid bit_size {}" , e. bit_size) ,
21
25
} ;
22
26
23
- for f in sorted ( & e. variants , |f| ( f. value , f. name . clone ( ) ) ) {
24
- let name = Ident :: new ( & f. name , span) ;
25
- let value = util:: hex ( f. value ) ;
26
- let doc = util:: doc ( & f. description ) ;
27
- items. extend ( quote ! (
28
- #doc
29
- pub const #name: Self = Self ( #value) ;
30
- ) ) ;
31
- }
32
-
33
27
let ( _, name) = super :: split_path ( path) ;
34
28
let name = Ident :: new ( name, span) ;
35
29
let doc = util:: doc ( & e. description ) ;
30
+ let mask = util:: hex ( 1u64 . wrapping_shl ( e. bit_size ) . wrapping_sub ( 1 ) ) ;
31
+
32
+ let mut out = TokenStream :: new ( ) ;
36
33
37
- let out = quote ! {
38
- #doc
39
- #[ repr( transparent) ]
40
- #[ derive( Copy , Clone , Eq , PartialEq , Ord , PartialOrd ) ]
41
- pub struct #name ( pub #ty) ;
34
+ if newtype {
35
+ let mut items = TokenStream :: new ( ) ;
42
36
43
- impl #name {
44
- #items
37
+ for f in sorted ( & e. variants , |f| ( f. value , f. name . clone ( ) ) ) {
38
+ let name = Ident :: new ( & f. name , span) ;
39
+ let value = util:: hex ( f. value ) ;
40
+ let doc = util:: doc ( & f. description ) ;
41
+ items. extend ( quote ! (
42
+ #doc
43
+ pub const #name: Self = Self ( #value) ;
44
+ ) ) ;
45
45
}
46
- } ;
46
+
47
+ out. extend ( quote ! {
48
+ #doc
49
+ #[ repr( transparent) ]
50
+ #[ derive( Copy , Clone , Eq , PartialEq , Ord , PartialOrd ) ]
51
+ pub struct #name ( pub #ty) ;
52
+
53
+ impl #name {
54
+ #items
55
+ }
56
+
57
+ impl #name {
58
+ pub const fn from_bits( val: #ty) -> #name {
59
+ Self ( val & #mask)
60
+ }
61
+
62
+ pub const fn to_bits( self ) -> #ty {
63
+ self . 0
64
+ }
65
+ }
66
+ } ) ;
67
+ } else {
68
+ let variants: HashMap < _ , _ > = e. variants . iter ( ) . map ( |v| ( v. value , v) ) . collect ( ) ;
69
+ let mut items = TokenStream :: new ( ) ;
70
+ for val in 0 ..( 1 << e. bit_size ) {
71
+ if let Some ( f) = variants. get ( & val) {
72
+ let name = Ident :: new ( & f. name , span) ;
73
+ let value = util:: hex ( f. value ) ;
74
+ let doc = util:: doc ( & f. description ) ;
75
+ items. extend ( quote ! (
76
+ #doc
77
+ #name = #value,
78
+ ) ) ;
79
+ } else {
80
+ let name = Ident :: new ( & format ! ( "_RESERVED_{:x}" , val) , span) ;
81
+ let value = util:: hex ( val) ;
82
+ items. extend ( quote ! (
83
+ #name = #value,
84
+ ) ) ;
85
+ }
86
+ }
87
+
88
+ out. extend ( quote ! {
89
+ #doc
90
+ #[ repr( #ty) ]
91
+ #[ derive( Copy , Clone , Eq , PartialEq , Ord , PartialOrd ) ]
92
+ pub enum #name {
93
+ #items
94
+ }
95
+
96
+ impl #name {
97
+ pub const fn from_bits( val: #ty) -> #name {
98
+ unsafe { core:: mem:: transmute( val & #mask) }
99
+ }
100
+
101
+ pub const fn to_bits( self ) -> #ty {
102
+ unsafe { core:: mem:: transmute( self ) }
103
+ }
104
+ }
105
+ } ) ;
106
+ }
107
+
108
+ out. extend ( quote ! {
109
+ impl From <#ty> for #name {
110
+ fn from( val: #ty) -> #name {
111
+ #name:: from_bits( val)
112
+ }
113
+ }
114
+
115
+ impl From <#name> for #ty {
116
+ fn from( val: #name) -> #ty {
117
+ #name:: to_bits( val)
118
+ }
119
+ }
120
+ } ) ;
47
121
48
122
Ok ( out)
49
123
}
0 commit comments