Skip to content

Commit 48da7a8

Browse files
committed
docs: Update README and module documentation for clarity and consistency
refactor: Simplify iterator implementations and improve type annotations fix: Correct type parameters in IntVec and related structures for better type safety
1 parent f55d465 commit 48da7a8

11 files changed

Lines changed: 342 additions & 86 deletions

File tree

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,4 +409,9 @@ python3 python/plot.py --random-access
409409
The resulting SVGs file will be saved in the `images` directory.
410410

411411
[`sux::BitFieldVec`]: https://docs.rs/sux/latest/sux/bits/bit_field_vec/index.htmll
412-
[`succinct::IntVector`]: https://docs.rs/succinct/latest/succinct/int_vec/trait.IntVec.html
412+
[`succinct::IntVector`]: https://docs.rs/succinct/latest/succinct/int_vec/trait.IntVec.html
413+
414+
# TODO
415+
416+
* [ ] Add support for [`epsilon-serde`](https://crates.io/crates/epserde)
417+
* [ ] Add SIMD feature

src/fixed/atomic/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,7 @@ where
12751275
current_index: usize,
12761276
}
12771277

1278-
impl<'a, T> Iterator for AtomicFixedVecIter<'a, T>
1278+
impl<T> Iterator for AtomicFixedVecIter<'_, T>
12791279
where
12801280
T: Storable<u64> + Copy + ToPrimitive,
12811281
{

src/fixed/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! # `FixedVec` Builders
1+
//! # [`FixedVec`] Builders
22
//!
33
//! This module provides builders for constructing an owned [`FixedVec`].
44
//!

src/fixed/iter.rs

Lines changed: 244 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
//! # `FixedVec` Iterators
1+
//! # [`FixedVec`] Iterators
22
//!
33
//! This module provides iterators for sequential access to the elements of a
44
//! [`FixedVec`]. The iterators decode values on the fly without allocating an
55
//! intermediate buffer, making them efficient for processing large datasets.
66
//!
7+
//! The core iterators ([`FixedVecIter`], [`FixedVecSliceIter`]) are stateful and
8+
//! employ a bit-windowing technique. They decode values on the fly without
9+
//! allocating an intermediate buffer. They support both forward and reverse
10+
//! iteration.
11+
//!
712
//! # Provided Iterators
813
//!
9-
//! - [`FixedVecIter`]: A bidirectional iterator over the elements of a `FixedVec`.
10-
//! - [`FixedVecIntoIter`]: A consuming iterator that takes ownership of a `FixedVec`.
11-
//! - [`Chunks`]: An iterator that yields non-overlapping, immutable slices.
12-
//! - [`Windows`]: An iterator that yields overlapping, immutable slices.
14+
//! - [`FixedVecIter`]: A stateful, bidirectional iterator over the elements of a borrowed [`FixedVec`].
15+
//! - [`FixedVecIntoIter`]: A consuming, bidirectional iterator that takes ownership of a [`FixedVec`].
16+
//! - [`FixedVecSliceIter`]: A stateful, bidirectional iterator over the elements of a [`FixedVecSlice`].
17+
//! - [`Chunks`]: An iterator that yields non-overlapping, immutable slices ([`FixedVecSlice`]).
18+
//! - [`Windows`]: An iterator that yields overlapping, immutable slices ([`FixedVecSlice`]).
19+
//! - [`FixedVecUncheckedIter`]: An `unsafe` bidirectional iterator that omits bounds checks for maximum performance.
1320
//!
1421
//! # Examples
1522
//!
@@ -28,6 +35,50 @@
2835
//!
2936
//! assert_eq!(sum, 200);
3037
//! ```
38+
//!
39+
//! ## Bidirectional Iteration
40+
//!
41+
//! The main iterators implement [`DoubleEndedIterator`], allowing for
42+
//! traversal from both ends of the sequence.
43+
//!
44+
//! ```rust
45+
//! use compressed_intvec::fixed::{FixedVec, UFixedVec};
46+
//!
47+
//! let vec: UFixedVec<u8> = (0..=5u8).collect();
48+
//! let mut iter = vec.iter();
49+
//!
50+
//! assert_eq!(iter.next(), Some(0));
51+
//! assert_eq!(iter.next_back(), Some(5));
52+
//! assert_eq!(iter.next(), Some(1));
53+
//! assert_eq!(iter.next_back(), Some(4));
54+
//!
55+
//! // The iterator correctly tracks its remaining length.
56+
//! assert_eq!(iter.len(), 2);
57+
//!
58+
//! assert_eq!(iter.next(), Some(2));
59+
//! assert_eq!(iter.next(), Some(3));
60+
//! assert_eq!(iter.next(), None);
61+
//! ```
62+
//!
63+
//! ## Iterating over a Slice
64+
//!
65+
//! You can also create an iterator over a zero-copy slice of a vector.
66+
//!
67+
//! ```rust
68+
//! use compressed_intvec::fixed::{FixedVec, UFixedVec};
69+
//!
70+
//! let vec: UFixedVec<u32> = (0..100u32).collect();
71+
//!
72+
//! // Create a slice containing elements from index 20 to 29.
73+
//! let slice = vec.slice(20, 10).unwrap();
74+
//! assert_eq!(slice.len(), 10);
75+
//!
76+
//! // The slice iterator will yield the elements from the slice.
77+
//! let collected: Vec<u32> = slice.iter().collect();
78+
//! let expected: Vec<u32> = (20..30).collect();
79+
//!
80+
//! assert_eq!(collected, expected);
81+
//! ```
3182
3283
use crate::fixed::{
3384
slice::FixedVecSlice,
@@ -119,7 +170,7 @@ where
119170
E: Endianness,
120171
B: AsRef<[W]>,
121172
{
122-
/// Creates a new stateful, bidirectional iterator for a given `FixedVec`.
173+
/// Creates a new stateful, bidirectional iterator for a given [`FixedVec`].
123174
pub(super) fn new(vec: &'a FixedVec<T, W, E, B>) -> Self {
124175
if vec.is_empty() {
125176
return Self {
@@ -137,7 +188,7 @@ where
137188
}
138189

139190
let limbs = vec.as_limbs();
140-
let bits_per_word = <W as Word>::BITS;
191+
let bits_per_word: usize = <W as Word>::BITS;
141192

142193
// --- Setup forward state ---
143194
let front_word_index = 1;
@@ -257,7 +308,7 @@ where
257308
}
258309

259310
let bit_width = self.vec.bit_width();
260-
let bits_per_word = <W as Word>::BITS;
311+
let bits_per_word: usize = <W as Word>::BITS;
261312

262313
if self.back_bits_in_window >= bit_width {
263314
self.back_bits_in_window -= bit_width;
@@ -354,6 +405,19 @@ where
354405
}
355406
}
356407

408+
impl<T, W, E, B> DoubleEndedIterator for FixedVecIntoIter<'_, T, W, E, B>
409+
where
410+
T: Storable<W>,
411+
W: Word,
412+
E: Endianness,
413+
B: AsRef<[W]>,
414+
{
415+
#[inline]
416+
fn next_back(&mut self) -> Option<Self::Item> {
417+
self.iter.next_back()
418+
}
419+
}
420+
357421
impl<T, W, E, B> ExactSizeIterator for FixedVecIntoIter<'_, T, W, E, B>
358422
where
359423
T: Storable<W>,
@@ -369,7 +433,8 @@ where
369433
/// An iterator over the elements of a [`FixedVecSlice`].
370434
///
371435
/// This struct is created by the [`iter`](super::slice::FixedVecSlice::iter)
372-
/// method on [`FixedVecSlice`].
436+
/// method on [`FixedVecSlice`]. It is a stateful bitstream reader that decodes
437+
/// values on the fly for both forward and reverse iteration.
373438
pub struct FixedVecSliceIter<'s, T, W, E, B, V>
374439
where
375440
T: Storable<W>,
@@ -379,7 +444,19 @@ where
379444
V: Deref<Target = FixedVec<T, W, E, B>>,
380445
{
381446
slice: &'s FixedVecSlice<V>,
382-
current_index: usize,
447+
front_index: usize, // index relative to slice start
448+
back_index: usize, // index relative to slice start
449+
450+
// State for forward iteration.
451+
front_window: W,
452+
front_bits_in_window: usize,
453+
front_word_index: usize,
454+
455+
// State for backward iteration.
456+
back_window: W,
457+
back_bits_in_window: usize,
458+
back_word_index: usize,
459+
383460
_phantom: PhantomData<(T, W, E, B)>,
384461
}
385462

@@ -391,11 +468,62 @@ where
391468
B: AsRef<[W]>,
392469
V: Deref<Target = FixedVec<T, W, E, B>>,
393470
{
394-
/// Creates a new iterator for a given `FixedVecSlice`.
471+
/// Creates a new stateful, bidirectional iterator for a given `FixedVecSlice`.
395472
pub(super) fn new(slice: &'s FixedVecSlice<V>) -> Self {
473+
let parent_vec = &slice.parent;
474+
if slice.is_empty() {
475+
return Self {
476+
slice,
477+
front_index: 0,
478+
back_index: 0,
479+
front_window: W::ZERO,
480+
front_bits_in_window: 0,
481+
front_word_index: 0,
482+
back_window: W::ZERO,
483+
back_bits_in_window: 0,
484+
back_word_index: 0,
485+
_phantom: PhantomData,
486+
};
487+
}
488+
489+
let bits_per_word: usize = <W as Word>::BITS;
490+
let bit_width: usize = parent_vec.bit_width();
491+
let limbs: &[W] = parent_vec.as_limbs();
492+
let slice_start_abs: usize = slice.range.start;
493+
let slice_end_abs: usize = slice.range.end;
494+
495+
// --- Setup forward state ---
496+
let start_bit_pos: usize = slice_start_abs * bit_width;
497+
let start_word_index: usize = start_bit_pos / bits_per_word;
498+
let start_bit_offset: usize = start_bit_pos % bits_per_word;
499+
500+
let front_window_raw: W = unsafe { *limbs.get_unchecked(start_word_index) };
501+
let front_window: W = front_window_raw >> start_bit_offset;
502+
let front_bits_in_window: usize = bits_per_word - start_bit_offset;
503+
let front_word_index: usize = start_word_index + 1;
504+
505+
// --- Setup backward state ---
506+
let end_bit_pos: usize = slice_end_abs * bit_width;
507+
let back_word_index: usize = (end_bit_pos.saturating_sub(1)) / bits_per_word;
508+
let back_window: W = unsafe { *limbs.get_unchecked(back_word_index) };
509+
510+
let back_bits_in_window = end_bit_pos % bits_per_word;
511+
let back_bits_in_window = if back_bits_in_window == 0 && end_bit_pos > 0 {
512+
bits_per_word
513+
} else {
514+
back_bits_in_window
515+
};
516+
396517
Self {
397518
slice,
398-
current_index: 0,
519+
front_index: 0,
520+
back_index: slice.len(),
521+
front_window,
522+
front_bits_in_window,
523+
front_word_index,
524+
back_window,
525+
back_bits_in_window,
526+
back_word_index,
399527
_phantom: PhantomData,
400528
}
401529
}
@@ -413,21 +541,107 @@ where
413541

414542
#[inline]
415543
fn next(&mut self) -> Option<Self::Item> {
416-
if self.current_index >= self.slice.len() {
544+
if self.front_index >= self.back_index {
417545
return None;
418546
}
419-
// SAFETY: The iterator's logic guarantees the index is in bounds.
420-
let value = unsafe { self.slice.get_unchecked(self.current_index) };
421-
self.current_index += 1;
422-
Some(value)
547+
548+
let parent_vec = &self.slice.parent;
549+
let bit_width = parent_vec.bit_width();
550+
let mask = parent_vec.mask;
551+
552+
let abs_index = self.slice.range.start + self.front_index;
553+
self.front_index += 1;
554+
555+
if bit_width == <W as Word>::BITS {
556+
let val = unsafe { *parent_vec.as_limbs().get_unchecked(abs_index) };
557+
let final_val = if E::IS_BIG { W::from_be(val) } else { val };
558+
return Some(<T as Storable<W>>::from_word(final_val));
559+
}
560+
561+
if E::IS_BIG {
562+
return Some(unsafe { parent_vec.get_unchecked(abs_index) });
563+
}
564+
565+
if self.front_bits_in_window >= bit_width {
566+
let value = self.front_window & mask;
567+
self.front_window >>= bit_width;
568+
self.front_bits_in_window -= bit_width;
569+
return Some(<T as Storable<W>>::from_word(value));
570+
}
571+
572+
unsafe {
573+
let limbs = parent_vec.as_limbs();
574+
let bits_from_old = self.front_bits_in_window;
575+
let mut result = self.front_window;
576+
577+
self.front_window = *limbs.get_unchecked(self.front_word_index);
578+
self.front_word_index += 1;
579+
result |= self.front_window << bits_from_old;
580+
let value = result & mask;
581+
582+
let bits_from_new = bit_width - bits_from_old;
583+
self.front_window >>= bits_from_new;
584+
self.front_bits_in_window = <W as Word>::BITS - bits_from_new;
585+
586+
Some(<T as Storable<W>>::from_word(value))
587+
}
423588
}
424589

425590
fn size_hint(&self) -> (usize, Option<usize>) {
426-
let remaining = self.slice.len().saturating_sub(self.current_index);
591+
let remaining = self.back_index.saturating_sub(self.front_index);
427592
(remaining, Some(remaining))
428593
}
429594
}
430595

596+
impl<T, W, E, B, V> DoubleEndedIterator for FixedVecSliceIter<'_, T, W, E, B, V>
597+
where
598+
T: Storable<W>,
599+
W: Word,
600+
E: Endianness,
601+
B: AsRef<[W]>,
602+
V: Deref<Target = FixedVec<T, W, E, B>>,
603+
{
604+
#[inline]
605+
fn next_back(&mut self) -> Option<Self::Item> {
606+
if self.front_index >= self.back_index {
607+
return None;
608+
}
609+
self.back_index -= 1;
610+
let abs_index = self.slice.range.start + self.back_index;
611+
let parent_vec = &self.slice.parent;
612+
613+
if E::IS_BIG || parent_vec.bit_width() == <W as Word>::BITS {
614+
return Some(unsafe { parent_vec.get_unchecked(abs_index) });
615+
}
616+
617+
let bit_width = parent_vec.bit_width();
618+
let bits_per_word: usize = <W as Word>::BITS;
619+
620+
if self.back_bits_in_window >= bit_width {
621+
self.back_bits_in_window -= bit_width;
622+
let value = (self.back_window >> self.back_bits_in_window) & parent_vec.mask;
623+
return Some(<T as Storable<W>>::from_word(value));
624+
}
625+
626+
unsafe {
627+
let limbs = parent_vec.as_limbs();
628+
let bits_from_old = self.back_bits_in_window;
629+
let mut result = self.back_window;
630+
631+
self.back_word_index -= 1;
632+
self.back_window = *limbs.get_unchecked(self.back_word_index);
633+
634+
result &= (W::ONE << bits_from_old).wrapping_sub(W::ONE);
635+
let bits_from_new = bit_width - bits_from_old;
636+
result <<= bits_from_new;
637+
result |= self.back_window >> (bits_per_word - bits_from_new);
638+
639+
self.back_bits_in_window = bits_per_word - bits_from_new;
640+
Some(<T as Storable<W>>::from_word(result))
641+
}
642+
}
643+
}
644+
431645
impl<T, W, E, B, V> ExactSizeIterator for FixedVecSliceIter<'_, T, W, E, B, V>
432646
where
433647
T: Storable<W>,
@@ -437,7 +651,7 @@ where
437651
V: Deref<Target = FixedVec<T, W, E, B>>,
438652
{
439653
fn len(&self) -> usize {
440-
self.slice.len().saturating_sub(self.current_index)
654+
self.back_index.saturating_sub(self.front_index)
441655
}
442656
}
443657

@@ -578,4 +792,14 @@ where
578792
// The primary gain here is removing the check in `next()`.
579793
self.iter.next().unwrap_unchecked()
580794
}
581-
}
795+
796+
/// Returns the next element from the back without bounds checking.
797+
///
798+
/// # Safety
799+
///
800+
/// Calling this method when the iterator is exhausted is undefined behavior.
801+
#[inline]
802+
pub unsafe fn next_back_unchecked(&mut self) -> T {
803+
self.iter.next_back().unwrap_unchecked()
804+
}
805+
}

0 commit comments

Comments
 (0)