iterator sizing lib
bendn 2024-05-20
parent 52ea1f5 · commit 959c760
-rw-r--r--Cargo.toml7
-rw-r--r--README.md2
-rw-r--r--src/lib.rs129
3 files changed, 132 insertions, 6 deletions
diff --git a/Cargo.toml b/Cargo.toml
index d176b56..a217fa3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,9 +1,12 @@
[package]
name = "hinted"
-version = "0.0.0"
+version = "0.0.1"
edition = "2021"
authors = ["bend-n <[email protected]>"]
license = "MIT"
repository = "https://github.com/bend-n/hinted"
exclude = [".gitignore"]
-description = "small crate for providing a size hint on an iterator"
+description = "small crate for providing a size hint and exact size on an iterator"
+
+[features]
+nightly = []
diff --git a/README.md b/README.md
index 9bdde41..ac741d1 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# hinted
-small library providing a `hinted` function to set the lower bound size hint of any iterator. \ No newline at end of file
+small library providing a `hinted` (and `has` to set the size) function to set the lower bound size hint of any iterator.
diff --git a/src/lib.rs b/src/lib.rs
index 86bb130..2c4ae93 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,9 +1,27 @@
+#![cfg_attr(
+ feature = "nightly",
+ feature(
+ trusted_len,
+ trusted_fused,
+ min_specialization,
+ trusted_random_access,
+ inplace_iteration,
+ doc_auto_cfg
+ )
+)]
+#![no_std]
+use core::iter::FusedIterator;
+#[cfg(feature = "nightly")]
+use core::iter::{
+ InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
+};
/// Provides a hint for an iterator.
-pub struct Hinted<I: Iterator> {
+#[derive(Clone, Debug)]
+pub struct Hinted<I> {
/// Wrapped iterator.
- pub iter: I,
+ iter: I,
/// Size hint, decreased on every call to [`next()`](Iterator::next).
- pub hint: usize,
+ hint: usize,
}
/// Set the size hint for an iterator.
@@ -11,9 +29,21 @@ pub trait HintExt: Iterator
where
Self: Sized,
{
+ /// Sets the lower bound of [`Iterator::size_hint`]. Useful for collects etc.
fn hinted(self, hint: usize) -> Hinted<Self> {
Hinted { iter: self, hint }
}
+ /// Implements [`ExactSizeIterator`] for any iterator, with a len provided by you.
+ /// This implements [`TrustedLen`](std::iter::TrustedLen) as well, so this function is unsafe.
+ ///
+ /// The resulting iterator will not return after the length has been reached.
+ ///
+ /// # Safety
+ ///
+ /// number of items *must* be `len`.
+ unsafe fn has(self, len: usize) -> Exactly<Self> {
+ Exactly { iter: self, len }
+ }
}
impl<I: Iterator> HintExt for I {}
@@ -33,3 +63,96 @@ where
(self.hint, self.iter.size_hint().1)
}
}
+impl<I: DoubleEndedIterator> DoubleEndedIterator for Hinted<I> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.hint = self.hint.saturating_sub(1);
+ self.iter.next_back()
+ }
+}
+
+impl<I: FusedIterator> FusedIterator for Hinted<I> {}
+#[cfg(feature = "nightly")]
+unsafe impl<I: TrustedRandomAccess> TrustedRandomAccess for Hinted<I> {}
+#[cfg(feature = "nightly")]
+unsafe impl<I: TrustedRandomAccessNoCoerce> TrustedRandomAccessNoCoerce for Hinted<I> {
+ const MAY_HAVE_SIDE_EFFECT: bool = true;
+}
+#[cfg(feature = "nightly")]
+unsafe impl<I: SourceIter> SourceIter for Hinted<I> {
+ type Source = I::Source;
+
+ #[inline]
+ unsafe fn as_inner(&mut self) -> &mut I::Source {
+ // SAFETY: unsafe function forwarding to unsafe function with the same requirements
+ unsafe { SourceIter::as_inner(&mut self.iter) }
+ }
+}
+#[cfg(feature = "nightly")]
+unsafe impl<I: InPlaceIterable> InPlaceIterable for Hinted<I> {
+ const EXPAND_BY: Option<core::num::NonZero<usize>> = I::EXPAND_BY;
+ const MERGE_BY: Option<core::num::NonZero<usize>> = I::MERGE_BY;
+}
+
+/// Provides a size for an iterator.
+#[derive(Clone, Debug)]
+pub struct Exactly<I> {
+ /// Wrapped iterator.
+ iter: I,
+ /// Exact size, decreased on every call to [`next()`](Iterator::next).
+ len: usize,
+}
+
+/// Set the size hint for an iterator.
+impl<I> Iterator for Exactly<I>
+where
+ I: Iterator + Sized,
+{
+ type Item = <I as Iterator>::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.len = unsafe { self.len.unchecked_sub(1) };
+ self.iter.next()
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (self.len, Some(self.len))
+ }
+}
+
+impl<I: Iterator> ExactSizeIterator for Exactly<I> {
+ fn len(&self) -> usize {
+ self.len
+ }
+}
+impl<I: DoubleEndedIterator> DoubleEndedIterator for Exactly<I> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.len = unsafe { self.len.unchecked_sub(1) };
+ self.iter.next_back()
+ }
+}
+impl<I: FusedIterator> FusedIterator for Exactly<I> {}
+
+// SAFETY: see [`HintExt::has`].
+#[cfg(feature = "nightly")]
+unsafe impl<I: Iterator> TrustedLen for Exactly<I> {}
+#[cfg(feature = "nightly")]
+unsafe impl<I: TrustedRandomAccess> TrustedRandomAccess for Exactly<I> {}
+#[cfg(feature = "nightly")]
+unsafe impl<I: TrustedRandomAccessNoCoerce> TrustedRandomAccessNoCoerce for Exactly<I> {
+ const MAY_HAVE_SIDE_EFFECT: bool = true;
+}
+#[cfg(feature = "nightly")]
+unsafe impl<I: SourceIter> SourceIter for Exactly<I> {
+ type Source = I::Source;
+
+ #[inline]
+ unsafe fn as_inner(&mut self) -> &mut I::Source {
+ // SAFETY: unsafe function forwarding to unsafe function with the same requirements
+ unsafe { SourceIter::as_inner(&mut self.iter) }
+ }
+}
+#[cfg(feature = "nightly")]
+unsafe impl<I: InPlaceIterable> InPlaceIterable for Exactly<I> {
+ const EXPAND_BY: Option<core::num::NonZero<usize>> = I::EXPAND_BY;
+ const MERGE_BY: Option<core::num::NonZero<usize>> = I::MERGE_BY;
+}