1+ use std:: collections:: HashMap ;
2+
13use anyhow:: Result ;
24use proc_macro2:: TokenStream ;
35use proc_macro2:: { Ident , Span } ;
@@ -10,7 +12,9 @@ use super::sorted;
1012
1113pub fn render ( _opts : & super :: Options , _ir : & IR , e : & Enum , path : & str ) -> Result < TokenStream > {
1214 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 ) ;
1418
1519 let ty = match e. bit_size {
1620 1 ..=8 => quote ! ( u8 ) ,
@@ -20,30 +24,100 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
2024 _ => panic ! ( "Invalid bit_size {}" , e. bit_size) ,
2125 } ;
2226
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-
3327 let ( _, name) = super :: split_path ( path) ;
3428 let name = Ident :: new ( name, span) ;
3529 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 ( ) ;
3633
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 ( ) ;
4236
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+ ) ) ;
4545 }
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+ } ) ;
47121
48122 Ok ( out)
49123}
0 commit comments