From 8b3485a3f4a59e6e3a2affe13a237f5c6cbda491 Mon Sep 17 00:00:00 2001 From: Techcable Date: Sat, 20 Sep 2025 13:29:14 -0700 Subject: [PATCH 1/2] Add trusted::TrustedContiguousToken Indicates that an id type can be trusted to be contiguous. --- intid-core/src/trusted.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/intid-core/src/trusted.rs b/intid-core/src/trusted.rs index 799b4b4..8ba81ca 100644 --- a/intid-core/src/trusted.rs +++ b/intid-core/src/trusted.rs @@ -67,3 +67,12 @@ impl TrustedRangeToken { } } } + +/* +/// Indicates +pub struct TrustedContiguousToken { + +} +impl TrustedContiguousToken { +} +*/ From 9784dd74f90649b9cd4d90cf65b245ee829dd2e0 Mon Sep 17 00:00:00 2001 From: Techcable Date: Sat, 20 Sep 2025 16:54:10 -0700 Subject: [PATCH 2/2] Add iterator over IntegerIdContiguous types --- intid-core/src/uint.rs | 28 ++++++++++++ intid-core/src/uint/sealed.rs | 12 +++++ intid-core/src/utils.rs | 1 + intid-core/src/utils/iter.rs | 82 +++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 intid-core/src/utils/iter.rs diff --git a/intid-core/src/uint.rs b/intid-core/src/uint.rs index 47dad6d..735561f 100644 --- a/intid-core/src/uint.rs +++ b/intid-core/src/uint.rs @@ -5,6 +5,7 @@ use core::fmt::{Debug, Display, Formatter}; use core::hash::Hash; +use core::ops::{Add, Div, Mul, Sub}; mod sealed; @@ -56,9 +57,36 @@ pub trait UnsignedPrimInt: + MaybeNumTrait + MaybePod + MaybeContiguous + + Add + + Sub + + Mul + + Div { } +/// Subtract the specified value from the integer, +/// triggering UB if the subtraction overflows. +/// +/// # Safety +/// Undefined behavior if the subtraction overflows. +#[inline] +pub unsafe fn unchecked_sub(left: T, right: T) -> T { + // SAFETY: Delegates responsibility + unsafe { sealed::PrivateUnsignedInt::unchecked_sub(left, right) } +} + + +/// Add the specified values, +/// triggering UB if the addition overflows. +/// +/// # Safety +/// Undefined behavior if the addition overflows. +#[inline] +pub unsafe fn unchecked_add(left: T, right: T) -> T { + // SAFETY: Delegates responsibility + unsafe { sealed::PrivateUnsignedInt::unchecked_add(left, right) } +} + /// Cast from one [`UnsignedPrimInt`] into another, /// returning `None` if there is overflow. #[inline] diff --git a/intid-core/src/uint/sealed.rs b/intid-core/src/uint/sealed.rs index 6ad216e..cfd8ca3 100644 --- a/intid-core/src/uint/sealed.rs +++ b/intid-core/src/uint/sealed.rs @@ -7,6 +7,8 @@ pub trait PrivateUnsignedInt: Sized { const TYPE_NAME: &'static str; fn checked_add(self, other: Self) -> Option; fn checked_sub(self, other: Self) -> Option; + unsafe fn unchecked_add(self, other: Self) -> Self; + unsafe fn unchecked_sub(self, other: Self) -> Self; fn from_usize_checked(val: usize) -> Option; fn from_usize_wrapping(val: usize) -> Self; #[allow(clippy::wrong_self_convention)] @@ -39,6 +41,16 @@ macro_rules! impl_primint { <$target>::checked_sub(self, other) } #[inline] + unsafe fn unchecked_add(self, other: Self) -> Self { + // SAFETY: Simply delegates + unsafe { <$target>::unchecked_add(self, other) } + } + #[inline] + unsafe fn unchecked_sub(self, other: Self) -> Self { + // SAFETY: Simply delegates + unsafe { <$target>::unchecked_sub(self, other) } + } + #[inline] fn from_usize_checked(val: usize) -> Option { <$target>::try_from(val).ok() } diff --git a/intid-core/src/utils.rs b/intid-core/src/utils.rs index 1d74cc8..ac54544 100644 --- a/intid-core/src/utils.rs +++ b/intid-core/src/utils.rs @@ -1,5 +1,6 @@ //! Miscellaneous utilities relating to the [`IntegerId`](crate::IntegerId) trait. mod order; +mod iter; pub use self::order::OrderByInt; diff --git a/intid-core/src/utils/iter.rs b/intid-core/src/utils/iter.rs new file mode 100644 index 0000000..811c742 --- /dev/null +++ b/intid-core/src/utils/iter.rs @@ -0,0 +1,82 @@ +use core::iter::StepBy; +use core::num::NonZero; +use crate::IntegerIdContiguous; + +pub fn contiguous() -> IterContiguous { + IterContiguous { + next: T::MIN_ID_INT, + } +} + +/// Indicates that the result of [`IterContiguous::len`] overflowed a [`u64`]. +#[derive(Copy, Clone, Debug)] +#[non_exhaustive] +pub struct IterLengthOverflowError; + +pub struct IterContiguous { + /// The next value to be returned from the iterator. + /// + /// Invariants: + /// - When not `None`, `T::MIN_ID_INT <= next.to_int <= T::MAX_ID_INT` + next: Option, +} +impl IterContiguous { + pub fn len(&self) -> Result { + match self.next { + None => Ok(0), + Some(current) => { + // Cannot overflow because Some(next) <= T::MAX_ID + // + // We can make this addition unchecked only if we trust the range + let delta = if T::TRUSTED_RANGE.is_some() { + // SAFETY: We trust the range and our own invariants + unsafe { + crate::uint::unchecked_sub( + T::MAX_ID_INT.unwrap(), + current.to_int() + ) + } + } else { + T::MAX_ID_INT.unwrap() - current.to_int() + }; + u64::try_from(delta).ok_or(IterLengthOverflowError) + } + } + } +} + +impl core::iter::FusedIterator for IterContiguous {} +impl Iterator for IterContiguous { + type Item = T; + + fn next(&mut self) -> Option { + + } + + fn nth(&mut self, n: usize) -> Option { + todo!() + } + + fn count(self) -> usize + where + Self: Sized, + { + todo!() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let remaining = self.next.unwrap().to_int(); + } +} +impl ExactSizeIterator for IterContiguous where T::Int: SmallerThanUsize {} + +/// Implemented for integer types smaller than a [`usize`]. +/// +/// Not implemented for `u32` on 64-bit platforms because that would be a portability hazard. +/// It is implemented for `u16` on 32-bit/64-bit platforms, +/// because supporting 16-bit platforms is rare in modern codebases. +trait SmallerThanUsize {} +#[cfg(not(target_pointer_width = "16"))] +impl SmallerThanUsize for u16 {} +impl SmallerThanUsize for u8 {}