usearch 2.26.0 → 2.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/cpp/test.cpp +66 -3
- package/include/usearch/index.hpp +9 -1
- package/include/usearch/index_dense.hpp +16 -5
- package/numkong/CITATION.cff +13 -9
- package/numkong/CONTRIBUTING.md +29 -24
- package/numkong/Cargo.toml +1 -1
- package/numkong/README.md +21 -7
- package/numkong/binding.gyp +47 -31
- package/numkong/build.rs +15 -5
- package/numkong/c/dispatch.h +0 -1
- package/numkong/c/parallel.c +139 -0
- package/numkong/c/parallel.h +47 -0
- package/numkong/cmake/toolchain-wasi.cmake +39 -5
- package/numkong/include/README.md +24 -17
- package/numkong/include/numkong/attention/sapphireamx.h +6 -6
- package/numkong/include/numkong/attention/sme.h +2 -2
- package/numkong/include/numkong/capabilities.h +2 -2
- package/numkong/include/numkong/cast/README.md +9 -7
- package/numkong/include/numkong/cast/loongsonasx.h +48 -40
- package/numkong/include/numkong/cast/serial.h +89 -11
- package/numkong/include/numkong/cast.h +3 -3
- package/numkong/include/numkong/curved/README.md +10 -10
- package/numkong/include/numkong/curved/neon.h +1 -0
- package/numkong/include/numkong/curved/neonbfdot.h +1 -0
- package/numkong/include/numkong/curved/smef64.h +1 -0
- package/numkong/include/numkong/dot/README.md +18 -18
- package/numkong/include/numkong/dot/alder.h +8 -8
- package/numkong/include/numkong/dot/haswell.h +8 -8
- package/numkong/include/numkong/dot/icelake.h +12 -12
- package/numkong/include/numkong/dot/loongsonasx.h +6 -6
- package/numkong/include/numkong/dot/neon.h +2 -1
- package/numkong/include/numkong/dot/neonbfdot.h +1 -0
- package/numkong/include/numkong/dot/neonfhm.h +1 -0
- package/numkong/include/numkong/dot/neonsdot.h +1 -0
- package/numkong/include/numkong/dot/powervsx.h +3 -3
- package/numkong/include/numkong/dot/serial.h +19 -6
- package/numkong/include/numkong/dot/skylake.h +4 -4
- package/numkong/include/numkong/dot/svehalf.h +1 -1
- package/numkong/include/numkong/dot/v128relaxed.h +24 -23
- package/numkong/include/numkong/dots/README.md +26 -26
- package/numkong/include/numkong/dots/genoa.h +3 -1
- package/numkong/include/numkong/dots/icelake.h +1 -0
- package/numkong/include/numkong/dots/powervsx.h +1 -1
- package/numkong/include/numkong/dots/serial.h +32 -33
- package/numkong/include/numkong/dots/sierra.h +4 -3
- package/numkong/include/numkong/each/README.md +5 -3
- package/numkong/include/numkong/each/sapphire.h +2 -0
- package/numkong/include/numkong/each/skylake.h +1 -0
- package/numkong/include/numkong/geospatial/README.md +6 -6
- package/numkong/include/numkong/geospatial/v128relaxed.h +1 -0
- package/numkong/include/numkong/maxsim/README.md +5 -5
- package/numkong/include/numkong/maxsim/genoa.h +4 -0
- package/numkong/include/numkong/maxsim/icelake.h +1 -0
- package/numkong/include/numkong/maxsim/serial.h +22 -9
- package/numkong/include/numkong/maxsim/sme.h +3 -2
- package/numkong/include/numkong/mesh/README.md +7 -4
- package/numkong/include/numkong/mesh/genoa.h +2 -2
- package/numkong/include/numkong/mesh/haswell.h +8 -8
- package/numkong/include/numkong/mesh/neon.h +6 -6
- package/numkong/include/numkong/mesh/neonbfdot.h +3 -2
- package/numkong/include/numkong/mesh/neonfhm.h +3 -2
- package/numkong/include/numkong/mesh/rvv.h +4 -4
- package/numkong/include/numkong/mesh/serial.h +17 -17
- package/numkong/include/numkong/mesh/skylake.h +4 -4
- package/numkong/include/numkong/mesh/v128relaxed.h +4 -4
- package/numkong/include/numkong/mesh.hpp +2 -2
- package/numkong/include/numkong/numkong.h +1 -1
- package/numkong/include/numkong/numkong.hpp +1 -1
- package/numkong/include/numkong/probability/README.md +17 -15
- package/numkong/include/numkong/probability/skylake.h +1 -1
- package/numkong/include/numkong/reduce/README.md +27 -21
- package/numkong/include/numkong/reduce/genoa.h +2 -1
- package/numkong/include/numkong/reduce/icelake.h +1 -0
- package/numkong/include/numkong/reduce/neon.h +1 -1
- package/numkong/include/numkong/reduce/serial.h +9 -9
- package/numkong/include/numkong/reduce/skylake.h +4 -4
- package/numkong/include/numkong/reduce.hpp +38 -0
- package/numkong/include/numkong/scalar/README.md +29 -28
- package/numkong/include/numkong/set/README.md +17 -14
- package/numkong/include/numkong/set/haswell.h +15 -16
- package/numkong/include/numkong/set/icelake.h +15 -16
- package/numkong/include/numkong/set/loongsonasx.h +12 -10
- package/numkong/include/numkong/set/neon.h +15 -19
- package/numkong/include/numkong/set/powervsx.h +15 -21
- package/numkong/include/numkong/set/serial.h +19 -20
- package/numkong/include/numkong/set/v128relaxed.h +10 -8
- package/numkong/include/numkong/sets/icelake.h +1 -0
- package/numkong/include/numkong/sparse/README.md +4 -4
- package/numkong/include/numkong/sparse/icelake.h +1 -0
- package/numkong/include/numkong/sparse/neon.h +1 -0
- package/numkong/include/numkong/sparse/serial.h +10 -3
- package/numkong/include/numkong/sparse/turin.h +1 -0
- package/numkong/include/numkong/spatial/README.md +25 -24
- package/numkong/include/numkong/spatial/alder.h +2 -1
- package/numkong/include/numkong/spatial/haswell.h +48 -40
- package/numkong/include/numkong/spatial/icelake.h +2 -1
- package/numkong/include/numkong/spatial/neon.h +54 -46
- package/numkong/include/numkong/spatial/neonbfdot.h +1 -1
- package/numkong/include/numkong/spatial/powervsx.h +54 -46
- package/numkong/include/numkong/spatial/serial.h +56 -46
- package/numkong/include/numkong/spatial/sierra.h +4 -3
- package/numkong/include/numkong/spatial/skylake.h +27 -22
- package/numkong/include/numkong/spatial/sve.h +6 -6
- package/numkong/include/numkong/spatial/svebfdot.h +1 -1
- package/numkong/include/numkong/spatial/svehalf.h +1 -1
- package/numkong/include/numkong/spatial/v128relaxed.h +36 -30
- package/numkong/include/numkong/spatials/README.md +15 -15
- package/numkong/include/numkong/spatials/genoa.h +3 -0
- package/numkong/include/numkong/spatials/graniteamx.h +2 -0
- package/numkong/include/numkong/spatials/icelake.h +1 -0
- package/numkong/include/numkong/spatials/sierra.h +1 -0
- package/numkong/include/numkong/tensor.hpp +236 -123
- package/numkong/include/numkong/trigonometry/README.md +4 -4
- package/numkong/include/numkong/trigonometry/serial.h +6 -6
- package/numkong/include/numkong/trigonometry/v128relaxed.h +1 -0
- package/numkong/include/numkong/types.hpp +46 -16
- package/numkong/include/numkong/vector.hpp +116 -23
- package/numkong/javascript/@numkong-darwin-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-darwin-x64/package.json +1 -1
- package/numkong/javascript/@numkong-linux-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-linux-x64/package.json +1 -1
- package/numkong/javascript/@numkong-win32-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-win32-x64/package.json +1 -1
- package/numkong/javascript/README.md +18 -16
- package/numkong/javascript/numkong.c +70 -40
- package/numkong/package.json +8 -8
- package/numkong/rust/README.md +19 -9
- package/numkong/rust/cast.rs +52 -1
- package/numkong/rust/each.rs +480 -7
- package/numkong/rust/matrix.rs +877 -216
- package/numkong/rust/numkong.rs +12 -8
- package/numkong/rust/reduce.rs +433 -5
- package/numkong/rust/tensor.rs +450 -842
- package/numkong/rust/vector.rs +520 -101
- package/numkong/swift/README.md +5 -3
- package/numkong/test/README.md +4 -4
- package/numkong/test/test-wasi.mjs +17 -0
- package/numkong/test/test_each.cpp +30 -1
- package/numkong/test/test_each.py +74 -1
- package/numkong/test/test_reduce.cpp +51 -0
- package/numkong/test/test_tensor.cpp +2 -63
- package/numkong/test/test_tensor.py +93 -0
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/prebuilds/win32-x64/usearch.node +0 -0
package/numkong/rust/vector.rs
CHANGED
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
//! - [`VectorSpan`]: Mutable, strided, non-owning view
|
|
8
8
|
//! - [`VectorIndex`]: Signed indexing trait (negative indices wrap from end)
|
|
9
9
|
//!
|
|
10
|
+
//! `Vector` and `VectorSpan` implement [`crate::tensor::Fill`] (`fill_zeros` / `fill`) and
|
|
11
|
+
//! [`crate::tensor::CopyFrom`] for the natural source type of each container.
|
|
12
|
+
//! `Vector<u1x8>`, `VectorView<u1x8>`, and `VectorSpan<u1x8>` expose `popcount` / `any_set` /
|
|
13
|
+
//! `none_set` / `all_set` directly so the bit-reduction spelling matches the
|
|
14
|
+
//! [`crate::reduce::BitwiseReductions`] trait used on tensor containers.
|
|
15
|
+
//!
|
|
10
16
|
//! All types use [`StorageElement`] as their element bound, with sub-byte types
|
|
11
17
|
//! (i4x2, u4x2, u1x8) supported via `try_get`/`try_set` and iterators.
|
|
12
18
|
//!
|
|
@@ -46,7 +52,7 @@ extern crate alloc;
|
|
|
46
52
|
use core::marker::PhantomData;
|
|
47
53
|
use core::ptr::NonNull;
|
|
48
54
|
|
|
49
|
-
use crate::tensor::{Allocator, Global, Tensor, TensorError, SIMD_ALIGNMENT};
|
|
55
|
+
use crate::tensor::{Allocator, CopyFrom, Fill, Global, Tensor, TensorError, SIMD_ALIGNMENT};
|
|
50
56
|
use crate::types::{DimMut, DimRef, FloatConvertible, NumberLike, StorageElement};
|
|
51
57
|
|
|
52
58
|
// region: VectorIndex — Signed Indexing
|
|
@@ -61,40 +67,126 @@ pub trait VectorIndex: private::Sealed + Copy {
|
|
|
61
67
|
fn resolve(self, len: usize) -> Option<usize>;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
70
|
+
// Unsigned indexing: direct widen-to-usize, then bounds-check.
|
|
71
|
+
impl private::Sealed for usize {}
|
|
72
|
+
impl VectorIndex for usize {
|
|
73
|
+
#[inline]
|
|
74
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
75
|
+
if self < len { Some(self) } else { None }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
impl private::Sealed for u8 {}
|
|
80
|
+
impl VectorIndex for u8 {
|
|
81
|
+
#[inline]
|
|
82
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
83
|
+
let index = self as usize;
|
|
84
|
+
if index < len { Some(index) } else { None }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
impl private::Sealed for u16 {}
|
|
89
|
+
impl VectorIndex for u16 {
|
|
90
|
+
#[inline]
|
|
91
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
92
|
+
let index = self as usize;
|
|
93
|
+
if index < len { Some(index) } else { None }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
impl private::Sealed for u32 {}
|
|
98
|
+
impl VectorIndex for u32 {
|
|
99
|
+
#[inline]
|
|
100
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
101
|
+
let index = self as usize;
|
|
102
|
+
if index < len { Some(index) } else { None }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
impl private::Sealed for u64 {}
|
|
107
|
+
impl VectorIndex for u64 {
|
|
108
|
+
#[inline]
|
|
109
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
110
|
+
let index = self as usize;
|
|
111
|
+
if index < len { Some(index) } else { None }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Signed indexing: negative values wrap from the end (-1 is the last element).
|
|
116
|
+
impl private::Sealed for isize {}
|
|
117
|
+
impl VectorIndex for isize {
|
|
118
|
+
#[inline]
|
|
119
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
120
|
+
let index = if self >= 0 {
|
|
121
|
+
self as usize
|
|
122
|
+
} else {
|
|
123
|
+
let negation = (-self) as usize;
|
|
124
|
+
if negation > len { return None; }
|
|
125
|
+
len - negation
|
|
126
|
+
};
|
|
127
|
+
if index < len { Some(index) } else { None }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
impl private::Sealed for i8 {}
|
|
132
|
+
impl VectorIndex for i8 {
|
|
133
|
+
#[inline]
|
|
134
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
135
|
+
let index = if self >= 0 {
|
|
136
|
+
self as usize
|
|
137
|
+
} else {
|
|
138
|
+
let negation = (-(self as isize)) as usize;
|
|
139
|
+
if negation > len { return None; }
|
|
140
|
+
len - negation
|
|
141
|
+
};
|
|
142
|
+
if index < len { Some(index) } else { None }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
impl private::Sealed for i16 {}
|
|
147
|
+
impl VectorIndex for i16 {
|
|
148
|
+
#[inline]
|
|
149
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
150
|
+
let index = if self >= 0 {
|
|
151
|
+
self as usize
|
|
152
|
+
} else {
|
|
153
|
+
let negation = (-(self as isize)) as usize;
|
|
154
|
+
if negation > len { return None; }
|
|
155
|
+
len - negation
|
|
156
|
+
};
|
|
157
|
+
if index < len { Some(index) } else { None }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
impl private::Sealed for i32 {}
|
|
162
|
+
impl VectorIndex for i32 {
|
|
163
|
+
#[inline]
|
|
164
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
165
|
+
let index = if self >= 0 {
|
|
166
|
+
self as usize
|
|
167
|
+
} else {
|
|
168
|
+
let negation = (-(self as isize)) as usize;
|
|
169
|
+
if negation > len { return None; }
|
|
170
|
+
len - negation
|
|
171
|
+
};
|
|
172
|
+
if index < len { Some(index) } else { None }
|
|
173
|
+
}
|
|
94
174
|
}
|
|
95
175
|
|
|
96
|
-
|
|
97
|
-
|
|
176
|
+
impl private::Sealed for i64 {}
|
|
177
|
+
impl VectorIndex for i64 {
|
|
178
|
+
#[inline]
|
|
179
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
180
|
+
let index = if self >= 0 {
|
|
181
|
+
self as usize
|
|
182
|
+
} else {
|
|
183
|
+
let negation = (-(self as isize)) as usize;
|
|
184
|
+
if negation > len { return None; }
|
|
185
|
+
len - negation
|
|
186
|
+
};
|
|
187
|
+
if index < len { Some(index) } else { None }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
98
190
|
|
|
99
191
|
// endregion: VectorIndex
|
|
100
192
|
|
|
@@ -309,10 +401,9 @@ impl<'a> BitRefMut<'a> {
|
|
|
309
401
|
pub struct Vector<Scalar: StorageElement, Alloc: Allocator = Global> {
|
|
310
402
|
/// Pointer to the allocated buffer (typed as `Scalar` for alignment).
|
|
311
403
|
data: NonNull<Scalar>,
|
|
312
|
-
/// Number of logical dimensions.
|
|
404
|
+
/// Number of logical dimensions. Storage size is derived as
|
|
405
|
+
/// `dims_to_values::<Scalar>(self.dims)`.
|
|
313
406
|
dims: usize,
|
|
314
|
-
/// Number of storage values (dims / dimensions_per_value, rounded up).
|
|
315
|
-
values: usize,
|
|
316
407
|
/// Allocator instance.
|
|
317
408
|
alloc: Alloc,
|
|
318
409
|
}
|
|
@@ -322,14 +413,15 @@ unsafe impl<Scalar: StorageElement + Sync, Alloc: Allocator + Sync> Sync for Vec
|
|
|
322
413
|
|
|
323
414
|
impl<Scalar: StorageElement, Alloc: Allocator> Drop for Vector<Scalar, Alloc> {
|
|
324
415
|
fn drop(&mut self) {
|
|
325
|
-
|
|
416
|
+
let storage_count = dims_to_values::<Scalar>(self.dims);
|
|
417
|
+
if storage_count > 0 {
|
|
326
418
|
let layout = alloc::alloc::Layout::from_size_align(
|
|
327
|
-
|
|
419
|
+
storage_count * core::mem::size_of::<Scalar>(),
|
|
328
420
|
SIMD_ALIGNMENT,
|
|
329
421
|
)
|
|
330
422
|
.unwrap();
|
|
331
423
|
// SAFETY: data was allocated with this layout in try_zeros_in,
|
|
332
|
-
// and
|
|
424
|
+
// and storage_count > 0 guarantees the pointer is non-dangling.
|
|
333
425
|
unsafe {
|
|
334
426
|
self.alloc.deallocate(
|
|
335
427
|
NonNull::new_unchecked(self.data.as_ptr() as *mut u8),
|
|
@@ -353,36 +445,25 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
353
445
|
/// Construct a vector from raw parts, taking ownership of the allocation.
|
|
354
446
|
///
|
|
355
447
|
/// # Safety
|
|
356
|
-
/// - `data` must point to
|
|
357
|
-
///
|
|
358
|
-
///
|
|
448
|
+
/// - `data` must point to an allocation obtained from `alloc`, aligned to
|
|
449
|
+
/// [`SIMD_ALIGNMENT`], whose backing buffer is sized for the storage count
|
|
450
|
+
/// implied by `dims` (`dims_to_values::<Scalar>(dims)` slots of `Scalar`).
|
|
359
451
|
/// - The caller must not free the memory (this vector takes ownership).
|
|
360
|
-
pub unsafe fn from_raw_parts(
|
|
361
|
-
data
|
|
362
|
-
dims: usize,
|
|
363
|
-
values: usize,
|
|
364
|
-
alloc: Alloc,
|
|
365
|
-
) -> Self {
|
|
366
|
-
Self {
|
|
367
|
-
data,
|
|
368
|
-
dims,
|
|
369
|
-
values,
|
|
370
|
-
alloc,
|
|
371
|
-
}
|
|
452
|
+
pub unsafe fn from_raw_parts(data: NonNull<Scalar>, dims: usize, alloc: Alloc) -> Self {
|
|
453
|
+
Self { data, dims, alloc }
|
|
372
454
|
}
|
|
373
455
|
|
|
374
456
|
/// Try to create a zero-initialized vector with the given number of dimensions.
|
|
375
457
|
pub fn try_zeros_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError> {
|
|
376
|
-
let
|
|
377
|
-
if
|
|
458
|
+
let storage_count = dims_to_values::<Scalar>(dims);
|
|
459
|
+
if storage_count == 0 {
|
|
378
460
|
return Ok(Self {
|
|
379
461
|
data: NonNull::dangling(),
|
|
380
462
|
dims: 0,
|
|
381
|
-
values: 0,
|
|
382
463
|
alloc,
|
|
383
464
|
});
|
|
384
465
|
}
|
|
385
|
-
let size =
|
|
466
|
+
let size = storage_count * core::mem::size_of::<Scalar>();
|
|
386
467
|
let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
|
|
387
468
|
.map_err(|_| TensorError::AllocationFailed)?;
|
|
388
469
|
let ptr = alloc
|
|
@@ -392,7 +473,6 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
392
473
|
Ok(Self {
|
|
393
474
|
data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
|
|
394
475
|
dims,
|
|
395
|
-
values,
|
|
396
476
|
alloc,
|
|
397
477
|
})
|
|
398
478
|
}
|
|
@@ -400,9 +480,10 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
400
480
|
/// Try to create a vector filled with `value`.
|
|
401
481
|
pub fn try_full_in(dims: usize, value: Scalar, alloc: Alloc) -> Result<Self, TensorError> {
|
|
402
482
|
let v = Self::try_zeros_in(dims, alloc)?;
|
|
403
|
-
|
|
483
|
+
let storage_count = dims_to_values::<Scalar>(v.dims);
|
|
484
|
+
if storage_count > 0 {
|
|
404
485
|
let ptr = v.data.as_ptr();
|
|
405
|
-
for i in 0..
|
|
486
|
+
for i in 0..storage_count {
|
|
406
487
|
unsafe { ptr.add(i).write(value) };
|
|
407
488
|
}
|
|
408
489
|
}
|
|
@@ -423,16 +504,15 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
423
504
|
/// The returned vector's contents are uninitialized. Reading from it before
|
|
424
505
|
/// writing is undefined behavior.
|
|
425
506
|
pub unsafe fn try_empty_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError> {
|
|
426
|
-
let
|
|
427
|
-
if
|
|
507
|
+
let storage_count = dims_to_values::<Scalar>(dims);
|
|
508
|
+
if storage_count == 0 {
|
|
428
509
|
return Ok(Self {
|
|
429
510
|
data: NonNull::dangling(),
|
|
430
511
|
dims: 0,
|
|
431
|
-
values: 0,
|
|
432
512
|
alloc,
|
|
433
513
|
});
|
|
434
514
|
}
|
|
435
|
-
let size =
|
|
515
|
+
let size = storage_count * core::mem::size_of::<Scalar>();
|
|
436
516
|
let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
|
|
437
517
|
.map_err(|_| TensorError::AllocationFailed)?;
|
|
438
518
|
let ptr = alloc
|
|
@@ -441,7 +521,6 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
441
521
|
Ok(Self {
|
|
442
522
|
data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
|
|
443
523
|
dims,
|
|
444
|
-
values,
|
|
445
524
|
alloc,
|
|
446
525
|
})
|
|
447
526
|
}
|
|
@@ -494,7 +573,7 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
494
573
|
/// Number of underlying storage values (`Scalar` instances).
|
|
495
574
|
#[inline]
|
|
496
575
|
pub fn size_values(&self) -> usize {
|
|
497
|
-
self.
|
|
576
|
+
dims_to_values::<Scalar>(self.dims)
|
|
498
577
|
}
|
|
499
578
|
|
|
500
579
|
/// Returns true if the vector has zero dimensions.
|
|
@@ -518,7 +597,7 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
518
597
|
/// Size in bytes.
|
|
519
598
|
#[inline]
|
|
520
599
|
pub fn size_bytes(&self) -> usize {
|
|
521
|
-
self.
|
|
600
|
+
dims_to_values::<Scalar>(self.dims) * core::mem::size_of::<Scalar>()
|
|
522
601
|
}
|
|
523
602
|
|
|
524
603
|
/// Create an immutable view of this vector.
|
|
@@ -579,7 +658,7 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
579
658
|
let dims_per_value = Scalar::dimensions_per_value();
|
|
580
659
|
let value_index = i / dims_per_value;
|
|
581
660
|
let sub_index = i % dims_per_value;
|
|
582
|
-
// SAFETY: value_index < self.
|
|
661
|
+
// SAFETY: value_index < dims_to_values(self.dims), implied by the bounds check above
|
|
583
662
|
let packed = unsafe { *self.data.as_ptr().add(value_index) };
|
|
584
663
|
Ok(packed.unpack().as_ref()[sub_index])
|
|
585
664
|
}
|
|
@@ -618,7 +697,7 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
618
697
|
let dims_per_value = Scalar::dimensions_per_value();
|
|
619
698
|
let value_index = i / dims_per_value;
|
|
620
699
|
let sub_index = i % dims_per_value;
|
|
621
|
-
// SAFETY: value_index < self.
|
|
700
|
+
// SAFETY: value_index < dims_to_values(self.dims), implied by the bounds check above
|
|
622
701
|
let ptr = unsafe { self.data.as_ptr().add(value_index) };
|
|
623
702
|
let mut unpacked = unsafe { *ptr }.unpack();
|
|
624
703
|
unpacked.as_mut()[sub_index] = value;
|
|
@@ -626,21 +705,19 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
626
705
|
Ok(())
|
|
627
706
|
}
|
|
628
707
|
|
|
629
|
-
/// Get a slice of the underlying storage values
|
|
708
|
+
/// Get a slice of the underlying storage values. For sub-byte types this returns
|
|
709
|
+
/// the packed storage values, not the logical dimensions.
|
|
630
710
|
#[inline]
|
|
631
711
|
pub fn as_slice(&self) -> &[Scalar] {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
} else {
|
|
635
|
-
// For sub-byte types, return the packed values
|
|
636
|
-
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), self.values) }
|
|
637
|
-
}
|
|
712
|
+
let storage_count = dims_to_values::<Scalar>(self.dims);
|
|
713
|
+
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), storage_count) }
|
|
638
714
|
}
|
|
639
715
|
|
|
640
716
|
/// Get a mutable slice of the underlying storage values.
|
|
641
717
|
#[inline]
|
|
642
718
|
pub fn as_mut_slice(&mut self) -> &mut [Scalar] {
|
|
643
|
-
|
|
719
|
+
let storage_count = dims_to_values::<Scalar>(self.dims);
|
|
720
|
+
unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr(), storage_count) }
|
|
644
721
|
}
|
|
645
722
|
|
|
646
723
|
/// Returns an iterator over the logical dimension values, yielding [`DimRef`] proxies.
|
|
@@ -678,13 +755,12 @@ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
|
678
755
|
shape[0] = self.dims;
|
|
679
756
|
let mut strides = [0isize; MAX_RANK];
|
|
680
757
|
strides[0] = core::mem::size_of::<Scalar>() as isize;
|
|
681
|
-
let alloc_bytes = self.values * core::mem::size_of::<Scalar>();
|
|
682
758
|
let data = self.data;
|
|
683
759
|
// SAFETY: we read the allocator out before forget, transferring ownership
|
|
684
760
|
let alloc = unsafe { core::ptr::read(&self.alloc) };
|
|
685
761
|
core::mem::forget(self);
|
|
686
|
-
// SAFETY:
|
|
687
|
-
let tensor = unsafe { Tensor::from_raw_parts(data,
|
|
762
|
+
// SAFETY: storage size derived from `shape[0]` matches the original allocation.
|
|
763
|
+
let tensor = unsafe { Tensor::from_raw_parts(data, shape, strides, 1, alloc) };
|
|
688
764
|
Ok(tensor)
|
|
689
765
|
}
|
|
690
766
|
}
|
|
@@ -774,15 +850,15 @@ impl<AnyIndex: VectorIndex, Scalar: StorageElement, Alloc: Allocator> core::ops:
|
|
|
774
850
|
impl<Scalar: StorageElement + Clone, Alloc: Allocator + Clone> Vector<Scalar, Alloc> {
|
|
775
851
|
/// Try to clone this vector, returning an error on allocation failure.
|
|
776
852
|
pub fn try_clone(&self) -> Result<Self, TensorError> {
|
|
777
|
-
|
|
853
|
+
let storage_count = dims_to_values::<Scalar>(self.dims);
|
|
854
|
+
if storage_count == 0 {
|
|
778
855
|
return Ok(Self {
|
|
779
856
|
data: NonNull::dangling(),
|
|
780
857
|
dims: 0,
|
|
781
|
-
values: 0,
|
|
782
858
|
alloc: self.alloc.clone(),
|
|
783
859
|
});
|
|
784
860
|
}
|
|
785
|
-
let size =
|
|
861
|
+
let size = storage_count * core::mem::size_of::<Scalar>();
|
|
786
862
|
let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
|
|
787
863
|
.map_err(|_| TensorError::AllocationFailed)?;
|
|
788
864
|
let ptr = self
|
|
@@ -795,7 +871,6 @@ impl<Scalar: StorageElement + Clone, Alloc: Allocator + Clone> Vector<Scalar, Al
|
|
|
795
871
|
Ok(Self {
|
|
796
872
|
data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
|
|
797
873
|
dims: self.dims,
|
|
798
|
-
values: self.values,
|
|
799
874
|
alloc: self.alloc.clone(),
|
|
800
875
|
})
|
|
801
876
|
}
|
|
@@ -812,7 +887,6 @@ impl<Scalar: StorageElement> Default for Vector<Scalar, Global> {
|
|
|
812
887
|
Self {
|
|
813
888
|
data: NonNull::dangling(),
|
|
814
889
|
dims: 0,
|
|
815
|
-
values: 0,
|
|
816
890
|
alloc: Global,
|
|
817
891
|
}
|
|
818
892
|
}
|
|
@@ -1173,21 +1247,6 @@ impl<'a, Scalar: StorageElement> VectorSpan<'a, Scalar> {
|
|
|
1173
1247
|
Ok(())
|
|
1174
1248
|
}
|
|
1175
1249
|
|
|
1176
|
-
/// Fill all elements with a value, respecting the stride between elements.
|
|
1177
|
-
///
|
|
1178
|
-
/// Iterates over storage values (not logical dimensions) to avoid buffer
|
|
1179
|
-
/// overwrite for sub-byte types.
|
|
1180
|
-
pub fn fill(&mut self, value: Scalar) {
|
|
1181
|
-
let values = self.dims.div_ceil(Scalar::dimensions_per_value());
|
|
1182
|
-
for i in 0..values {
|
|
1183
|
-
// SAFETY: i < values; stride * i stays within the allocation
|
|
1184
|
-
let ptr = unsafe {
|
|
1185
|
-
(self.data as *mut u8).offset(self.stride_bytes * i as isize) as *mut Scalar
|
|
1186
|
-
};
|
|
1187
|
-
unsafe { ptr.write(value) };
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
1250
|
/// Get a contiguous slice, if this span is contiguous.
|
|
1192
1251
|
#[inline]
|
|
1193
1252
|
pub fn as_contiguous_slice(&self) -> Option<&[Scalar]> {
|
|
@@ -1275,6 +1334,280 @@ impl<'a, AnyIndex: VectorIndex, Scalar: StorageElement> core::ops::IndexMut<AnyI
|
|
|
1275
1334
|
|
|
1276
1335
|
// endregion: VectorSpan
|
|
1277
1336
|
|
|
1337
|
+
// region: Bit Reductions on u1x8 vectors
|
|
1338
|
+
|
|
1339
|
+
use crate::reduce::ReduceMoments;
|
|
1340
|
+
use crate::types::u1x8;
|
|
1341
|
+
|
|
1342
|
+
/// Population count of a contiguous packed-bit storage slice.
|
|
1343
|
+
///
|
|
1344
|
+
/// Shared body for the inherent `popcount` methods on `Vector<u1x8>`,
|
|
1345
|
+
/// `VectorView<u1x8>`, and `VectorSpan<u1x8>`. Sub-byte vector views always
|
|
1346
|
+
/// have a one-byte stride (you cannot stride by less than a byte), so
|
|
1347
|
+
/// contiguous storage is implied — the slice form is always valid.
|
|
1348
|
+
fn popcount_u1x8_storage(storage: &[u1x8]) -> u64 {
|
|
1349
|
+
if storage.is_empty() {
|
|
1350
|
+
return 0;
|
|
1351
|
+
}
|
|
1352
|
+
let (sum, _sum_of_squares) = u1x8::reduce_moments(storage, core::mem::size_of::<u1x8>());
|
|
1353
|
+
sum
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
impl<Alloc: Allocator> Vector<u1x8, Alloc> {
|
|
1357
|
+
/// Number of set bits across the entire vector.
|
|
1358
|
+
pub fn popcount(&self) -> u64 {
|
|
1359
|
+
let storage_count = self.dims.div_ceil(u1x8::dimensions_per_value());
|
|
1360
|
+
let storage =
|
|
1361
|
+
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), storage_count) };
|
|
1362
|
+
popcount_u1x8_storage(storage)
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/// `true` if at least one bit in the vector is set.
|
|
1366
|
+
pub fn any_set(&self) -> bool {
|
|
1367
|
+
self.popcount() != 0
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/// `true` if no bit in the vector is set.
|
|
1371
|
+
pub fn none_set(&self) -> bool {
|
|
1372
|
+
!self.any_set()
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/// `true` if every bit in the vector is set.
|
|
1376
|
+
pub fn all_set(&self) -> bool {
|
|
1377
|
+
self.popcount() == self.dims as u64
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
impl<'a> VectorView<'a, u1x8> {
|
|
1382
|
+
/// Number of set bits across the entire vector view.
|
|
1383
|
+
pub fn popcount(&self) -> u64 {
|
|
1384
|
+
let storage_count = self.dims.div_ceil(u1x8::dimensions_per_value());
|
|
1385
|
+
let storage = unsafe { core::slice::from_raw_parts(self.data, storage_count) };
|
|
1386
|
+
popcount_u1x8_storage(storage)
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/// `true` if at least one bit in the vector view is set.
|
|
1390
|
+
pub fn any_set(&self) -> bool {
|
|
1391
|
+
self.popcount() != 0
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
/// `true` if no bit in the vector view is set.
|
|
1395
|
+
pub fn none_set(&self) -> bool {
|
|
1396
|
+
!self.any_set()
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/// `true` if every bit in the vector view is set.
|
|
1400
|
+
pub fn all_set(&self) -> bool {
|
|
1401
|
+
self.popcount() == self.dims as u64
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
impl<'a> VectorSpan<'a, u1x8> {
|
|
1406
|
+
/// Number of set bits across the entire vector span.
|
|
1407
|
+
pub fn popcount(&self) -> u64 {
|
|
1408
|
+
let storage_count = self.dims.div_ceil(u1x8::dimensions_per_value());
|
|
1409
|
+
let storage = unsafe { core::slice::from_raw_parts(self.data as *const u1x8, storage_count) };
|
|
1410
|
+
popcount_u1x8_storage(storage)
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/// `true` if at least one bit in the vector span is set.
|
|
1414
|
+
pub fn any_set(&self) -> bool {
|
|
1415
|
+
self.popcount() != 0
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/// `true` if no bit in the vector span is set.
|
|
1419
|
+
pub fn none_set(&self) -> bool {
|
|
1420
|
+
!self.any_set()
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/// `true` if every bit in the vector span is set.
|
|
1424
|
+
pub fn all_set(&self) -> bool {
|
|
1425
|
+
self.popcount() == self.dims as u64
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
// endregion: Bit Reductions on u1x8 vectors
|
|
1430
|
+
|
|
1431
|
+
// region: Fill / CopyFrom Implementations
|
|
1432
|
+
|
|
1433
|
+
impl<Scalar: StorageElement, Alloc: Allocator> Fill<Scalar> for Vector<Scalar, Alloc> {
|
|
1434
|
+
fn fill_zeros(&mut self) {
|
|
1435
|
+
let storage_count = dims_to_values::<Scalar>(self.dims);
|
|
1436
|
+
if storage_count == 0 {
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
unsafe {
|
|
1440
|
+
core::ptr::write_bytes(self.data.as_ptr(), 0, storage_count);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
fn fill(&mut self, value: Scalar) {
|
|
1445
|
+
self.fill_zeros();
|
|
1446
|
+
let storage_count = dims_to_values::<Scalar>(self.dims);
|
|
1447
|
+
if storage_count == 0 {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
// Skip overlay when `value` matches the freshly-zeroed bit pattern.
|
|
1451
|
+
let default_value = Scalar::default();
|
|
1452
|
+
let value_bytes = unsafe {
|
|
1453
|
+
core::slice::from_raw_parts(
|
|
1454
|
+
(&value as *const Scalar) as *const u8,
|
|
1455
|
+
core::mem::size_of::<Scalar>(),
|
|
1456
|
+
)
|
|
1457
|
+
};
|
|
1458
|
+
let default_bytes = unsafe {
|
|
1459
|
+
core::slice::from_raw_parts(
|
|
1460
|
+
(&default_value as *const Scalar) as *const u8,
|
|
1461
|
+
core::mem::size_of::<Scalar>(),
|
|
1462
|
+
)
|
|
1463
|
+
};
|
|
1464
|
+
if value_bytes == default_bytes {
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
if core::mem::size_of::<Scalar>() == 1 {
|
|
1468
|
+
unsafe {
|
|
1469
|
+
core::ptr::write_bytes(self.data.as_ptr() as *mut u8, value_bytes[0], storage_count);
|
|
1470
|
+
}
|
|
1471
|
+
} else {
|
|
1472
|
+
for storage_index in 0..storage_count {
|
|
1473
|
+
unsafe { core::ptr::write(self.data.as_ptr().add(storage_index), value) };
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
impl<Scalar: StorageElement, Alloc: Allocator> CopyFrom<&[Scalar]> for Vector<Scalar, Alloc> {
|
|
1480
|
+
fn copy_from(&mut self, source: &[Scalar]) -> Result<(), TensorError> {
|
|
1481
|
+
let storage_count = dims_to_values::<Scalar>(self.dims);
|
|
1482
|
+
if source.len() != storage_count {
|
|
1483
|
+
return Err(TensorError::ShapeMismatch {
|
|
1484
|
+
axis: 0,
|
|
1485
|
+
expected: storage_count,
|
|
1486
|
+
got: source.len(),
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
if storage_count == 0 {
|
|
1490
|
+
return Ok(());
|
|
1491
|
+
}
|
|
1492
|
+
unsafe {
|
|
1493
|
+
core::ptr::copy_nonoverlapping(source.as_ptr(), self.data.as_ptr(), storage_count);
|
|
1494
|
+
}
|
|
1495
|
+
Ok(())
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
impl<'a, Scalar: StorageElement> Fill<Scalar> for VectorSpan<'a, Scalar> {
|
|
1500
|
+
fn fill_zeros(&mut self) {
|
|
1501
|
+
let storage_count = self.dims.div_ceil(Scalar::dimensions_per_value());
|
|
1502
|
+
if storage_count == 0 {
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
let element_size = core::mem::size_of::<Scalar>() as isize;
|
|
1506
|
+
if self.stride_bytes == element_size {
|
|
1507
|
+
// Contiguous fast path.
|
|
1508
|
+
unsafe {
|
|
1509
|
+
core::ptr::write_bytes(self.data, 0, storage_count);
|
|
1510
|
+
}
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
// Strided: zero one element at a time.
|
|
1514
|
+
for storage_index in 0..storage_count {
|
|
1515
|
+
unsafe {
|
|
1516
|
+
let ptr = (self.data as *mut u8)
|
|
1517
|
+
.offset(self.stride_bytes * storage_index as isize)
|
|
1518
|
+
as *mut Scalar;
|
|
1519
|
+
core::ptr::write_bytes(ptr, 0, 1);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
fn fill(&mut self, value: Scalar) {
|
|
1525
|
+
self.fill_zeros();
|
|
1526
|
+
let storage_count = self.dims.div_ceil(Scalar::dimensions_per_value());
|
|
1527
|
+
if storage_count == 0 {
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
let default_value = Scalar::default();
|
|
1531
|
+
let value_bytes = unsafe {
|
|
1532
|
+
core::slice::from_raw_parts(
|
|
1533
|
+
(&value as *const Scalar) as *const u8,
|
|
1534
|
+
core::mem::size_of::<Scalar>(),
|
|
1535
|
+
)
|
|
1536
|
+
};
|
|
1537
|
+
let default_bytes = unsafe {
|
|
1538
|
+
core::slice::from_raw_parts(
|
|
1539
|
+
(&default_value as *const Scalar) as *const u8,
|
|
1540
|
+
core::mem::size_of::<Scalar>(),
|
|
1541
|
+
)
|
|
1542
|
+
};
|
|
1543
|
+
if value_bytes == default_bytes {
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
let element_size = core::mem::size_of::<Scalar>() as isize;
|
|
1547
|
+
if self.stride_bytes == element_size {
|
|
1548
|
+
// Contiguous fast path.
|
|
1549
|
+
if core::mem::size_of::<Scalar>() == 1 {
|
|
1550
|
+
unsafe {
|
|
1551
|
+
core::ptr::write_bytes(self.data as *mut u8, value_bytes[0], storage_count);
|
|
1552
|
+
}
|
|
1553
|
+
} else {
|
|
1554
|
+
for storage_index in 0..storage_count {
|
|
1555
|
+
unsafe { core::ptr::write(self.data.add(storage_index), value) };
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
// Strided typed broadcast.
|
|
1561
|
+
for storage_index in 0..storage_count {
|
|
1562
|
+
unsafe {
|
|
1563
|
+
let ptr = (self.data as *mut u8)
|
|
1564
|
+
.offset(self.stride_bytes * storage_index as isize)
|
|
1565
|
+
as *mut Scalar;
|
|
1566
|
+
core::ptr::write(ptr, value);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
impl<'a, 'b, Scalar: StorageElement> CopyFrom<&'b VectorView<'_, Scalar>>
|
|
1573
|
+
for VectorSpan<'a, Scalar>
|
|
1574
|
+
{
|
|
1575
|
+
fn copy_from(&mut self, source: &'b VectorView<'_, Scalar>) -> Result<(), TensorError> {
|
|
1576
|
+
if source.size() != self.dims {
|
|
1577
|
+
return Err(TensorError::ShapeMismatch {
|
|
1578
|
+
axis: 0,
|
|
1579
|
+
expected: self.dims,
|
|
1580
|
+
got: source.size(),
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
if self.dims == 0 {
|
|
1584
|
+
return Ok(());
|
|
1585
|
+
}
|
|
1586
|
+
let storage_count = self.dims.div_ceil(Scalar::dimensions_per_value());
|
|
1587
|
+
let element_size = core::mem::size_of::<Scalar>() as isize;
|
|
1588
|
+
if self.stride_bytes == element_size && source.stride_bytes() == element_size {
|
|
1589
|
+
unsafe {
|
|
1590
|
+
core::ptr::copy_nonoverlapping(source.as_ptr(), self.data, storage_count);
|
|
1591
|
+
}
|
|
1592
|
+
return Ok(());
|
|
1593
|
+
}
|
|
1594
|
+
for storage_index in 0..storage_count {
|
|
1595
|
+
unsafe {
|
|
1596
|
+
let destination_ptr = (self.data as *mut u8)
|
|
1597
|
+
.offset(self.stride_bytes * storage_index as isize)
|
|
1598
|
+
as *mut Scalar;
|
|
1599
|
+
let source_ptr = (source.as_ptr() as *const u8)
|
|
1600
|
+
.offset(source.stride_bytes() * storage_index as isize)
|
|
1601
|
+
as *const Scalar;
|
|
1602
|
+
core::ptr::write(destination_ptr, *source_ptr);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
Ok(())
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
// endregion: Fill / CopyFrom Implementations
|
|
1610
|
+
|
|
1278
1611
|
// region: Iterators
|
|
1279
1612
|
|
|
1280
1613
|
/// Stride-aware immutable iterator over logical dimensions, yielding [`DimRef`] proxies.
|
|
@@ -1894,6 +2227,92 @@ mod tests {
|
|
|
1894
2227
|
let v = Vector::<f32>::try_from_scalars(&[1.0]).unwrap();
|
|
1895
2228
|
let _it: VectorIterator<'_, f32> = v.iter();
|
|
1896
2229
|
}
|
|
2230
|
+
|
|
2231
|
+
#[test]
|
|
2232
|
+
fn vector_index_signed_negative_resolves_from_end() {
|
|
2233
|
+
// After macro removal: i32 negative indices wrap from end.
|
|
2234
|
+
assert_eq!(VectorIndex::resolve(-1i32, 5), Some(4));
|
|
2235
|
+
assert_eq!(VectorIndex::resolve(-5i32, 5), Some(0));
|
|
2236
|
+
assert_eq!(VectorIndex::resolve(-6i32, 5), None); // out of bounds
|
|
2237
|
+
// Same logic across all signed widths.
|
|
2238
|
+
assert_eq!(VectorIndex::resolve(-1i8, 5), Some(4));
|
|
2239
|
+
assert_eq!(VectorIndex::resolve(-1i16, 5), Some(4));
|
|
2240
|
+
assert_eq!(VectorIndex::resolve(-1i64, 5), Some(4));
|
|
2241
|
+
assert_eq!(VectorIndex::resolve(-1isize, 5), Some(4));
|
|
2242
|
+
// Unsigned: bounds check only.
|
|
2243
|
+
assert_eq!(VectorIndex::resolve(3u32, 5), Some(3));
|
|
2244
|
+
assert_eq!(VectorIndex::resolve(5u32, 5), None);
|
|
2245
|
+
assert_eq!(VectorIndex::resolve(0u8, 5), Some(0));
|
|
2246
|
+
assert_eq!(VectorIndex::resolve(0usize, 5), Some(0));
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
#[test]
|
|
2250
|
+
fn fill_zeros_and_fill_on_vector_and_span() {
|
|
2251
|
+
// Vector: Fill trait gives fill_zeros + fill methods.
|
|
2252
|
+
use crate::tensor::Fill;
|
|
2253
|
+
let mut v = Vector::<f32>::try_full(8, 3.5).unwrap();
|
|
2254
|
+
v.fill_zeros();
|
|
2255
|
+
assert!(v.as_slice().iter().all(|&value| value == 0.0));
|
|
2256
|
+
v.fill(2.5);
|
|
2257
|
+
assert!(v.as_slice().iter().all(|&value| value == 2.5));
|
|
2258
|
+
|
|
2259
|
+
// VectorSpan: same trait methods.
|
|
2260
|
+
let mut data = [0.0f32; 8];
|
|
2261
|
+
let mut span = unsafe {
|
|
2262
|
+
VectorSpan::<f32>::from_raw_parts(
|
|
2263
|
+
data.as_mut_ptr(),
|
|
2264
|
+
8,
|
|
2265
|
+
core::mem::size_of::<f32>() as isize,
|
|
2266
|
+
)
|
|
2267
|
+
};
|
|
2268
|
+
span.fill(1.25);
|
|
2269
|
+
assert!(data.iter().all(|&value| value == 1.25));
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
#[test]
|
|
2273
|
+
fn copy_from_round_trips_vector_and_span() {
|
|
2274
|
+
use crate::tensor::CopyFrom;
|
|
2275
|
+
// Vector: CopyFrom<&[Scalar]>.
|
|
2276
|
+
let mut destination = Vector::<f32>::try_zeros(4).unwrap();
|
|
2277
|
+
let source = [1.0f32, 2.0, 3.0, 4.0];
|
|
2278
|
+
destination.copy_from(source.as_slice()).unwrap();
|
|
2279
|
+
assert_eq!(destination.as_slice(), &source[..]);
|
|
2280
|
+
|
|
2281
|
+
// VectorSpan: CopyFrom<&VectorView>.
|
|
2282
|
+
let source_vec = Vector::<f32>::try_from_scalars(&[5.0, 6.0, 7.0, 8.0]).unwrap();
|
|
2283
|
+
let mut destination_buffer = [0.0f32; 4];
|
|
2284
|
+
{
|
|
2285
|
+
let mut destination_span = unsafe {
|
|
2286
|
+
VectorSpan::<f32>::from_raw_parts(
|
|
2287
|
+
destination_buffer.as_mut_ptr(),
|
|
2288
|
+
4,
|
|
2289
|
+
core::mem::size_of::<f32>() as isize,
|
|
2290
|
+
)
|
|
2291
|
+
};
|
|
2292
|
+
let source_view = source_vec.view();
|
|
2293
|
+
destination_span.copy_from(&source_view).unwrap();
|
|
2294
|
+
}
|
|
2295
|
+
assert_eq!(destination_buffer, [5.0f32, 6.0, 7.0, 8.0]);
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
#[test]
|
|
2299
|
+
fn bit_reductions_on_u1x8_vector_match_method_names() {
|
|
2300
|
+
use crate::types::u1x8;
|
|
2301
|
+
let mut bits_vector = Vector::<u1x8>::try_zeros(32).unwrap();
|
|
2302
|
+
// Set all 32 bits.
|
|
2303
|
+
for slot in bits_vector.as_mut_slice().iter_mut() {
|
|
2304
|
+
*slot = u1x8(0xFFu8);
|
|
2305
|
+
}
|
|
2306
|
+
assert_eq!(bits_vector.popcount(), 32);
|
|
2307
|
+
assert!(bits_vector.any_set());
|
|
2308
|
+
assert!(!bits_vector.none_set());
|
|
2309
|
+
assert!(bits_vector.all_set());
|
|
2310
|
+
|
|
2311
|
+
bits_vector.fill_zeros();
|
|
2312
|
+
assert_eq!(bits_vector.popcount(), 0);
|
|
2313
|
+
assert!(!bits_vector.any_set());
|
|
2314
|
+
assert!(bits_vector.none_set());
|
|
2315
|
+
}
|
|
1897
2316
|
}
|
|
1898
2317
|
|
|
1899
2318
|
// endregion: Tests
|