|
1 | | -//! SoA-shaped SIMD substrate primitives (PR-X1). |
| 1 | +//! SoA-shaped SIMD substrate carriers (PR-X1). |
2 | 2 | //! |
3 | 3 | //! Lives at the crate root under `crate::simd_soa::*` and is re-exported |
4 | 4 | //! through `crate::simd::*` per the W1a consumer contract. This is the |
5 | | -//! canonical home for SoA-of-bytes / multi-lane column carriers and the |
6 | | -//! const-size chunk-walking helpers that SIMD-staged inner loops use. |
| 5 | +//! canonical home for SoA-of-bytes / multi-lane column carriers. |
| 6 | +//! |
| 7 | +//! Generic slice / chunk helpers (`array_chunks`, `array_chunks_checked`) |
| 8 | +//! live in `crate::simd_ops` — they're operations, not carriers. |
7 | 9 | //! |
8 | 10 | //! # What lives here |
9 | 11 | //! |
10 | 12 | //! - [`MultiLaneColumn`] — `Arc<[u8]>` carrier with typed-width chunk iters |
11 | | -//! - [`array_chunks`] / [`array_chunks_checked`] — generic const-size |
12 | | -//! non-overlapping `&[T] → impl Iterator<Item = &[T; N]>` helpers |
13 | 13 | //! |
14 | 14 | //! # Layering |
15 | 15 | //! |
16 | 16 | //! This module is **layout-only**. No `#[target_feature]`, no per-arch |
17 | 17 | //! imports, no raw intrinsics. The SIMD register load happens inside the |
18 | 18 | //! consumer's loop using `crate::simd::F32x16::from_array` etc. The |
19 | | -//! `simd.rs` dispatcher re-exports these primitives via `pub use |
| 19 | +//! `simd.rs` dispatcher re-exports these carriers via `pub use |
20 | 20 | //! crate::simd_soa::{…};` so consumers always go through |
21 | 21 | //! `use ndarray::simd::*;`. |
22 | 22 | //! |
|
25 | 25 | //! These types are layout-only. No distance-aware API. See |
26 | 26 | //! `.claude/knowledge/cognitive-distance-typing.md` (no-umbrella rule). |
27 | 27 | //! |
28 | | -//! # Design references |
| 28 | +//! # Design reference |
29 | 29 | //! |
30 | | -//! - `.claude/knowledge/pr-x1-design.md` § "1. `MultiLaneColumn`" |
31 | | -//! - `.claude/knowledge/pr-x1-design.md` § "3. `array_window`" (the |
32 | | -//! singular-window sketch superseded by the iterator form here; the |
33 | | -//! name landed as `array_chunks` to avoid collision with std's |
34 | | -//! nightly overlapping `slice::array_windows`). |
| 30 | +//! `.claude/knowledge/pr-x1-design.md` § "1. `MultiLaneColumn`". |
35 | 31 |
|
36 | 32 | use std::sync::Arc; |
37 | 33 |
|
@@ -174,77 +170,6 @@ impl MultiLaneColumn { |
174 | 170 | } |
175 | 171 | } |
176 | 172 |
|
177 | | -// ════════════════════════════════════════════════════════════════════ |
178 | | -// array_chunks — generic non-overlapping const-size chunk helpers |
179 | | -// ════════════════════════════════════════════════════════════════════ |
180 | | -// |
181 | | -// Naming: `array_chunks` (NOT `array_windows`) because: |
182 | | -// - `std::slice::array_windows::<N>()` (nightly) is the **overlapping** |
183 | | -// variant, already referenced in src/simd.rs comments. |
184 | | -// - These helpers are the **non-overlapping** variant, matching |
185 | | -// `std::slice::ArrayChunks` / stable `slice::as_chunks`. |
186 | | - |
187 | | -/// Walk `data` as a sequence of non-overlapping const-size windows. |
188 | | -/// |
189 | | -/// Returns an iterator over `&[T; N]` references into `data`. The tail |
190 | | -/// (`data.len() % N` items) is discarded; use [`array_chunks_checked`] to |
191 | | -/// fail-fast when the length is not a multiple of `N`. |
192 | | -/// |
193 | | -/// Zero-cost: this is a thin wrapper around [`slice::as_chunks`] that pins |
194 | | -/// the chunk size at the call site for type inference. |
195 | | -/// |
196 | | -/// # Examples |
197 | | -/// |
198 | | -/// ``` |
199 | | -/// use ndarray::simd::array_chunks; |
200 | | -/// let data: Vec<u8> = (0..16).collect(); |
201 | | -/// let windows: Vec<&[u8; 4]> = array_chunks::<u8, 4>(&data).collect(); |
202 | | -/// assert_eq!(windows.len(), 4); |
203 | | -/// assert_eq!(windows[0], &[0, 1, 2, 3]); |
204 | | -/// assert_eq!(windows[3], &[12, 13, 14, 15]); |
205 | | -/// ``` |
206 | | -/// |
207 | | -/// # Examples — tail discarded |
208 | | -/// |
209 | | -/// ``` |
210 | | -/// use ndarray::simd::array_chunks; |
211 | | -/// let data: Vec<u8> = (0..7).collect(); |
212 | | -/// let windows: Vec<&[u8; 4]> = array_chunks::<u8, 4>(&data).collect(); |
213 | | -/// assert_eq!(windows.len(), 1); |
214 | | -/// ``` |
215 | | -#[inline] |
216 | | -pub fn array_chunks<T, const N: usize>(data: &[T]) -> impl Iterator<Item = &[T; N]> + '_ { |
217 | | - data.as_chunks::<N>().0.iter() |
218 | | -} |
219 | | - |
220 | | -/// Walk `data` as `&[T; N]` windows, returning `Err(())` if `data.len()` |
221 | | -/// is not a multiple of `N`. |
222 | | -/// |
223 | | -/// Strict variant of [`array_chunks`]: the consumer asserts up front that |
224 | | -/// the buffer is lane-aligned and wants the error surfaced rather than |
225 | | -/// silently truncating. |
226 | | -/// |
227 | | -/// # Examples |
228 | | -/// |
229 | | -/// ``` |
230 | | -/// use ndarray::simd::array_chunks_checked; |
231 | | -/// let data: Vec<u8> = (0..16).collect(); |
232 | | -/// let it = array_chunks_checked::<u8, 4>(&data).expect("16 is a multiple of 4"); |
233 | | -/// assert_eq!(it.count(), 4); |
234 | | -/// |
235 | | -/// let bad: Vec<u8> = (0..7).collect(); |
236 | | -/// assert!(array_chunks_checked::<u8, 4>(&bad).is_err()); |
237 | | -/// ``` |
238 | | -#[inline] |
239 | | -pub fn array_chunks_checked<T, const N: usize>( |
240 | | - data: &[T], |
241 | | -) -> Result<impl Iterator<Item = &[T; N]> + '_, ()> { |
242 | | - if data.len() % N != 0 { |
243 | | - return Err(()); |
244 | | - } |
245 | | - Ok(array_chunks::<T, N>(data)) |
246 | | -} |
247 | | - |
248 | 173 | // ════════════════════════════════════════════════════════════════════ |
249 | 174 | // Tests |
250 | 175 | // ════════════════════════════════════════════════════════════════════ |
@@ -352,46 +277,4 @@ mod tests { |
352 | 277 | fn assert_send_sync<T: Send + Sync>() {} |
353 | 278 | assert_send_sync::<MultiLaneColumn>(); |
354 | 279 | } |
355 | | - |
356 | | - // ---- array_chunks ---- |
357 | | - |
358 | | - #[test] |
359 | | - fn array_chunks_4_over_16() { |
360 | | - let data: Vec<u8> = (0u8..16).collect(); |
361 | | - let windows: Vec<&[u8; 4]> = array_chunks::<u8, 4>(&data).collect(); |
362 | | - assert_eq!(windows.len(), 4); |
363 | | - assert_eq!(windows[0], &[0, 1, 2, 3]); |
364 | | - assert_eq!(windows[1], &[4, 5, 6, 7]); |
365 | | - assert_eq!(windows[2], &[8, 9, 10, 11]); |
366 | | - assert_eq!(windows[3], &[12, 13, 14, 15]); |
367 | | - } |
368 | | - |
369 | | - #[test] |
370 | | - fn array_chunks_drops_tail() { |
371 | | - let data: Vec<u8> = (0u8..7).collect(); |
372 | | - let windows: Vec<&[u8; 4]> = array_chunks::<u8, 4>(&data).collect(); |
373 | | - assert_eq!(windows.len(), 1); |
374 | | - assert_eq!(windows[0], &[0, 1, 2, 3]); |
375 | | - } |
376 | | - |
377 | | - #[test] |
378 | | - fn array_chunks_checked_rejects_mismatch() { |
379 | | - assert!(array_chunks_checked::<u8, 4>(&[0u8; 7]).is_err()); |
380 | | - assert!(array_chunks_checked::<u8, 4>(&[0u8; 5]).is_err()); |
381 | | - assert!(array_chunks_checked::<u8, 4>(&[0u8; 1]).is_err()); |
382 | | - } |
383 | | - |
384 | | - #[test] |
385 | | - fn array_chunks_checked_accepts_aligned() { |
386 | | - let data = [0u8; 16]; |
387 | | - let it = array_chunks_checked::<u8, 4>(&data).expect("16 is a multiple of 4"); |
388 | | - assert_eq!(it.count(), 4); |
389 | | - } |
390 | | - |
391 | | - #[test] |
392 | | - fn array_chunks_empty_buffer() { |
393 | | - assert_eq!(array_chunks::<u8, 4>(&[]).count(), 0); |
394 | | - let it = array_chunks_checked::<u8, 4>(&[]).expect("0 % 4 == 0, should be Ok"); |
395 | | - assert_eq!(it.count(), 0); |
396 | | - } |
397 | 280 | } |
0 commit comments