Skip to content

Commit 0973420

Browse files
committed
✨ Default for padding
1 parent 39fc7ba commit 0973420

File tree

3 files changed

+129
-66
lines changed

3 files changed

+129
-66
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ struct MyBitfield {
108108
pub public: usize,
109109
/// padding
110110
#[bits(5)]
111-
_p: u8,
111+
__: u8,
112112
}
113113

114114
/// A custom enum

src/lib.rs

+65-64
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
//! pub public: usize,
9999
//! /// padding
100100
//! #[bits(5)]
101-
//! _p: u8,
101+
//! __: u8,
102102
//! }
103103
//!
104104
//! /// A custom enum
@@ -292,10 +292,11 @@ fn bitfield_inner(args: TokenStream, input: TokenStream) -> syn::Result<TokenStr
292292
#vis struct #name(#ty);
293293

294294
impl #name {
295-
/// Creates a new zero initialized bitfield.
295+
/// Creates a new default initialized bitfield.
296296
#vis const fn new() -> Self {
297-
Self(0)
297+
let mut this = Self(0);
298298
#( #defaults )*
299+
this
299300
}
300301

301302
#( #members )*
@@ -321,6 +322,7 @@ struct Member {
321322
offset: usize,
322323
bits: usize,
323324
base_ty: syn::Type,
325+
default: TokenStream,
324326
inner: Option<MemberInner>,
325327
}
326328

@@ -329,7 +331,6 @@ struct MemberInner {
329331
ty: syn::Type,
330332
attrs: Vec<syn::Attribute>,
331333
vis: syn::Visibility,
332-
default: TokenStream,
333334
into: TokenStream,
334335
from: TokenStream,
335336
}
@@ -352,42 +353,50 @@ impl Member {
352353
let Field {
353354
bits,
354355
ty,
355-
class: _,
356-
default,
356+
mut default,
357357
into,
358358
from,
359359
} = parse_field(&attrs, &ty, ignore)?;
360360

361361
if bits > 0 && !ignore {
362-
if default.is_empty() || into.is_empty() || from.is_empty() {
362+
if into.is_empty() || from.is_empty() {
363363
return Err(syn::Error::new(
364364
ty.span(),
365365
"Custom types require 'into', and 'from' in the #[bits] attribute",
366366
));
367367
}
368368

369+
if default.is_empty() {
370+
default = quote!(#ty::from_bits(0));
371+
}
372+
369373
// remove our attribute
370374
attrs.retain(|a| !a.path().is_ident("bits"));
371375

372376
Ok(Self {
373377
offset,
374378
bits,
375379
base_ty,
380+
default,
376381
inner: Some(MemberInner {
377382
ident,
378383
ty,
379384
attrs,
380385
vis,
381-
default,
382386
into,
383387
from,
384388
}),
385389
})
386390
} else {
391+
if default.is_empty() {
392+
default = quote!(0);
393+
}
394+
387395
Ok(Self {
388396
offset,
389397
bits,
390398
base_ty,
399+
default,
391400
inner: None,
392401
})
393402
}
@@ -399,18 +408,20 @@ impl Member {
399408
let ident = &inner.ident;
400409
quote!(.field(#ident_str, &self.#ident()))
401410
} else {
402-
Default::default()
411+
quote!()
403412
}
404413
}
405414

406415
fn default(&self) -> TokenStream {
416+
let default = &self.default;
407417
if let Some(inner) = &self.inner {
408418
let ident = &inner.ident;
409419
let with_ident = format_ident!("with_{ident}");
410-
let default = &inner.default;
411-
quote!(.#with_ident(#default))
420+
quote!(this = this.#with_ident(#default);)
412421
} else {
413-
Default::default()
422+
let offset = self.offset;
423+
let base_ty = &self.base_ty;
424+
quote!(this.0 |= (#default as #base_ty) << #offset;)
414425
}
415426
}
416427
}
@@ -421,7 +432,8 @@ impl ToTokens for Member {
421432
offset,
422433
bits,
423434
base_ty,
424-
inner: Some(MemberInner { ident, ty, attrs, vis, default: _, into, from }),
435+
default: _,
436+
inner: Some(MemberInner { ident, ty, attrs, vis, into, from }),
425437
} = self else {
426438
return Default::default();
427439
};
@@ -441,23 +453,12 @@ impl ToTokens for Member {
441453
.map(ToTokens::to_token_stream)
442454
.collect();
443455

444-
let general = quote! {
445-
const #bits_ident: usize = #bits;
446-
const #offset_ident: usize = #offset;
447-
448-
#doc
449-
#[doc = #location]
450-
#vis fn #set_ident(&mut self, value: #ty) {
451-
*self = self.#with_ident(value);
452-
}
453-
};
454-
455-
let bits = *bits as u32;
456-
let mask: u128 = !0 >> (u128::BITS - bits);
456+
let mask: u128 = !0 >> (u128::BITS - *bits as u32);
457457
let mask = syn::LitInt::new(&format!("0x{mask:x}"), Span::mixed_site());
458458

459459
let code = quote! {
460-
#general
460+
const #bits_ident: usize = #bits;
461+
const #offset_ident: usize = #offset;
461462

462463
#doc
463464
#[doc = #location]
@@ -475,6 +476,12 @@ impl ToTokens for Member {
475476
let this = (self.0 >> #offset) & #mask;
476477
#from
477478
}
479+
#doc
480+
#[doc = #location]
481+
#vis fn #set_ident(&mut self, value: #ty) {
482+
*self = self.#with_ident(value);
483+
}
484+
478485
};
479486
tokens.extend(code);
480487
}
@@ -497,7 +504,6 @@ enum TypeClass {
497504
struct Field {
498505
bits: usize,
499506
ty: syn::Type,
500-
class: TypeClass,
501507

502508
default: TokenStream,
503509
into: TokenStream,
@@ -517,32 +523,28 @@ fn parse_field(attrs: &[syn::Attribute], ty: &syn::Type, ignore: bool) -> syn::R
517523
TypeClass::Bool => Field {
518524
bits: ty_bits,
519525
ty: ty.clone(),
520-
class,
521526
default: quote!(false),
522527
into: quote!(this as _),
523528
from: quote!(this != 0),
524529
},
525530
TypeClass::SInt => Field {
526531
bits: ty_bits,
527532
ty: ty.clone(),
528-
class,
529533
default: quote!(0),
530534
into: TokenStream::new(),
531535
from: TokenStream::new(),
532536
},
533537
TypeClass::UInt => Field {
534538
bits: ty_bits,
535539
ty: ty.clone(),
536-
class,
537540
default: quote!(0),
538541
into: quote!(this as _),
539542
from: quote!(this as _),
540543
},
541544
TypeClass::Other => Field {
542545
bits: ty_bits,
543546
ty: ty.clone(),
544-
class,
545-
default: quote!(#ty::from_bits(0)),
547+
default: TokenStream::new(),
546548
into: quote!(#ty::into_bits(this)),
547549
from: quote!(#ty::from_bits(this)),
548550
},
@@ -572,10 +574,10 @@ fn parse_field(attrs: &[syn::Attribute], ty: &syn::Type, ignore: bool) -> syn::R
572574
}
573575
ret.bits = bits;
574576
}
575-
if ignore && (default.is_some() || into.is_some() || from.is_some()) {
577+
if ignore && (into.is_some() || from.is_some()) {
576578
return Err(syn::Error::new(
577579
default.span(),
578-
"'default', 'into', and 'from' are not (yet) supported on padding",
580+
"'into' and 'from' are not supported on padding",
579581
));
580582
}
581583

@@ -606,9 +608,9 @@ fn parse_field(attrs: &[syn::Attribute], ty: &syn::Type, ignore: bool) -> syn::R
606608
}
607609

608610
// Negative integers need some special handling...
609-
if !ignore && ret.class == TypeClass::SInt {
611+
if !ignore && class == TypeClass::SInt {
610612
let bits = ret.bits as u32;
611-
let mask: u128 = !0 >> (u128::BITS - bits);
613+
let mask: u128 = !0 >> (u128::BITS - ret.bits as u32);
612614
let mask = syn::LitInt::new(&format!("0x{mask:x}"), Span::mixed_site());
613615
if ret.into.is_empty() {
614616
// Bounds check and remove leading ones from negative values
@@ -659,7 +661,6 @@ impl Parse for BitsAttr {
659661

660662
<Token![=]>::parse(input)?;
661663

662-
663664
if ident == "default" {
664665
attr.default = Some(input.parse()?);
665666
} else if ident == "into" {
@@ -679,32 +680,6 @@ impl Parse for BitsAttr {
679680
}
680681
}
681682

682-
/// Returns the number of bits for a given type
683-
fn type_bits(ty: &syn::Type) -> (TypeClass, usize) {
684-
let syn::Type::Path(syn::TypePath{ path, .. }) = ty else {
685-
return (TypeClass::Other, 0);
686-
};
687-
let Some(ident) = path.get_ident() else {
688-
return (TypeClass::Other, 0);
689-
};
690-
if ident == "bool" {
691-
return (TypeClass::Bool, 1);
692-
}
693-
if ident == "isize" || ident == "usize" {
694-
return (TypeClass::UInt, 0); // they have architecture dependend sizes
695-
}
696-
macro_rules! integer {
697-
($ident:ident => $($uint:ident),* ; $($sint:ident),*) => {
698-
match ident {
699-
$(_ if ident == stringify!($uint) => (TypeClass::UInt, $uint::BITS as _),)*
700-
$(_ if ident == stringify!($sint) => (TypeClass::SInt, $sint::BITS as _),)*
701-
_ => (TypeClass::Other, 0)
702-
}
703-
};
704-
}
705-
integer!(ident => u8, u16, u32, u64, u128 ; i8, i16, i32, i64, i128)
706-
}
707-
708683
/// The bitfield macro parameters
709684
struct Params {
710685
ty: syn::Type,
@@ -740,6 +715,32 @@ impl Parse for Params {
740715
}
741716
}
742717

718+
/// Returns the number of bits for a given type
719+
fn type_bits(ty: &syn::Type) -> (TypeClass, usize) {
720+
let syn::Type::Path(syn::TypePath{ path, .. }) = ty else {
721+
return (TypeClass::Other, 0);
722+
};
723+
let Some(ident) = path.get_ident() else {
724+
return (TypeClass::Other, 0);
725+
};
726+
if ident == "bool" {
727+
return (TypeClass::Bool, 1);
728+
}
729+
if ident == "isize" || ident == "usize" {
730+
return (TypeClass::UInt, 0); // they have architecture dependend sizes
731+
}
732+
macro_rules! integer {
733+
($ident:ident => $($uint:ident),* ; $($sint:ident),*) => {
734+
match ident {
735+
$(_ if ident == stringify!($uint) => (TypeClass::UInt, $uint::BITS as _),)*
736+
$(_ if ident == stringify!($sint) => (TypeClass::SInt, $sint::BITS as _),)*
737+
_ => (TypeClass::Other, 0)
738+
}
739+
};
740+
}
741+
integer!(ident => u8, u16, u32, u64, u128 ; i8, i16, i32, i64, i128)
742+
}
743+
743744
#[cfg(test)]
744745
mod test {
745746
use quote::quote;

tests/test.rs

+63-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn members() {
2929
pub public: usize,
3030
/// padding
3131
#[bits(5)]
32-
_p: (),
32+
__: (),
3333
}
3434

3535
/// A custom enum
@@ -176,3 +176,65 @@ fn entirely_negative() {
176176
let v = MyBitfield::new().with_negative(i32::MAX);
177177
assert_eq!(v.negative(), i32::MAX);
178178
}
179+
180+
#[test]
181+
fn defaults() {
182+
#[bitfield(u16)]
183+
#[derive(PartialEq, Eq)]
184+
struct MyBitfield {
185+
/// Interpreted as 1-bit flag, with custom default
186+
#[bits(default = true)]
187+
flag: bool,
188+
/// Supports any type, with default/to/from expressions (that are const eval)
189+
/// - into/from call #ty::into_bits/#ty::from_bits if nothing else is specified
190+
#[bits(13, default = CustomEnum::B, from = CustomEnum::my_from_bits)]
191+
custom: CustomEnum,
192+
// Padding with default
193+
#[bits(2, default = 0b10)]
194+
__: (),
195+
}
196+
197+
/// A custom enum
198+
#[derive(Debug, PartialEq, Eq)]
199+
#[repr(u16)]
200+
enum CustomEnum {
201+
A = 0,
202+
B = 1,
203+
C = 2,
204+
}
205+
impl CustomEnum {
206+
// This has to be const eval
207+
const fn into_bits(self) -> u16 {
208+
self as _
209+
}
210+
const fn my_from_bits(value: u16) -> Self {
211+
match value {
212+
0 => Self::A,
213+
1 => Self::B,
214+
_ => Self::C,
215+
}
216+
}
217+
}
218+
219+
// Uses defaults
220+
let val = MyBitfield::new();
221+
222+
assert_eq!(val.flag(), true);
223+
assert_eq!(val.custom(), CustomEnum::B);
224+
assert_eq!(val.0 >> 14, 0b10); // padding
225+
}
226+
227+
#[test]
228+
fn default_padding() {
229+
#[bitfield(u32)]
230+
struct MyBitfield {
231+
value: u16,
232+
#[bits(15, default = 0xfff)]
233+
__: (),
234+
#[bits(default = true)]
235+
__: bool,
236+
}
237+
let v = MyBitfield::new().with_value(0xff);
238+
239+
assert_eq!(v.0, 0x8fff_00ff);
240+
}

0 commit comments

Comments
 (0)