diff --git a/Cargo.toml b/Cargo.toml index ff5baa1..0740499 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "num-primitive" -version = "0.3.2" +version = "0.3.3" description = "Traits for primitive numeric types" repository = "https://github.com/rust-num/num-primitive" license = "MIT OR Apache-2.0" @@ -9,6 +9,10 @@ categories = ["algorithms", "science", "no-std"] edition = "2024" rust-version = "1.91" +[package.metadata.release] +allow-branch = ["main"] +sign-tag = true + [features] default = ["std"] std = [] @@ -24,7 +28,3 @@ unreachable-pub = "deny" [lints.rustdoc] broken-intra-doc-links = "deny" private-intra-doc-links = "deny" - -[package.metadata.release] -allow-branch = ["main"] -sign-tag = true diff --git a/RELEASES.md b/RELEASES.md index d79dd39..04f1aac 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,9 @@ +# Release 0.3.3 (2025-12-17) + +- Added `PrimitiveBytes` to consolidate the constraints on `PrimitiveNumber::Bytes` + - Extended with `IndexMut`, and `PartialEq` and `TryFrom` with slice refs. + - Added array construction methods `from_fn` and `repeat`. + # Release 0.3.2 (2025-12-16) - Updated to MSRV 1.91. diff --git a/src/bytes.rs b/src/bytes.rs new file mode 100644 index 0000000..6ece3f5 --- /dev/null +++ b/src/bytes.rs @@ -0,0 +1,129 @@ +use core::array::TryFromSliceError; + +trait Sealed {} + +/// Trait for arrays of bytes that may be used in numeric conversions. +/// +/// In particular, this is used as a bound for the associated type +/// [`PrimitiveNumber::Bytes`][crate::PrimitiveNumber::Bytes] for converting numbers to and from an +/// array of bytes in various endian orders. It is simply `[u8; size_of::()]` for every +/// primitive number type, but there's no way yet to write that directly in the trait. +/// +/// This trait is not exhaustive of everything byte arrays can do, but it's enough to be useful for +/// generically constructing bytes and dealing with them as slices. +/// +/// This trait is sealed with a private trait to prevent downstream implementations, so we may +/// continue to expand along with the standard library without worrying about breaking changes for +/// implementors. +/// +/// # Examples +/// +/// The supertraits of `PrimitiveBytes` can be used without importing this trait directly: +/// +/// ``` +/// use num_primitive::PrimitiveNumber; +/// +/// // Return a value with the most significant bit set +/// fn msb() -> T { +/// let mut bytes = T::Bytes::default(); // prelude `Default` +/// bytes[0] = 0x80; // operator `IndexMut` +/// T::from_be_bytes(bytes) +/// } +/// +/// assert_eq!(msb::(), i64::MIN); +/// assert_eq!(msb::(), 1u16 << 15); +/// assert!(msb::().total_cmp(&-0.0).is_eq()); +/// ``` +/// +/// However, this trait must be imported to use its own methods like [`repeat`][Self::repeat]: +/// +/// ``` +/// use num_primitive::{PrimitiveBytes, PrimitiveNumber}; +/// +/// // Return a value with all bits set +/// fn all_ones() -> T { +/// T::from_ne_bytes(T::Bytes::repeat(0xff)) +/// } +/// +/// assert_eq!(all_ones::(), -1); +/// assert_eq!(all_ones::(), usize::MAX); +/// assert!(all_ones::().is_nan()); +/// ``` +/// +/// In cases where the size is known, you can use that as a constraint and then work with byte +/// arrays directly, regardless of this trait. +/// +/// ``` +/// use num_primitive::PrimitiveNumber; +/// +/// fn rust>() -> T { +/// T::from_be_bytes(*b"Rust") +/// } +/// +/// assert_eq!(rust::(), 0x52_75_73_74_i32); +/// assert_eq!(rust::(), 0x52_75_73_74_u32); +/// assert_eq!(rust::(), 2.63551e11); +/// ``` +#[expect(private_bounds)] +pub trait PrimitiveBytes: + 'static + + Sealed + + core::borrow::Borrow<[u8]> + + core::borrow::BorrowMut<[u8]> + + core::cmp::Eq + + core::cmp::Ord + + core::cmp::PartialEq<[u8]> + + core::convert::AsRef<[u8]> + + core::convert::AsMut<[u8]> + + core::default::Default + + core::fmt::Debug + + core::hash::Hash + + core::marker::Copy + + core::marker::Send + + core::marker::Sync + + core::marker::Unpin + + core::ops::Index + + core::ops::IndexMut + + core::panic::RefUnwindSafe + + core::panic::UnwindSafe + + for<'a> core::cmp::PartialEq<&'a [u8]> + + for<'a> core::cmp::PartialEq<&'a mut [u8]> + + for<'a> core::convert::TryFrom<&'a [u8], Error = TryFromSliceError> + + for<'a> core::convert::TryFrom<&'a mut [u8], Error = TryFromSliceError> +{ + /// Creates an array of bytes where each byte is produced by calling `f` + /// with that element's index while walking forward through the array. + /// + /// See the [`core::array::from_fn`] function. + fn from_fn(f: F) -> Self + where + F: FnMut(usize) -> u8; + + /// Creates an array of bytes with a repeat expression, `[value; N]`. + fn repeat(value: u8) -> Self; +} + +macro_rules! impl_bytes { + ($($N:literal),+) => {$( + impl Sealed for [u8; $N] {} + + impl PrimitiveBytes for [u8; $N] { + #[inline] + fn from_fn(f: F) -> Self + where + F: FnMut(usize) -> u8 + { + core::array::from_fn(f) + } + + #[inline] + fn repeat(value: u8) -> Self { + // We don't need to forward to `array::repeat` for cloning, + // since we can construct it directly with `u8` copies. + [value; $N] + } + } + )+} +} + +impl_bytes!(1, 2, 4, 8, 16); diff --git a/src/lib.rs b/src/lib.rs index b826a4b..2901999 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,7 @@ extern crate std; #[macro_use] mod macros; +mod bytes; mod error; mod float; mod integer; @@ -69,6 +70,7 @@ mod unsigned; #[cfg(test)] mod tests; +pub use self::bytes::PrimitiveBytes; pub use self::error::PrimitiveError; pub use self::float::{PrimitiveFloat, PrimitiveFloatRef, PrimitiveFloatToInt}; pub use self::integer::{PrimitiveInteger, PrimitiveIntegerRef}; diff --git a/src/number.rs b/src/number.rs index b8a5815..84b5a5f 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,4 +1,4 @@ -use crate::PrimitiveError; +use crate::{PrimitiveBytes, PrimitiveError}; trait Sealed {} struct SealedToken; @@ -102,23 +102,7 @@ pub trait PrimitiveNumber: { /// An array of bytes used by methods like [`from_be_bytes`][Self::from_be_bytes] and /// [`to_be_bytes`][Self::to_be_bytes]. It is effectively `[u8; size_of::()]`. - type Bytes: 'static - + core::borrow::Borrow<[u8]> - + core::borrow::BorrowMut<[u8]> - + core::cmp::Eq - + core::cmp::Ord - + core::cmp::PartialEq<[u8]> - + core::convert::AsRef<[u8]> - + core::convert::AsMut<[u8]> - + core::default::Default - + core::fmt::Debug - + core::hash::Hash - + core::marker::Copy - + core::marker::Send - + core::marker::Sync - + core::marker::Unpin - + core::panic::RefUnwindSafe - + core::panic::UnwindSafe; + type Bytes: PrimitiveBytes; /// Creates a number from its representation as a byte array in big endian. fn from_be_bytes(bytes: Self::Bytes) -> Self;