Skip to content

Commit

Permalink
Implement 8 and optionally 16 bit integers with build.rs generated enum
Browse files Browse the repository at this point in the history
  • Loading branch information
GnomedDev committed Jan 5, 2024
1 parent ebe62f8 commit a3d0ead
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
enum_repr_16 = []
default = ["std"]
std = []

Expand Down
127 changes: 127 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#[derive(Clone, Copy)]
enum BitInfo {
Unsigned {
prim_max: u64,
},
Signed {
signed_max: i64,
signed_min: i64,
prim_max: i64,
},
}

impl BitInfo {
fn prim_max(&self) -> i64 {
match self {
Self::Unsigned { prim_max } => *prim_max as i64,
Self::Signed { prim_max, .. } => *prim_max,
}
}
}

struct EnumInfo {
ty_name: &'static str,
name: &'static str,
bit_info: BitInfo,
}

const FILES: &[EnumInfo] = &[
EnumInfo {
ty_name: "u8",
name: "u8_repr.rs",
bit_info: BitInfo::Unsigned {
prim_max: u8::MAX as _,
},
},
EnumInfo {
ty_name: "i8",
name: "i8_repr.rs",
bit_info: BitInfo::Signed {
signed_max: i8::MAX as _,
signed_min: i8::MIN as _,
prim_max: u8::MAX as _,
},
},
#[cfg(feature = "enum_repr_16")]
EnumInfo {
ty_name: "u16",
name: "u16_repr.rs",
bit_info: BitInfo::Unsigned {
prim_max: u16::MAX as _,
},
},
#[cfg(feature = "enum_repr_16")]
EnumInfo {
ty_name: "i16",
name: "i16_repr.rs",
bit_info: BitInfo::Signed {
signed_max: i16::MAX as _,
signed_min: i16::MIN as _,
prim_max: u16::MAX as _,
},
},
];

fn generate_variants(
generated_file: &mut impl std::fmt::Write,
repr_name: &str,
bit_info: BitInfo,
) {
write!(
generated_file,
"#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[allow(dead_code)]
pub(crate) enum {repr_name} {{",
)
.unwrap();

match bit_info {
BitInfo::Unsigned { prim_max } => {
for i in 0..prim_max {
write!(generated_file, "V{i}={i},").unwrap()
}
}
BitInfo::Signed {
signed_max,
signed_min,
prim_max,
} => {
for i in 0..signed_max {
write!(generated_file, "V{i}={i},").unwrap();
}

for (i, v) in (signed_max..prim_max).zip(signed_min..0) {
write!(generated_file, "MV{i}={v},").unwrap();
}
}
}

write!(generated_file, "}}").unwrap();
}

fn generate_impl(generated_file: &mut impl std::fmt::Write, repr_name: &str, ty_name: &str) {
write!(
generated_file,
"impl {repr_name} {{
pub(crate) const fn new(value: {ty_name}) -> Option<Self> {{
unsafe {{ std::mem::transmute(value) }}
}}
}}"
)
.unwrap()
}

fn main() {
let out_dir = std::env::var("OUT_DIR").unwrap();

for file in FILES {
let mut generated_file = String::with_capacity(file.bit_info.prim_max() as usize);

let repr_name = format!("{}Repr", file.ty_name.to_uppercase());

generate_variants(&mut generated_file, &repr_name, file.bit_info);
generate_impl(&mut generated_file, &repr_name, file.ty_name);

std::fs::write(format!("{out_dir}/{}", file.name), generated_file).unwrap();
}
}
56 changes: 56 additions & 0 deletions src/enum_impl/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#[cfg(feature = "enum_repr_16")]
pub(crate) mod i16_repr {
include!(concat!(env!("OUT_DIR"), "/i16_repr.rs"));
}
#[cfg(feature = "enum_repr_16")]
pub(crate) mod u16_repr {
include!(concat!(env!("OUT_DIR"), "/u16_repr.rs"));
}
pub(crate) mod i8_repr {
include!(concat!(env!("OUT_DIR"), "/i8_repr.rs"));
}
pub(crate) mod u8_repr {
include!(concat!(env!("OUT_DIR"), "/u8_repr.rs"));
}

macro_rules! nonmax {
( $nonmax: ident, $primitive: ident, $byte_repr: ident ) => {
/// An integer that is known not to equal its maximum value.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct $nonmax($byte_repr);

impl $nonmax {
/// Creates a new non-max if the given value is not the maximum
/// value.
pub const fn new(value: $primitive) -> Option<Self> {
match $byte_repr::new(value) {
Some(byte) => Some(Self(byte)),
None => None,
}
}

/// Creates a new non-max without checking the value.
///
/// # Safety
///
/// The value must not equal the maximum representable value for the
/// primitive type.
#[inline]
pub const unsafe fn new_unchecked(value: $primitive) -> Self {
match Self::new(value) {
Some(this) => this,
None => unsafe { std::hint::unreachable_unchecked() },
}
}

/// Returns the value as a primitive type.
#[inline]
pub const fn get(&self) -> $primitive {
self.0 as $primitive
}
}
};
}

pub(crate) use nonmax;
23 changes: 21 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ will only require minor version bumps, but will need significant justification.
#![forbid(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]

#[macro_use]
mod enum_impl;

#[cfg(feature = "enum_repr_16")]
use enum_impl::i16_repr::I16Repr;
use enum_impl::i8_repr::I8Repr;
use enum_impl::u8_repr::U8Repr;

/// An error type returned when a checked integral type conversion fails (mimics [std::num::TryFromIntError])
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TryFromIntError(());
Expand Down Expand Up @@ -401,14 +409,25 @@ macro_rules! nonmax_impls {
};
}

nonmax_impls!(def, signed, NonMaxI8, NonZeroI8, i8);
enum_impl::nonmax!(NonMaxI8, i8, I8Repr);
enum_impl::nonmax!(NonMaxU8, u8, U8Repr);
#[cfg(feature = "enum_repr_16")]
enum_impl::nonmax!(NonMaxI16, i16, I16Repr);

nonmax_impls!(signed, NonMaxI8, NonZeroI8, i8);
#[cfg(feature = "enum_repr_16")]
nonmax_impls!(signed, NonMaxI16, NonZeroI16, i16);
#[cfg(not(feature = "enum_repr_16"))]
nonmax_impls!(def, signed, NonMaxI16, NonZeroI16, i16);
nonmax_impls!(def, signed, NonMaxI32, NonZeroI32, i32);
nonmax_impls!(def, signed, NonMaxI64, NonZeroI64, i64);
nonmax_impls!(def, signed, NonMaxI128, NonZeroI128, i128);
nonmax_impls!(def, signed, NonMaxIsize, NonZeroIsize, isize);

nonmax_impls!(def, unsigned, NonMaxU8, NonZeroU8, u8);
nonmax_impls!(unsigned, NonMaxU8, NonZeroU8, u8);
#[cfg(feature = "enum_repr_16")]
nonmax_impls!(unsigned, NonMaxU16, NonZeroU16, u16);
#[cfg(not(feature = "enum_repr_16"))]
nonmax_impls!(def, unsigned, NonMaxU16, NonZeroU16, u16);
nonmax_impls!(def, unsigned, NonMaxU32, NonZeroU32, u32);
nonmax_impls!(def, unsigned, NonMaxU64, NonZeroU64, u64);
Expand Down

0 comments on commit a3d0ead

Please sign in to comment.