Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 8 and optionally 16 bit integers with build.rs generated enum #18

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
use std::io::{BufWriter, Result, Write};

#[derive(Clone, Copy)]
enum BitInfo {
Unsigned {
prim_max: u64,
},
Signed {
signed_max: i64,
signed_min: i64,
prim_max: i64,
},
}

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(
mut generated_file: impl Write,
repr_name: &str,
bit_info: BitInfo,
) -> Result<()> {
write!(
generated_file,
"#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[allow(dead_code)]
pub(crate) enum {} {{",
repr_name
)?;

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

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

write!(generated_file, "}}")?;
Ok(())
}

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

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

let mut open_options = std::fs::OpenOptions::new();
open_options.create(true).write(true).truncate(true);

for file in FILES {
let file_path = format!("{}/{}", out_dir, file.name);
let mut generated_file = BufWriter::new(open_options.open(file_path)?);

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)?;

generated_file.flush()?;
}

Ok(())
}
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;
73 changes: 57 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ 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;
#[cfg(feature = "enum_repr_16")]
use enum_impl::u16_repr::U16Repr;
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 @@ -118,8 +128,9 @@ macro_rules! impl_nonmax_fmt {
};
}

/// Define the basic nonmax wrapper using NonZero inner
macro_rules! nonmax {
( common, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
( $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
/// An integer that is known not to equal its maximum value.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
Expand Down Expand Up @@ -153,7 +164,14 @@ macro_rules! nonmax {
pub const fn get(&self) -> $primitive {
self.0.get() ^ $primitive::MAX
}
}
};
}

/// Provide convenience methods over an basic nonmax wrapper with `new_unchecked`` and `get`.
macro_rules! nonmax_impls {
( common, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
impl $nonmax {
/// Gets non-max with the value zero (0)
pub const ZERO: $nonmax = unsafe { Self::new_unchecked(0) };

Expand Down Expand Up @@ -343,12 +361,12 @@ macro_rules! nonmax {
};

( signed, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
nonmax!(common, $nonmax, $non_zero, $primitive);
nonmax_impls!(common, $nonmax, $non_zero, $primitive);
// Nothing unique to signed versions (yet)
};

( unsigned, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
nonmax!(common, $nonmax, $non_zero, $primitive);
nonmax_impls!(common, $nonmax, $non_zero, $primitive);

impl core::ops::BitAnd<$nonmax> for $primitive {
type Output = $nonmax;
Expand Down Expand Up @@ -381,21 +399,44 @@ macro_rules! nonmax {
}
}
};

( def, signed, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
nonmax!($nonmax, $non_zero, $primitive);
nonmax_impls!(signed, $nonmax, $non_zero, $primitive);
};

( def, unsigned, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
nonmax!($nonmax, $non_zero, $primitive);
nonmax_impls!(unsigned, $nonmax, $non_zero, $primitive);
};
}

nonmax!(signed, NonMaxI8, NonZeroI8, i8);
nonmax!(signed, NonMaxI16, NonZeroI16, i16);
nonmax!(signed, NonMaxI32, NonZeroI32, i32);
nonmax!(signed, NonMaxI64, NonZeroI64, i64);
nonmax!(signed, NonMaxI128, NonZeroI128, i128);
nonmax!(signed, NonMaxIsize, NonZeroIsize, isize);

nonmax!(unsigned, NonMaxU8, NonZeroU8, u8);
nonmax!(unsigned, NonMaxU16, NonZeroU16, u16);
nonmax!(unsigned, NonMaxU32, NonZeroU32, u32);
nonmax!(unsigned, NonMaxU64, NonZeroU64, u64);
nonmax!(unsigned, NonMaxU128, NonZeroU128, u128);
nonmax!(unsigned, NonMaxUsize, NonZeroUsize, usize);
enum_impl::nonmax!(NonMaxI8, i8, I8Repr);
enum_impl::nonmax!(NonMaxU8, u8, U8Repr);
#[cfg(feature = "enum_repr_16")]
enum_impl::nonmax!(NonMaxI16, i16, I16Repr);
#[cfg(feature = "enum_repr_16")]
enum_impl::nonmax!(NonMaxU16, u16, U16Repr);

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!(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);
nonmax_impls!(def, unsigned, NonMaxU128, NonZeroU128, u128);
nonmax_impls!(def, unsigned, NonMaxUsize, NonZeroUsize, usize);

// https://doc.rust-lang.org/1.47.0/src/core/convert/num.rs.html#383-407
macro_rules! impl_nonmax_from {
Expand Down