1- //! Const-size sliding-window helpers over `&[T]` (PR-X1).
1+ //! Const-size non-overlapping chunk helpers over `&[T]` (PR-X1).
22//!
33//! Centralises the `&[T] → impl Iterator<Item = &[T; N]>` pattern that
44//! SIMD-staged consumers use to walk a buffer N lanes at a time. Without
55//! this helper, each consumer either calls `slice::as_chunks::<N>` directly
66//! (fine but undiscoverable) or rolls a raw slice index without the
77//! compile-time bounds check.
88//!
9- //! # Semantics — non-overlapping (NOT std `array_windows`)
9+ //! # Naming — `array_chunks`, not `array_windows`
1010//!
11- //! Despite the plural name, this is the **non-overlapping** chunk variant —
12- //! consecutive windows do not share elements. The name follows std's
13- //! plural iterator-type convention ([`std::slice::ArrayWindows`]), but the
14- //! semantics match [`std::slice::ArrayChunks`] / `slice::as_chunks`.
11+ //! `std::slice::array_windows::<N>()` (nightly) already exists and is the
12+ //! **overlapping** iterator — `crate::simd::*` re-uses that name for the
13+ //! std method once it stabilises (see the `// Preferred SIMD lane widths`
14+ //! block at the top of `src/simd.rs`, which uses `data.array_windows::<N>()`
15+ //! in its examples).
1516//!
16- //! For an 8-element input walked at `N = 4`:
17- //! - `array_windows` (this module): yields `[0..4]`, `[4..8]` — **2 windows**.
17+ //! This helper is the **non-overlapping** variant, matching
18+ //! [`std::slice::ArrayChunks`] / stable `slice::as_chunks`. For an 8-element
19+ //! input at `N = 4`:
20+ //! - `array_chunks` (this module): yields `[0..4]`, `[4..8]` — 2 windows.
1821//! - `std::slice::array_windows` (overlapping): would yield 5 windows.
1922//!
2023//! Non-overlapping is the correct shape for SIMD-staged inner loops where
2124//! each lane-register load consumes its N elements before advancing by N.
2225//!
2326//! # Layering
2427//!
25- //! Lives in `hpc::array_windows `, re-exported from `crate::simd::*` per the
28+ //! Lives in `hpc::array_chunks `, re-exported from `crate::simd::*` per the
2629//! W1a consumer contract at
2730//! `.claude/knowledge/vertical-simd-consumer-contract.md`.
2831//!
2932//! # Design reference
3033//!
31- //! `.claude/knowledge/pr-x1-design.md` § "3. `array_window`". This module
32- //! ships the **iterator-shape** variant (whole-buffer walk yielding all
33- //! const-size windows); the design doc's singular-window sketch
34- //! (`array_window(slice, offset) -> &[T; N]`) was superseded by the
35- //! iterator form here, which composes directly with SIMD-staged consumer
36- //! loops and avoids per-call panic surface in tight inner loops. The name
37- //! was pluralised to match the std iterator-type convention.
34+ //! `.claude/knowledge/pr-x1-design.md` § "3. `array_window`". The design
35+ //! doc's singular-window sketch (`array_window(slice, offset) -> &[T; N]`)
36+ //! was superseded by the iterator form here. The name landed as
37+ //! `array_chunks` to avoid collision with std's `array_windows` (overlapping)
38+ //! already referenced in the SIMD layer.
3839
3940/// Walk `data` as a sequence of non-overlapping const-size windows.
4041///
4142/// Returns an iterator over `&[T; N]` references into `data`. The tail
42- /// (`data.len() % N` items) is discarded; use [`array_windows_checked `] to
43+ /// (`data.len() % N` items) is discarded; use [`array_chunks_checked `] to
4344/// fail-fast when the length is not a multiple of `N`.
4445///
4546/// Zero-cost: this is a thin wrapper around [`slice::as_chunks`] that pins
4849/// # Examples
4950///
5051/// ```
51- /// use ndarray::hpc::array_windows::array_windows ;
52+ /// use ndarray::hpc::array_chunks::array_chunks ;
5253/// let data: Vec<u8> = (0..16).collect();
53- /// let windows: Vec<&[u8; 4]> = array_windows ::<u8, 4>(&data).collect();
54+ /// let windows: Vec<&[u8; 4]> = array_chunks ::<u8, 4>(&data).collect();
5455/// assert_eq!(windows.len(), 4);
5556/// assert_eq!(windows[0], &[0, 1, 2, 3]);
5657/// assert_eq!(windows[3], &[12, 13, 14, 15]);
5960/// # Examples — tail discarded
6061///
6162/// ```
62- /// use ndarray::hpc::array_windows::array_windows ;
63+ /// use ndarray::hpc::array_chunks::array_chunks ;
6364/// let data: Vec<u8> = (0..7).collect();
64- /// let windows: Vec<&[u8; 4]> = array_windows ::<u8, 4>(&data).collect();
65+ /// let windows: Vec<&[u8; 4]> = array_chunks ::<u8, 4>(&data).collect();
6566/// // 7 / 4 = 1 window; the trailing 3 items are dropped.
6667/// assert_eq!(windows.len(), 1);
6768/// ```
6869#[ inline]
69- pub fn array_windows < T , const N : usize > ( data : & [ T ] ) -> impl Iterator < Item = & [ T ; N ] > + ' _ {
70+ pub fn array_chunks < T , const N : usize > ( data : & [ T ] ) -> impl Iterator < Item = & [ T ; N ] > + ' _ {
7071 data. as_chunks :: < N > ( ) . 0 . iter ( )
7172}
7273
7374/// Walk `data` as `&[T; N]` windows, returning `Err(())` if `data.len()`
7475/// is not a multiple of `N`.
7576///
76- /// This is the strict variant of [`array_windows `]: the consumer asserts up
77+ /// This is the strict variant of [`array_chunks `]: the consumer asserts up
7778/// front that the buffer is lane-aligned and the caller wants the error
7879/// surfaced rather than silently truncating.
7980///
8081/// # Examples
8182///
8283/// ```
83- /// use ndarray::hpc::array_windows::array_windows_checked ;
84+ /// use ndarray::hpc::array_chunks::array_chunks_checked ;
8485/// let data: Vec<u8> = (0..16).collect();
85- /// let it = array_windows_checked ::<u8, 4>(&data).expect("16 is a multiple of 4");
86+ /// let it = array_chunks_checked ::<u8, 4>(&data).expect("16 is a multiple of 4");
8687/// assert_eq!(it.count(), 4);
8788///
8889/// let bad: Vec<u8> = (0..7).collect();
89- /// assert!(array_windows_checked ::<u8, 4>(&bad).is_err());
90+ /// assert!(array_chunks_checked ::<u8, 4>(&bad).is_err());
9091/// ```
9192#[ inline]
92- pub fn array_windows_checked < T , const N : usize > (
93+ pub fn array_chunks_checked < T , const N : usize > (
9394 data : & [ T ] ,
9495) -> Result < impl Iterator < Item = & [ T ; N ] > + ' _ , ( ) > {
9596 if data. len ( ) % N != 0 {
9697 return Err ( ( ) ) ;
9798 }
98- Ok ( array_windows :: < T , N > ( data) )
99+ Ok ( array_chunks :: < T , N > ( data) )
99100}
100101
101102// ============================================================================
@@ -108,46 +109,46 @@ mod tests {
108109
109110 /// 16-element buffer yields four 4-wide windows.
110111 #[ test]
111- fn array_windows_4_over_16 ( ) {
112+ fn array_chunks_4_over_16 ( ) {
112113 let data: Vec < u8 > = ( 0u8 ..16 ) . collect ( ) ;
113- let windows: Vec < & [ u8 ; 4 ] > = array_windows :: < u8 , 4 > ( & data) . collect ( ) ;
114+ let windows: Vec < & [ u8 ; 4 ] > = array_chunks :: < u8 , 4 > ( & data) . collect ( ) ;
114115 assert_eq ! ( windows. len( ) , 4 ) ;
115116 assert_eq ! ( windows[ 0 ] , & [ 0 , 1 , 2 , 3 ] ) ;
116117 assert_eq ! ( windows[ 1 ] , & [ 4 , 5 , 6 , 7 ] ) ;
117118 assert_eq ! ( windows[ 2 ] , & [ 8 , 9 , 10 , 11 ] ) ;
118119 assert_eq ! ( windows[ 3 ] , & [ 12 , 13 , 14 , 15 ] ) ;
119120 }
120121
121- /// Tail items are silently discarded by `array_windows `.
122+ /// Tail items are silently discarded by `array_chunks `.
122123 #[ test]
123- fn array_windows_drops_tail ( ) {
124+ fn array_chunks_drops_tail ( ) {
124125 let data: Vec < u8 > = ( 0u8 ..7 ) . collect ( ) ;
125- let windows: Vec < & [ u8 ; 4 ] > = array_windows :: < u8 , 4 > ( & data) . collect ( ) ;
126+ let windows: Vec < & [ u8 ; 4 ] > = array_chunks :: < u8 , 4 > ( & data) . collect ( ) ;
126127 assert_eq ! ( windows. len( ) , 1 ) ;
127128 assert_eq ! ( windows[ 0 ] , & [ 0 , 1 , 2 , 3 ] ) ;
128129 }
129130
130131 /// Mismatched length surfaces as Err in the checked variant.
131132 #[ test]
132- fn array_windows_checked_rejects_mismatch ( ) {
133- assert ! ( array_windows_checked :: <u8 , 4 >( & [ 0u8 ; 7 ] ) . is_err( ) ) ;
134- assert ! ( array_windows_checked :: <u8 , 4 >( & [ 0u8 ; 5 ] ) . is_err( ) ) ;
135- assert ! ( array_windows_checked :: <u8 , 4 >( & [ 0u8 ; 1 ] ) . is_err( ) ) ;
133+ fn array_chunks_checked_rejects_mismatch ( ) {
134+ assert ! ( array_chunks_checked :: <u8 , 4 >( & [ 0u8 ; 7 ] ) . is_err( ) ) ;
135+ assert ! ( array_chunks_checked :: <u8 , 4 >( & [ 0u8 ; 5 ] ) . is_err( ) ) ;
136+ assert ! ( array_chunks_checked :: <u8 , 4 >( & [ 0u8 ; 1 ] ) . is_err( ) ) ;
136137 }
137138
138139 /// Aligned length succeeds in the checked variant.
139140 #[ test]
140- fn array_windows_checked_accepts_aligned ( ) {
141+ fn array_chunks_checked_accepts_aligned ( ) {
141142 let data = [ 0u8 ; 16 ] ;
142- let it = array_windows_checked :: < u8 , 4 > ( & data) . expect ( "16 is a multiple of 4" ) ;
143+ let it = array_chunks_checked :: < u8 , 4 > ( & data) . expect ( "16 is a multiple of 4" ) ;
143144 assert_eq ! ( it. count( ) , 4 ) ;
144145 }
145146
146147 /// Empty buffer yields zero windows (not an error in either variant).
147148 #[ test]
148- fn array_windows_empty_buffer ( ) {
149- assert_eq ! ( array_windows :: <u8 , 4 >( & [ ] ) . count( ) , 0 ) ;
150- let it = array_windows_checked :: < u8 , 4 > ( & [ ] ) . expect ( "0 % 4 == 0, should be Ok" ) ;
149+ fn array_chunks_empty_buffer ( ) {
150+ assert_eq ! ( array_chunks :: <u8 , 4 >( & [ ] ) . count( ) , 0 ) ;
151+ let it = array_chunks_checked :: < u8 , 4 > ( & [ ] ) . expect ( "0 % 4 == 0, should be Ok" ) ;
151152 assert_eq ! ( it. count( ) , 0 ) ;
152153 }
153154}
0 commit comments