Skip to content

Commit a3d0ead

Browse files
committed
Implement 8 and optionally 16 bit integers with build.rs generated enum
1 parent ebe62f8 commit a3d0ead

File tree

4 files changed

+205
-2
lines changed

4 files changed

+205
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ license = "MIT OR Apache-2.0"
1414
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1515

1616
[features]
17+
enum_repr_16 = []
1718
default = ["std"]
1819
std = []
1920

build.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#[derive(Clone, Copy)]
2+
enum BitInfo {
3+
Unsigned {
4+
prim_max: u64,
5+
},
6+
Signed {
7+
signed_max: i64,
8+
signed_min: i64,
9+
prim_max: i64,
10+
},
11+
}
12+
13+
impl BitInfo {
14+
fn prim_max(&self) -> i64 {
15+
match self {
16+
Self::Unsigned { prim_max } => *prim_max as i64,
17+
Self::Signed { prim_max, .. } => *prim_max,
18+
}
19+
}
20+
}
21+
22+
struct EnumInfo {
23+
ty_name: &'static str,
24+
name: &'static str,
25+
bit_info: BitInfo,
26+
}
27+
28+
const FILES: &[EnumInfo] = &[
29+
EnumInfo {
30+
ty_name: "u8",
31+
name: "u8_repr.rs",
32+
bit_info: BitInfo::Unsigned {
33+
prim_max: u8::MAX as _,
34+
},
35+
},
36+
EnumInfo {
37+
ty_name: "i8",
38+
name: "i8_repr.rs",
39+
bit_info: BitInfo::Signed {
40+
signed_max: i8::MAX as _,
41+
signed_min: i8::MIN as _,
42+
prim_max: u8::MAX as _,
43+
},
44+
},
45+
#[cfg(feature = "enum_repr_16")]
46+
EnumInfo {
47+
ty_name: "u16",
48+
name: "u16_repr.rs",
49+
bit_info: BitInfo::Unsigned {
50+
prim_max: u16::MAX as _,
51+
},
52+
},
53+
#[cfg(feature = "enum_repr_16")]
54+
EnumInfo {
55+
ty_name: "i16",
56+
name: "i16_repr.rs",
57+
bit_info: BitInfo::Signed {
58+
signed_max: i16::MAX as _,
59+
signed_min: i16::MIN as _,
60+
prim_max: u16::MAX as _,
61+
},
62+
},
63+
];
64+
65+
fn generate_variants(
66+
generated_file: &mut impl std::fmt::Write,
67+
repr_name: &str,
68+
bit_info: BitInfo,
69+
) {
70+
write!(
71+
generated_file,
72+
"#[derive(Clone, Copy, PartialEq, Eq, Hash)]
73+
#[allow(dead_code)]
74+
pub(crate) enum {repr_name} {{",
75+
)
76+
.unwrap();
77+
78+
match bit_info {
79+
BitInfo::Unsigned { prim_max } => {
80+
for i in 0..prim_max {
81+
write!(generated_file, "V{i}={i},").unwrap()
82+
}
83+
}
84+
BitInfo::Signed {
85+
signed_max,
86+
signed_min,
87+
prim_max,
88+
} => {
89+
for i in 0..signed_max {
90+
write!(generated_file, "V{i}={i},").unwrap();
91+
}
92+
93+
for (i, v) in (signed_max..prim_max).zip(signed_min..0) {
94+
write!(generated_file, "MV{i}={v},").unwrap();
95+
}
96+
}
97+
}
98+
99+
write!(generated_file, "}}").unwrap();
100+
}
101+
102+
fn generate_impl(generated_file: &mut impl std::fmt::Write, repr_name: &str, ty_name: &str) {
103+
write!(
104+
generated_file,
105+
"impl {repr_name} {{
106+
pub(crate) const fn new(value: {ty_name}) -> Option<Self> {{
107+
unsafe {{ std::mem::transmute(value) }}
108+
}}
109+
}}"
110+
)
111+
.unwrap()
112+
}
113+
114+
fn main() {
115+
let out_dir = std::env::var("OUT_DIR").unwrap();
116+
117+
for file in FILES {
118+
let mut generated_file = String::with_capacity(file.bit_info.prim_max() as usize);
119+
120+
let repr_name = format!("{}Repr", file.ty_name.to_uppercase());
121+
122+
generate_variants(&mut generated_file, &repr_name, file.bit_info);
123+
generate_impl(&mut generated_file, &repr_name, file.ty_name);
124+
125+
std::fs::write(format!("{out_dir}/{}", file.name), generated_file).unwrap();
126+
}
127+
}

src/enum_impl/mod.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#[cfg(feature = "enum_repr_16")]
2+
pub(crate) mod i16_repr {
3+
include!(concat!(env!("OUT_DIR"), "/i16_repr.rs"));
4+
}
5+
#[cfg(feature = "enum_repr_16")]
6+
pub(crate) mod u16_repr {
7+
include!(concat!(env!("OUT_DIR"), "/u16_repr.rs"));
8+
}
9+
pub(crate) mod i8_repr {
10+
include!(concat!(env!("OUT_DIR"), "/i8_repr.rs"));
11+
}
12+
pub(crate) mod u8_repr {
13+
include!(concat!(env!("OUT_DIR"), "/u8_repr.rs"));
14+
}
15+
16+
macro_rules! nonmax {
17+
( $nonmax: ident, $primitive: ident, $byte_repr: ident ) => {
18+
/// An integer that is known not to equal its maximum value.
19+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
20+
#[repr(transparent)]
21+
pub struct $nonmax($byte_repr);
22+
23+
impl $nonmax {
24+
/// Creates a new non-max if the given value is not the maximum
25+
/// value.
26+
pub const fn new(value: $primitive) -> Option<Self> {
27+
match $byte_repr::new(value) {
28+
Some(byte) => Some(Self(byte)),
29+
None => None,
30+
}
31+
}
32+
33+
/// Creates a new non-max without checking the value.
34+
///
35+
/// # Safety
36+
///
37+
/// The value must not equal the maximum representable value for the
38+
/// primitive type.
39+
#[inline]
40+
pub const unsafe fn new_unchecked(value: $primitive) -> Self {
41+
match Self::new(value) {
42+
Some(this) => this,
43+
None => unsafe { std::hint::unreachable_unchecked() },
44+
}
45+
}
46+
47+
/// Returns the value as a primitive type.
48+
#[inline]
49+
pub const fn get(&self) -> $primitive {
50+
self.0 as $primitive
51+
}
52+
}
53+
};
54+
}
55+
56+
pub(crate) use nonmax;

src/lib.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ will only require minor version bumps, but will need significant justification.
5656
#![forbid(missing_docs)]
5757
#![cfg_attr(not(feature = "std"), no_std)]
5858

59+
#[macro_use]
60+
mod enum_impl;
61+
62+
#[cfg(feature = "enum_repr_16")]
63+
use enum_impl::i16_repr::I16Repr;
64+
use enum_impl::i8_repr::I8Repr;
65+
use enum_impl::u8_repr::U8Repr;
66+
5967
/// An error type returned when a checked integral type conversion fails (mimics [std::num::TryFromIntError])
6068
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6169
pub struct TryFromIntError(());
@@ -401,14 +409,25 @@ macro_rules! nonmax_impls {
401409
};
402410
}
403411

404-
nonmax_impls!(def, signed, NonMaxI8, NonZeroI8, i8);
412+
enum_impl::nonmax!(NonMaxI8, i8, I8Repr);
413+
enum_impl::nonmax!(NonMaxU8, u8, U8Repr);
414+
#[cfg(feature = "enum_repr_16")]
415+
enum_impl::nonmax!(NonMaxI16, i16, I16Repr);
416+
417+
nonmax_impls!(signed, NonMaxI8, NonZeroI8, i8);
418+
#[cfg(feature = "enum_repr_16")]
419+
nonmax_impls!(signed, NonMaxI16, NonZeroI16, i16);
420+
#[cfg(not(feature = "enum_repr_16"))]
405421
nonmax_impls!(def, signed, NonMaxI16, NonZeroI16, i16);
406422
nonmax_impls!(def, signed, NonMaxI32, NonZeroI32, i32);
407423
nonmax_impls!(def, signed, NonMaxI64, NonZeroI64, i64);
408424
nonmax_impls!(def, signed, NonMaxI128, NonZeroI128, i128);
409425
nonmax_impls!(def, signed, NonMaxIsize, NonZeroIsize, isize);
410426

411-
nonmax_impls!(def, unsigned, NonMaxU8, NonZeroU8, u8);
427+
nonmax_impls!(unsigned, NonMaxU8, NonZeroU8, u8);
428+
#[cfg(feature = "enum_repr_16")]
429+
nonmax_impls!(unsigned, NonMaxU16, NonZeroU16, u16);
430+
#[cfg(not(feature = "enum_repr_16"))]
412431
nonmax_impls!(def, unsigned, NonMaxU16, NonZeroU16, u16);
413432
nonmax_impls!(def, unsigned, NonMaxU32, NonZeroU32, u32);
414433
nonmax_impls!(def, unsigned, NonMaxU64, NonZeroU64, u64);

0 commit comments

Comments
 (0)