ndcopy/
lib.rs

1//! Fast N-dimensional array memcpy.
2//!
3//! Speed is achieved by copying slices row-by-row. Rust code is much faster at copying slices than trying to index
4//! N-dimensional coordinates for every value index.
5//!
6//! # Example Code
7//!
8//! ```
9//! use ndcopy::ndshape::{ConstShape, ConstShape3u32};
10//! use ndcopy::copy3;
11//!
12//! type SrcShape = ConstShape3u32<50, 50, 50>;
13//! type DstShape = ConstShape3u32<25, 25, 25>;
14//! let src = [1u8; SrcShape::USIZE];
15//! let mut dst = [0u8; DstShape::USIZE];
16//!
17//! let copy_shape = [20; 3];
18//! let src_min = [1, 2, 3];
19//! let dst_min = [2, 3, 4];
20//! copy3(
21//!     copy_shape,
22//!     &src,
23//!     &SrcShape {},
24//!     src_min,
25//!     &mut dst,
26//!     &DstShape {},
27//!     dst_min,
28//! );
29//! ```
30
31pub use ndshape;
32
33use ndshape::Shape;
34
35/// Copy 2-dimensional data from `src` to `dst`.
36///
37/// - `copy_shape`: Dimensions of the extent to be copied.
38/// - `src`: The source slice.
39/// - `src_shape`: A `Shape<2, Coord=u32>` for the entire `src` slice.
40/// - `src_start`: The starting 2D offset to copy from `src`.
41/// - `dst`: The destination slice.
42/// - `dst_shape`: A `Shape<2, Coord=u32>` for the entire `dst` slice.
43/// - `dst_start`: The starting 2D offset to copy into `dst`.
44#[inline]
45pub fn copy2<T, Src, Dst>(
46    copy_shape: [u32; 2],
47    src: &[T],
48    src_shape: &Src,
49    src_start: [u32; 2],
50    dst: &mut [T],
51    dst_shape: &Dst,
52    dst_start: [u32; 2],
53) where
54    T: Clone,
55    Src: Shape<2, Coord=u32>,
56    Dst: Shape<2, Coord=u32>,
57{
58    let row_length = copy_shape[0];
59
60    let mut src_y = src_start[1];
61    let mut dst_y = dst_start[1];
62    for _ in 0..copy_shape[1] {
63        let src_row_start = src_shape.linearize([src_start[0], src_y]) as usize;
64        let src_row_end = src_row_start + row_length as usize;
65
66        let dst_row_start = dst_shape.linearize([dst_start[0], dst_y]) as usize;
67        let dst_row_end = dst_row_start + row_length as usize;
68
69        dst[dst_row_start..dst_row_end].clone_from_slice(&src[src_row_start..src_row_end]);
70
71        src_y += 1;
72        dst_y += 1;
73    }
74}
75
76#[test]
77fn test_copy2() {
78    use ndshape::ConstShape2u32;
79
80    let src_shape = ConstShape2u32::<10, 11>;
81    const SRC_SIZE: usize = 10 * 11;
82    let src = [1; SRC_SIZE];
83    let dst_shape = ConstShape2u32::<11, 12>;
84    const DST_SIZE: usize = 11 * 12;
85    let mut dst = [0; DST_SIZE];
86
87    copy2(
88        [2, 3],
89        &src,
90        &src_shape,
91        [3, 4],
92        &mut dst,
93        &dst_shape,
94        [4, 5],
95    );
96
97    for y in 5..5 + 3 {
98        for x in 4..4 + 2 {
99            let i = dst_shape.linearize([x, y]) as usize;
100            assert_eq!(1, dst[i]);
101            dst[i] = 0;
102        }
103    }
104    for i in 0..DST_SIZE {
105        assert_eq!(dst[i], 0);
106    }
107}
108
109/// Fill a 2-dimensional extent of `dst` with `value`.
110///
111/// - `fill_shape`: Dimensions of the extent to be copied.
112/// - `value`: The value to write.
113/// - `dst`: The destination slice.
114/// - `dst_shape`: A `Shape<2, Coord=u32>` for the entire `dst` slice.
115/// - `dst_start`: The starting 2D offset to copy into `dst`.
116#[inline]
117pub fn fill2<T, Dst>(
118    fill_shape: [u32; 2],
119    value: T,
120    dst: &mut [T],
121    dst_shape: &Dst,
122    dst_start: [u32; 2],
123) where
124    T: Clone,
125    Dst: Shape<2, Coord=u32>,
126{
127    let row_length = fill_shape[0];
128
129    let mut dst_y = dst_start[1];
130    for _ in 0..fill_shape[1] {
131        let dst_row_start = dst_shape.linearize([dst_start[0], dst_y]) as usize;
132        let dst_row_end = dst_row_start + row_length as usize;
133
134        dst[dst_row_start..dst_row_end].fill(value.clone());
135
136        dst_y += 1;
137    }
138}
139
140#[test]
141fn test_fill2() {
142    use ndshape::ConstShape2u32;
143
144    let dst_shape = ConstShape2u32::<11, 12>;
145    const DST_SIZE: usize = 11 * 12;
146    let mut dst = [0; DST_SIZE];
147
148    fill2([2, 3], 1, &mut dst, &dst_shape, [4, 5]);
149
150    for y in 5..5 + 3 {
151        for x in 4..4 + 2 {
152            let i = dst_shape.linearize([x, y]) as usize;
153            assert_eq!(1, dst[i]);
154            dst[i] = 0;
155        }
156    }
157    for i in 0..DST_SIZE {
158        assert_eq!(dst[i], 0);
159    }
160}
161
162/// Copy 3-dimensional data from `src` to `dst`.
163///
164/// - `copy_shape`: Dimensions of the extent to be copied.
165/// - `src`: The source slice.
166/// - `src_shape`: A `Shape<3, Coord=u32>` for the entire `src` slice.
167/// - `src_start`: The starting 3D offset to copy from `src`.
168/// - `dst`: The destination slice.
169/// - `dst_shape`: A `Shape<3, Coord=u32>` for the entire `dst` slice.
170/// - `dst_start`: The starting 3D offset to copy into `dst`.
171#[inline]
172pub fn copy3<T, Src, Dst>(
173    copy_shape: [u32; 3],
174    src: &[T],
175    src_shape: &Src,
176    src_start: [u32; 3],
177    dst: &mut [T],
178    dst_shape: &Dst,
179    dst_start: [u32; 3],
180) where
181    T: Clone,
182    Src: Shape<3, Coord=u32>,
183    Dst: Shape<3, Coord=u32>,
184{
185    let row_length = copy_shape[0];
186
187    let mut src_z = src_start[2];
188    let mut dst_z = dst_start[2];
189    for _ in 0..copy_shape[2] {
190        let mut src_y = src_start[1];
191        let mut dst_y = dst_start[1];
192        for _ in 0..copy_shape[1] {
193            let src_row_start = src_shape.linearize([src_start[0], src_y, src_z]) as usize;
194            let src_row_end = src_row_start + row_length as usize;
195
196            let dst_row_start = dst_shape.linearize([dst_start[0], dst_y, dst_z]) as usize;
197            let dst_row_end = dst_row_start + row_length as usize;
198
199            dst[dst_row_start..dst_row_end].clone_from_slice(&src[src_row_start..src_row_end]);
200
201            src_y += 1;
202            dst_y += 1;
203        }
204        src_z += 1;
205        dst_z += 1;
206    }
207}
208
209#[test]
210fn test_copy3() {
211    use ndshape::ConstShape3u32;
212
213    let src_shape = ConstShape3u32::<10, 11, 12>;
214    const SRC_SIZE: usize = 10 * 11 * 12;
215    let src = [1; SRC_SIZE];
216
217    let dst_shape = ConstShape3u32::<11, 12, 13>;
218    const DST_SIZE: usize = 11 * 12 * 13;
219    let mut dst = [0; DST_SIZE];
220
221    copy3(
222        [2, 3, 4],
223        &src,
224        &src_shape,
225        [3, 4, 5],
226        &mut dst,
227        &dst_shape,
228        [4, 5, 6],
229    );
230
231    for z in 6..6 + 4 {
232        for y in 5..5 + 3 {
233            for x in 4..4 + 2 {
234                let i = dst_shape.linearize([x, y, z]) as usize;
235                assert_eq!(1, dst[i]);
236                dst[i] = 0;
237            }
238        }
239    }
240    for i in 0..DST_SIZE {
241        assert_eq!(dst[i], 0);
242    }
243}
244
245/// Fill a 3-dimensional extent of `dst` with `value`.
246///
247/// - `fill_shape`: Dimensions of the extent to be copied.
248/// - `value`: The value to write.
249/// - `dst`: The destination slice.
250/// - `dst_shape`: A `Shape<3, Coord=u32>` for the entire `dst` slice.
251/// - `dst_start`: The starting 3D offset to copy into `dst`.
252#[inline]
253pub fn fill3<T, Dst>(
254    fill_shape: [u32; 3],
255    value: T,
256    dst: &mut [T],
257    dst_shape: &Dst,
258    dst_start: [u32; 3],
259) where
260    T: Clone,
261    Dst: Shape<3, Coord=u32>,
262{
263    let row_length = fill_shape[0];
264
265    let mut dst_z = dst_start[2];
266    for _ in 0..fill_shape[2] {
267        let mut dst_y = dst_start[1];
268        for _ in 0..fill_shape[1] {
269            let dst_row_start = dst_shape.linearize([dst_start[0], dst_y, dst_z]) as usize;
270            let dst_row_end = dst_row_start + row_length as usize;
271
272            dst[dst_row_start..dst_row_end].fill(value.clone());
273
274            dst_y += 1;
275        }
276        dst_z += 1;
277    }
278}
279
280#[test]
281fn test_fill3() {
282    use ndshape::ConstShape3u32;
283
284    let dst_shape = ConstShape3u32::<11, 12, 13>;
285    const DST_SIZE: usize = 11 * 12 * 13;
286    let mut dst = [0; DST_SIZE];
287
288    fill3([2, 3, 4], 1, &mut dst, &dst_shape, [4, 5, 6]);
289
290    for z in 6..6 + 4 {
291        for y in 5..5 + 3 {
292            for x in 4..4 + 2 {
293                let i = dst_shape.linearize([x, y, z]) as usize;
294                assert_eq!(1, dst[i]);
295                dst[i] = 0;
296            }
297        }
298    }
299    for i in 0..DST_SIZE {
300        assert_eq!(dst[i], 0);
301    }
302}
303
304/// Copy 4-dimensional data from `src` to `dst`.
305///
306/// - `copy_shape`: Dimensions of the extent to be copied.
307/// - `src`: The source slice.
308/// - `src_shape`: A `Shape<4, Coord=u32>` for the entire `src` slice.
309/// - `src_start`: The starting 4D offset to copy from `src`.
310/// - `dst`: The destination slice.
311/// - `dst_shape`: A `Shape<4, Coord=u32>` for the entire `dst` slice.
312/// - `dst_start`: The starting 4D offset to copy into `dst`.
313#[inline]
314pub fn copy4<T, Src, Dst>(
315    copy_shape: [u32; 4],
316    src: &[T],
317    src_shape: &Src,
318    src_start: [u32; 4],
319    dst: &mut [T],
320    dst_shape: &Dst,
321    dst_start: [u32; 4],
322) where
323    T: Clone,
324    Src: Shape<4, Coord=u32>,
325    Dst: Shape<4, Coord=u32>,
326{
327    let row_length = copy_shape[0];
328
329    let mut src_w = src_start[3];
330    let mut dst_w = dst_start[3];
331    for _ in 0..copy_shape[3] {
332        let mut src_z = src_start[2];
333        let mut dst_z = dst_start[2];
334        for _ in 0..copy_shape[2] {
335            let mut src_y = src_start[1];
336            let mut dst_y = dst_start[1];
337            for _ in 0..copy_shape[1] {
338                let src_row_start =
339                    src_shape.linearize([src_start[0], src_y, src_z, src_w]) as usize;
340                let src_row_end = src_row_start + row_length as usize;
341
342                let dst_row_start =
343                    dst_shape.linearize([dst_start[0], dst_y, dst_z, dst_w]) as usize;
344                let dst_row_end = dst_row_start + row_length as usize;
345
346                dst[dst_row_start..dst_row_end].clone_from_slice(&src[src_row_start..src_row_end]);
347
348                src_y += 1;
349                dst_y += 1;
350            }
351            src_z += 1;
352            dst_z += 1;
353        }
354        src_w += 1;
355        dst_w += 1;
356    }
357}
358
359#[test]
360fn test_copy4() {
361    use ndshape::ConstShape4u32;
362
363    let src_shape = ConstShape4u32::<10, 11, 12, 13>;
364    const SRC_SIZE: usize = 10 * 11 * 12 * 13;
365    let src = [1; SRC_SIZE];
366
367    let dst_shape = ConstShape4u32::<11, 12, 13, 14>;
368    const DST_SIZE: usize = 11 * 12 * 13 * 14;
369    let mut dst = [0; DST_SIZE];
370
371    copy4(
372        [2, 3, 4, 5],
373        &src,
374        &src_shape,
375        [3, 4, 5, 6],
376        &mut dst,
377        &dst_shape,
378        [4, 5, 6, 7],
379    );
380
381    for w in 7..7 + 5 {
382        for z in 6..6 + 4 {
383            for y in 5..5 + 3 {
384                for x in 4..4 + 2 {
385                    let i = dst_shape.linearize([x, y, z, w]) as usize;
386                    assert_eq!(1, dst[i]);
387                    dst[i] = 0;
388                }
389            }
390        }
391    }
392    for i in 0..DST_SIZE {
393        assert_eq!(dst[i], 0);
394    }
395}
396
397/// Fill a 4-dimensional extent of `dst` with `value`.
398///
399/// - `fill_shape`: Dimensions of the extent to be copied.
400/// - `value`: The value to write.
401/// - `dst`: The destination slice.
402/// - `dst_shape`: A `Shape<4, Coord=u32>` for the entire `dst` slice.
403/// - `dst_start`: The starting 4D offset to copy into `dst`.
404#[inline]
405pub fn fill4<T, Dst>(
406    fill_shape: [u32; 4],
407    value: T,
408    dst: &mut [T],
409    dst_shape: &Dst,
410    dst_start: [u32; 4],
411) where
412    T: Clone,
413    Dst: Shape<4, Coord=u32>,
414{
415    let row_length = fill_shape[0];
416
417    let mut dst_w = dst_start[3];
418    for _ in 0..fill_shape[3] {
419        let mut dst_z = dst_start[2];
420        for _ in 0..fill_shape[2] {
421            let mut dst_y = dst_start[1];
422            for _ in 0..fill_shape[1] {
423                let dst_row_start =
424                    dst_shape.linearize([dst_start[0], dst_y, dst_z, dst_w]) as usize;
425                let dst_row_end = dst_row_start + row_length as usize;
426
427                dst[dst_row_start..dst_row_end].fill(value.clone());
428
429                dst_y += 1;
430            }
431            dst_z += 1;
432        }
433        dst_w += 1;
434    }
435}
436
437#[test]
438fn test_fill4() {
439    use ndshape::ConstShape4u32;
440
441    let dst_shape = ConstShape4u32::<11, 12, 13, 14>;
442    const DST_SIZE: usize = 11 * 12 * 13 * 14;
443    let mut dst = [0; DST_SIZE];
444
445    fill4([2, 3, 4, 5], 1, &mut dst, &dst_shape, [4, 5, 6, 7]);
446
447    for w in 7..7 + 5 {
448        for z in 6..6 + 4 {
449            for y in 5..5 + 3 {
450                for x in 4..4 + 2 {
451                    let i = dst_shape.linearize([x, y, z, w]) as usize;
452                    assert_eq!(1, dst[i]);
453                    dst[i] = 0;
454                }
455            }
456        }
457    }
458    for i in 0..DST_SIZE {
459        assert_eq!(dst[i], 0);
460    }
461}