1use core::mem::MaybeUninit;
9
10use crate::{CopyPlan, MaybeSendSync, RawStridedMut, RawStridedRef, Result, StridedError};
11
12type AxisVec<T> = crate::inline::InlineVec<T>;
13
14#[derive(Clone, Debug)]
19pub struct SlicePlan {
20 operand_dims: AxisVec<usize>,
21 operand_strides: AxisVec<isize>,
22 dest_dims: AxisVec<usize>,
23 dest_strides: AxisVec<isize>,
24 source_strides: AxisVec<isize>,
25 source_offset_delta: isize,
26 copy_plan: CopyPlan,
27}
28
29#[derive(Clone, Debug)]
33pub struct ReversePlan {
34 operand_dims: AxisVec<usize>,
35 operand_strides: AxisVec<isize>,
36 dest_strides: AxisVec<isize>,
37 source_strides: AxisVec<isize>,
38 source_offset_delta: isize,
39 copy_plan: CopyPlan,
40}
41
42#[derive(Clone, Debug)]
50pub struct PadPlan {
51 operand_dims: AxisVec<usize>,
52 operand_strides: AxisVec<isize>,
53 dest_dims: AxisVec<usize>,
54 dest_strides: AxisVec<isize>,
55 edge_padding_low: AxisVec<i64>,
56 interior_step: AxisVec<i64>,
57 operand_total: usize,
58 dest_total: usize,
59 contiguous_dest_fill: bool,
60 contiguous_axis0_run: Option<ContiguousPadAxis0Run>,
61 contiguous_axis0_columns: Option<ContiguousPadAxis0Columns>,
62 generic_fill: PadFillCursor,
63 generic_copy: PadCopyCursor,
64}
65
66#[derive(Clone, Debug)]
67struct PadFillCursor {
68 steps: AxisVec<isize>,
69 resets: AxisVec<isize>,
70}
71
72#[derive(Clone, Debug)]
73struct PadCopyCursor {
74 shape: AxisVec<usize>,
75 source_base_delta: isize,
76 dest_base_delta: isize,
77 source_steps: AxisVec<isize>,
78 source_resets: AxisVec<isize>,
79 dest_steps: AxisVec<isize>,
80 dest_resets: AxisVec<isize>,
81 total: usize,
82}
83
84#[derive(Clone, Copy, Debug, Eq, PartialEq)]
85struct ContiguousPadAxis0Run {
86 operand_start: usize,
87 dest_start: usize,
88 len: usize,
89}
90
91#[derive(Clone, Debug)]
101struct ContiguousPadAxis0Columns {
102 column_total: usize,
104 dest_axis0_len: usize,
105 dest_axis0_step: isize,
106 low0: usize,
107 high0: usize,
108 run_len: usize,
109 operand_outer_dims: AxisVec<usize>,
110 operand_outer_strides: AxisVec<isize>,
111 dest_outer_dims: AxisVec<usize>,
112 edge_low_outer: AxisVec<usize>,
113}
114
115#[derive(Clone, Debug)]
126pub struct ConcatenatePlan {
127 input_dims: crate::inline::OperandVec<AxisVec<usize>>,
128 input_strides: crate::inline::OperandVec<AxisVec<isize>>,
129 dest_dims: AxisVec<usize>,
130 dest_strides: AxisVec<isize>,
131 dest_offset_deltas: crate::inline::OperandVec<isize>,
132 #[cfg_attr(not(feature = "parallel"), allow(dead_code))]
135 segment_starts: crate::inline::OperandVec<usize>,
136 copy_plans: crate::inline::OperandVec<CopyPlan>,
137}
138
139impl SlicePlan {
140 #[allow(clippy::too_many_arguments)]
142 pub fn compile(
143 operand_dims: &[usize],
144 operand_strides: &[isize],
145 dest_dims: &[usize],
146 dest_strides: &[isize],
147 starts: &[usize],
148 limits: &[usize],
149 slice_strides: &[usize],
150 ) -> Result<Self> {
151 let rank = operand_dims.len();
152 if operand_strides.len() != rank || dest_dims.len() != rank || dest_strides.len() != rank {
153 return Err(StridedError::StrideLengthMismatch);
154 }
155 if starts.len() != rank {
156 return Err(StridedError::RankMismatch(starts.len(), rank));
157 }
158 if limits.len() != rank {
159 return Err(StridedError::RankMismatch(limits.len(), rank));
160 }
161 if slice_strides.len() != rank {
162 return Err(StridedError::RankMismatch(slice_strides.len(), rank));
163 }
164 checked_total_len(operand_dims)?;
165 checked_total_len(dest_dims)?;
166
167 let mut expected_dest_dims: AxisVec<usize> = AxisVec::with_capacity(rank);
168 let mut source_strides: AxisVec<isize> = AxisVec::with_capacity(rank);
169 let mut source_offset_delta = 0isize;
170 for axis in 0..rank {
171 let start = starts[axis];
172 let limit = limits[axis];
173 let stride = slice_strides[axis];
174 if start > limit || limit > operand_dims[axis] || stride == 0 {
175 return Err(StridedError::InvalidAxis { axis, rank });
176 }
177 let span = limit - start;
178 expected_dest_dims.push(span.div_ceil(stride));
179 source_strides.push(checked_stride_mul(operand_strides[axis], stride)?);
180 source_offset_delta =
181 checked_offset_add(source_offset_delta, operand_strides[axis], start)?;
182 }
183 if dest_dims != &expected_dest_dims[..] {
184 return Err(StridedError::ShapeMismatch(
185 dest_dims.to_vec(),
186 expected_dest_dims.to_vec(),
187 ));
188 }
189 let copy_plan = CopyPlan::compile(dest_dims, dest_strides, &source_strides)?;
190
191 Ok(Self {
192 operand_dims: operand_dims.into(),
193 operand_strides: operand_strides.into(),
194 dest_dims: dest_dims.into(),
195 dest_strides: dest_strides.into(),
196 source_strides,
197 source_offset_delta,
198 copy_plan,
199 })
200 }
201
202 pub fn execute<T>(
204 &self,
205 dest: &mut RawStridedMut<'_, T>,
206 operand: &RawStridedRef<'_, T>,
207 ) -> Result<()>
208 where
209 T: Copy + MaybeSendSync,
210 {
211 self.check_call(dest, operand)?;
212 let source_offset = operand
213 .offset()
214 .checked_add(self.source_offset_delta)
215 .ok_or(StridedError::OffsetOverflow)?;
216 let source = unsafe {
217 RawStridedRef::new_unchecked(
218 operand.data(),
219 &self.dest_dims,
220 &self.source_strides,
221 source_offset,
222 )
223 };
224 self.copy_plan.execute(dest, &source)
225 }
226
227 pub fn execute_uninit<T>(
229 &self,
230 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
231 operand: &RawStridedRef<'_, T>,
232 ) -> Result<()>
233 where
234 T: Copy + MaybeSendSync,
235 {
236 self.check_call(dest, operand)?;
237 let source_offset = operand
238 .offset()
239 .checked_add(self.source_offset_delta)
240 .ok_or(StridedError::OffsetOverflow)?;
241 let source = unsafe {
242 RawStridedRef::new_unchecked(
243 operand.data(),
244 &self.dest_dims,
245 &self.source_strides,
246 source_offset,
247 )
248 };
249 self.copy_plan.execute_uninit(dest, &source)
250 }
251
252 fn check_call<D, T>(
253 &self,
254 dest: &RawStridedMut<'_, D>,
255 operand: &RawStridedRef<'_, T>,
256 ) -> Result<()> {
257 if operand.dims() != &self.operand_dims[..]
258 || operand.strides() != &self.operand_strides[..]
259 || dest.dims() != &self.dest_dims[..]
260 || dest.strides() != &self.dest_strides[..]
261 {
262 return Err(StridedError::PlanLayoutMismatch);
263 }
264 Ok(())
265 }
266}
267
268impl PadPlan {
269 #[allow(clippy::too_many_arguments)]
271 pub fn compile(
272 operand_dims: &[usize],
273 operand_strides: &[isize],
274 dest_dims: &[usize],
275 dest_strides: &[isize],
276 edge_padding_low: &[i64],
277 edge_padding_high: &[i64],
278 interior_padding: &[i64],
279 ) -> Result<Self> {
280 let rank = operand_dims.len();
281 if operand_strides.len() != rank || dest_dims.len() != rank || dest_strides.len() != rank {
282 return Err(StridedError::StrideLengthMismatch);
283 }
284 if edge_padding_low.len() != rank {
285 return Err(StridedError::RankMismatch(edge_padding_low.len(), rank));
286 }
287 if edge_padding_high.len() != rank {
288 return Err(StridedError::RankMismatch(edge_padding_high.len(), rank));
289 }
290 if interior_padding.len() != rank {
291 return Err(StridedError::RankMismatch(interior_padding.len(), rank));
292 }
293
294 let operand_total = checked_total_len(operand_dims)?;
295 let dest_total = checked_total_len(dest_dims)?;
296 if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
297 return Err(StridedError::NonInjectiveOutputLayout);
298 }
299
300 let mut expected_dest_dims: AxisVec<usize> = AxisVec::with_capacity(rank);
301 let mut interior_step: AxisVec<i64> = AxisVec::with_capacity(rank);
302 for axis in 0..rank {
303 if interior_padding[axis] < 0 {
304 return Err(StridedError::InvalidAxis { axis, rank });
305 }
306 let step = interior_padding[axis]
307 .checked_add(1)
308 .ok_or(StridedError::OffsetOverflow)?;
309 interior_step.push(step);
310 expected_dest_dims.push(checked_pad_output_dim(
311 operand_dims[axis],
312 edge_padding_low[axis],
313 edge_padding_high[axis],
314 step,
315 axis,
316 rank,
317 )?);
318 }
319 if dest_dims != &expected_dest_dims[..] {
320 return Err(StridedError::ShapeMismatch(
321 dest_dims.to_vec(),
322 expected_dest_dims.to_vec(),
323 ));
324 }
325 let contiguous_dest_fill = is_dense_col_major(dest_dims, dest_strides);
326 let contiguous_axis0_run = compile_contiguous_pad_axis0_run(
327 operand_dims,
328 operand_strides,
329 dest_dims,
330 dest_strides,
331 edge_padding_low,
332 &interior_step,
333 );
334 let contiguous_axis0_columns = contiguous_axis0_run.and_then(|run| {
335 compile_contiguous_pad_axis0_columns(
336 operand_dims,
337 operand_strides,
338 dest_dims,
339 edge_padding_low,
340 edge_padding_high,
341 &interior_step,
342 contiguous_dest_fill,
343 run,
344 )
345 });
346 let generic_fill = compile_pad_fill_cursor(dest_dims, dest_strides)?;
347 let generic_copy = compile_pad_copy_cursor(
348 operand_dims,
349 operand_strides,
350 dest_dims,
351 dest_strides,
352 edge_padding_low,
353 &interior_step,
354 )?;
355
356 Ok(Self {
357 operand_dims: operand_dims.into(),
358 operand_strides: operand_strides.into(),
359 dest_dims: dest_dims.into(),
360 dest_strides: dest_strides.into(),
361 edge_padding_low: edge_padding_low.into(),
362 interior_step,
363 operand_total,
364 dest_total,
365 contiguous_dest_fill,
366 contiguous_axis0_run,
367 contiguous_axis0_columns,
368 generic_fill,
369 generic_copy,
370 })
371 }
372
373 pub fn execute<T>(
375 &self,
376 dest: &mut RawStridedMut<'_, T>,
377 operand: &RawStridedRef<'_, T>,
378 fill: T,
379 ) -> Result<()>
380 where
381 T: Copy + MaybeSendSync,
382 {
383 self.check_call(dest, operand)?;
384 if self.operand_total == 0 {
385 return self.fill_dest(dest, fill);
386 }
387 if let Some(columns) = &self.contiguous_axis0_columns {
388 return self.execute_axis0_columns(dest, operand, fill, columns);
389 }
390 self.fill_dest(dest, fill)?;
391 if let Some(run) = self.contiguous_axis0_run {
392 return self.copy_operand_axis0_runs(dest, operand, run);
393 }
394 self.copy_operand(dest, operand)
395 }
396
397 pub fn execute_uninit<T>(
399 &self,
400 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
401 operand: &RawStridedRef<'_, T>,
402 fill: T,
403 ) -> Result<()>
404 where
405 T: Copy + MaybeSendSync,
406 {
407 self.check_call(dest, operand)?;
408 if self.operand_total == 0 {
409 return self.fill_dest(dest, MaybeUninit::new(fill));
410 }
411 if let Some(columns) = &self.contiguous_axis0_columns {
412 return self.execute_axis0_columns_uninit(dest, operand, fill, columns);
413 }
414 self.fill_dest(dest, MaybeUninit::new(fill))?;
415 if let Some(run) = self.contiguous_axis0_run {
416 return self.copy_operand_axis0_runs_uninit(dest, operand, run);
417 }
418 self.copy_operand_uninit(dest, operand)
419 }
420
421 fn fill_dest<T>(&self, dest: &mut RawStridedMut<'_, T>, fill: T) -> Result<()>
422 where
423 T: Copy + MaybeSendSync,
424 {
425 if self.dest_total == 0 {
426 return Ok(());
427 }
428 if self.contiguous_dest_fill {
429 let dest_offset =
430 usize::try_from(dest.offset()).map_err(|_| StridedError::OffsetOverflow)?;
431 let dest_end = dest_offset
432 .checked_add(self.dest_total)
433 .ok_or(StridedError::OffsetOverflow)?;
434 let dest_data = dest.data_mut();
435 let dest_slice = dest_data
436 .get_mut(dest_offset..dest_end)
437 .ok_or(StridedError::OffsetOverflow)?;
438 crate::threading::fill_contiguous(dest_slice, fill);
439 return Ok(());
440 }
441 #[cfg(feature = "parallel")]
442 {
443 let nthreads = crate::threading::parallel_threads_for_len(self.dest_total);
444 if nthreads > 1 {
445 return self.fill_dest_parallel(dest, fill, nthreads);
446 }
447 }
448 self.fill_dest_serial(dest, fill)
449 }
450
451 fn copy_operand_axis0_runs<T>(
452 &self,
453 dest: &mut RawStridedMut<'_, T>,
454 operand: &RawStridedRef<'_, T>,
455 run: ContiguousPadAxis0Run,
456 ) -> Result<()>
457 where
458 T: Copy + MaybeSendSync,
459 {
460 let dest_offset = dest.offset();
461 let dest_ptr = dest.data_mut().as_mut_ptr();
462 unsafe { self.copy_axis0_runs_raw(dest_ptr, dest_offset, operand, run) }
466 }
467
468 fn copy_operand_axis0_runs_uninit<T>(
469 &self,
470 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
471 operand: &RawStridedRef<'_, T>,
472 run: ContiguousPadAxis0Run,
473 ) -> Result<()>
474 where
475 T: Copy + MaybeSendSync,
476 {
477 let dest_offset = dest.offset();
478 let dest_ptr = dest.data_mut().as_mut_ptr().cast::<T>();
479 unsafe { self.copy_axis0_runs_raw(dest_ptr, dest_offset, operand, run) }
482 }
483
484 fn execute_axis0_columns<T>(
485 &self,
486 dest: &mut RawStridedMut<'_, T>,
487 operand: &RawStridedRef<'_, T>,
488 fill: T,
489 columns: &ContiguousPadAxis0Columns,
490 ) -> Result<()>
491 where
492 T: Copy + MaybeSendSync,
493 {
494 let dest_offset = dest.offset();
495 let dest_ptr = dest.data_mut().as_mut_ptr();
496 let operand_offset = operand.offset();
497 let operand_ptr = operand.data().as_ptr();
498 unsafe {
502 self.write_axis0_columns_raw::<T, false>(
503 dest_ptr,
504 dest_offset,
505 operand_ptr,
506 operand_offset,
507 fill,
508 columns,
509 )
510 }
511 }
512
513 fn execute_axis0_columns_uninit<T>(
514 &self,
515 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
516 operand: &RawStridedRef<'_, T>,
517 fill: T,
518 columns: &ContiguousPadAxis0Columns,
519 ) -> Result<()>
520 where
521 T: Copy + MaybeSendSync,
522 {
523 let dest_offset = dest.offset();
524 let dest_ptr = dest.data_mut().as_mut_ptr().cast::<T>();
525 let operand_offset = operand.offset();
526 let operand_ptr = operand.data().as_ptr();
527 unsafe {
530 self.write_axis0_columns_raw::<T, true>(
531 dest_ptr,
532 dest_offset,
533 operand_ptr,
534 operand_offset,
535 fill,
536 columns,
537 )
538 }
539 }
540
541 unsafe fn write_axis0_columns_raw<T, const UNINIT: bool>(
559 &self,
560 dest_ptr: *mut T,
561 dest_offset: isize,
562 operand_ptr: *const T,
563 operand_offset: isize,
564 fill: T,
565 columns: &ContiguousPadAxis0Columns,
566 ) -> Result<()>
567 where
568 T: Copy + MaybeSendSync,
569 {
570 let column_total = columns.column_total;
571 if column_total == 0 {
572 return Ok(());
573 }
574 #[cfg(feature = "parallel")]
575 {
576 let nthreads = crate::threading::parallel_threads_for_len(self.dest_total);
577 if nthreads > 1 && column_total >= nthreads {
578 let dest_ptr = crate::threading::SendPtr(dest_ptr);
579 let operand_ptr = crate::threading::SendPtr(operand_ptr as *mut T);
580 return crate::threading::parallel_map_reduce(
581 0..column_total,
582 nthreads,
583 &|range| {
584 let mut outer_idx_storage =
585 CoordScratch::new(columns.dest_outer_dims.len());
586 let outer_idx = outer_idx_storage.as_mut_slice();
587 fill_col_major_index(range.start, &columns.dest_outer_dims, outer_idx);
588 unsafe {
594 self.write_axis0_columns_range::<T, UNINIT>(
595 dest_ptr.as_ptr(),
596 dest_offset,
597 operand_ptr.as_const(),
598 operand_offset,
599 fill,
600 columns,
601 outer_idx,
602 range.start,
603 range.len(),
604 )
605 }
606 },
607 &|left, right| left.and(right),
608 );
609 }
610 }
611 let mut outer_idx_storage = CoordScratch::new(columns.dest_outer_dims.len());
612 let outer_idx = outer_idx_storage.as_mut_slice();
613 unsafe {
615 self.write_axis0_columns_range::<T, UNINIT>(
616 dest_ptr,
617 dest_offset,
618 operand_ptr,
619 operand_offset,
620 fill,
621 columns,
622 outer_idx,
623 0,
624 column_total,
625 )
626 }
627 }
628
629 #[allow(clippy::too_many_arguments)]
637 unsafe fn write_axis0_columns_range<T, const UNINIT: bool>(
638 &self,
639 dest_ptr: *mut T,
640 dest_offset: isize,
641 operand_ptr: *const T,
642 operand_offset: isize,
643 fill: T,
644 columns: &ContiguousPadAxis0Columns,
645 outer_idx: &mut [usize],
646 column_start: usize,
647 count: usize,
648 ) -> Result<()>
649 where
650 T: Copy + MaybeSendSync,
651 {
652 if count == 0 {
653 return Ok(());
654 }
655 let mut dest_column_offset =
656 checked_offset_add(dest_offset, columns.dest_axis0_step, column_start)?;
657 let mut operand_column_offset = operand_offset;
662 let mut out_of_bounds = 0usize;
663 for (axis, &coord) in outer_idx.iter().enumerate() {
664 match in_bounds_outer_coord(columns, axis, coord) {
665 Some(q) => {
666 operand_column_offset = checked_offset_add(
667 operand_column_offset,
668 columns.operand_outer_strides[axis],
669 q,
670 )?;
671 }
672 None => out_of_bounds += 1,
673 }
674 }
675 for _ in 0..count {
676 if out_of_bounds == 0 {
677 let low_len = columns.low0;
678 let run_len = columns.run_len;
679 let high_len = columns.high0;
680 unsafe {
687 if UNINIT {
688 fill_raw_span_uninit(dest_ptr.offset(dest_column_offset), low_len, fill);
689 } else {
690 fill_raw_span(dest_ptr.offset(dest_column_offset), low_len, fill);
691 }
692 crate::threading::copy_contiguous(
693 operand_ptr.offset(operand_column_offset),
694 dest_ptr.offset(dest_column_offset + low_len as isize),
695 run_len,
696 );
697 if UNINIT {
698 fill_raw_span_uninit(
699 dest_ptr
700 .offset(dest_column_offset + low_len as isize + run_len as isize),
701 high_len,
702 fill,
703 );
704 } else {
705 fill_raw_span(
706 dest_ptr
707 .offset(dest_column_offset + low_len as isize + run_len as isize),
708 high_len,
709 fill,
710 );
711 }
712 }
713 } else {
714 unsafe {
717 if UNINIT {
718 fill_raw_span_uninit(
719 dest_ptr.offset(dest_column_offset),
720 columns.dest_axis0_len,
721 fill,
722 );
723 } else {
724 fill_raw_span(
725 dest_ptr.offset(dest_column_offset),
726 columns.dest_axis0_len,
727 fill,
728 );
729 }
730 }
731 }
732 dest_column_offset = dest_column_offset
735 .checked_add(columns.dest_axis0_step)
736 .ok_or(StridedError::OffsetOverflow)?;
737 for (axis, coord) in outer_idx.iter_mut().enumerate() {
738 let was_in_bounds = in_bounds_outer_coord(columns, axis, *coord);
739 if let Some(q) = was_in_bounds {
740 let q = isize::try_from(q).map_err(|_| StridedError::OffsetOverflow)?;
741 let scaled = columns.operand_outer_strides[axis]
742 .checked_mul(q)
743 .ok_or(StridedError::OffsetOverflow)?;
744 operand_column_offset = operand_column_offset
745 .checked_sub(scaled)
746 .ok_or(StridedError::OffsetOverflow)?;
747 }
748 *coord += 1;
749 let wrapped = *coord >= columns.dest_outer_dims[axis];
750 if wrapped {
751 *coord = 0;
752 }
753 let is_in_bounds = in_bounds_outer_coord(columns, axis, *coord);
754 if let Some(q) = is_in_bounds {
755 operand_column_offset = checked_offset_add(
756 operand_column_offset,
757 columns.operand_outer_strides[axis],
758 q,
759 )?;
760 }
761 match (was_in_bounds.is_some(), is_in_bounds.is_some()) {
762 (false, true) => out_of_bounds -= 1,
763 (true, false) => out_of_bounds += 1,
764 _ => {}
765 }
766 if !wrapped {
767 break;
768 }
769 }
770 }
771 Ok(())
772 }
773
774 unsafe fn copy_axis0_runs_raw<T>(
787 &self,
788 dest_ptr: *mut T,
789 dest_offset: isize,
790 operand: &RawStridedRef<'_, T>,
791 run: ContiguousPadAxis0Run,
792 ) -> Result<()>
793 where
794 T: Copy + MaybeSendSync,
795 {
796 if run.len == 0 {
797 return Ok(());
798 }
799 let outer_dims = &self.operand_dims[1..];
800 let outer_total = checked_total_len(outer_dims)?;
801 #[cfg(feature = "parallel")]
802 {
803 let copied = outer_total.saturating_mul(run.len);
804 let nthreads = crate::threading::parallel_threads_for_len(copied);
805 if nthreads > 1 && outer_total >= nthreads {
806 let dest_ptr = crate::threading::SendPtr(dest_ptr);
807 return crate::threading::parallel_map_reduce(
808 0..outer_total,
809 nthreads,
810 &|range| {
811 let mut outer_idx_storage = CoordScratch::new(outer_dims.len());
812 let outer_idx = outer_idx_storage.as_mut_slice();
813 fill_col_major_index(range.start, outer_dims, outer_idx);
814 unsafe {
818 self.copy_axis0_run_range(
819 dest_ptr.as_ptr(),
820 dest_offset,
821 operand,
822 run,
823 outer_idx,
824 range.len(),
825 )
826 }
827 },
828 &|left, right| left.and(right),
829 );
830 }
831 }
832 let mut outer_idx_storage = CoordScratch::new(outer_dims.len());
833 let outer_idx = outer_idx_storage.as_mut_slice();
834 unsafe {
836 self.copy_axis0_run_range(dest_ptr, dest_offset, operand, run, outer_idx, outer_total)
837 }
838 }
839
840 unsafe fn copy_axis0_run_range<T>(
848 &self,
849 dest_ptr: *mut T,
850 dest_offset: isize,
851 operand: &RawStridedRef<'_, T>,
852 run: ContiguousPadAxis0Run,
853 outer_idx: &mut [usize],
854 count: usize,
855 ) -> Result<()>
856 where
857 T: Copy + MaybeSendSync,
858 {
859 let outer_dims = &self.operand_dims[1..];
860 let operand_ptr = operand.data().as_ptr();
861 for _ in 0..count {
862 let mut operand_offset =
863 checked_offset_add(operand.offset(), self.operand_strides[0], run.operand_start)?;
864 let mut dest_run_offset =
865 checked_offset_add(dest_offset, self.dest_strides[0], run.dest_start)?;
866 let mut in_bounds = true;
867 for (outer_axis, &coord) in outer_idx.iter().enumerate() {
868 let axis = outer_axis + 1;
869 let out_pos = i128::from(self.edge_padding_low[axis])
870 + coord as i128 * i128::from(self.interior_step[axis]);
871 if out_pos < 0 || out_pos >= self.dest_dims[axis] as i128 {
872 in_bounds = false;
873 break;
874 }
875 operand_offset =
876 checked_offset_add(operand_offset, self.operand_strides[axis], coord)?;
877 dest_run_offset =
878 checked_offset_add(dest_run_offset, self.dest_strides[axis], out_pos as usize)?;
879 }
880 if in_bounds {
881 unsafe {
887 crate::threading::copy_contiguous(
888 operand_ptr.offset(operand_offset),
889 dest_ptr.offset(dest_run_offset),
890 run.len,
891 );
892 }
893 }
894 advance_col_major_index(outer_idx, outer_dims);
895 }
896 Ok(())
897 }
898
899 #[cfg(test)]
900 fn contiguous_axis0_run(&self) -> Option<(usize, usize, usize)> {
901 self.contiguous_axis0_run
902 .map(|run| (run.operand_start, run.dest_start, run.len))
903 }
904
905 #[cfg(test)]
906 fn has_contiguous_dest_fill(&self) -> bool {
907 self.contiguous_dest_fill
908 }
909
910 #[cfg(test)]
911 fn has_contiguous_axis0_columns(&self) -> bool {
912 self.contiguous_axis0_columns.is_some()
913 }
914
915 fn fill_dest_serial<T>(&self, dest: &mut RawStridedMut<'_, T>, fill: T) -> Result<()>
916 where
917 T: Copy,
918 {
919 let dest_ptr = dest.data_mut().as_mut_ptr();
920 let mut cursor = PadFillState::new(dest.offset(), &self.dest_dims, &self.generic_fill);
921 for _ in 0..self.dest_total {
922 unsafe {
923 *dest_ptr.offset(cursor.offset) = fill;
927 }
928 cursor.advance(&self.dest_dims, &self.generic_fill);
929 }
930 Ok(())
931 }
932
933 #[cfg(feature = "parallel")]
934 fn fill_dest_parallel<T>(
935 &self,
936 dest: &mut RawStridedMut<'_, T>,
937 fill: T,
938 nthreads: usize,
939 ) -> Result<()>
940 where
941 T: Copy + MaybeSendSync,
942 {
943 let dest_offset_base = dest.offset();
944 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
945 crate::threading::parallel_map_reduce(
946 0..self.dest_total,
947 nthreads,
948 &|range| {
949 let mut cursor = PadFillState::decode(
950 range.start,
951 dest_offset_base,
952 &self.dest_dims,
953 &self.generic_fill,
954 )?;
955 let dest_ptr = dest_ptr.as_ptr();
956 for _ in range {
957 unsafe {
958 *dest_ptr.offset(cursor.offset) = fill;
963 }
964 cursor.advance(&self.dest_dims, &self.generic_fill);
965 }
966 Ok(())
967 },
968 &|left, right| left.and(right),
969 )
970 }
971
972 fn copy_operand<T>(
973 &self,
974 dest: &mut RawStridedMut<'_, T>,
975 operand: &RawStridedRef<'_, T>,
976 ) -> Result<()>
977 where
978 T: Copy + MaybeSendSync,
979 {
980 #[cfg(feature = "parallel")]
981 {
982 let nthreads = crate::threading::parallel_threads_for_len(self.generic_copy.total);
983 if nthreads > 1 {
984 return self.copy_operand_parallel(dest, operand, nthreads);
985 }
986 }
987 self.copy_operand_serial(dest, operand)
988 }
989
990 fn copy_operand_uninit<T>(
991 &self,
992 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
993 operand: &RawStridedRef<'_, T>,
994 ) -> Result<()>
995 where
996 T: Copy + MaybeSendSync,
997 {
998 #[cfg(feature = "parallel")]
999 {
1000 let nthreads = crate::threading::parallel_threads_for_len(self.generic_copy.total);
1001 if nthreads > 1 {
1002 return self.copy_operand_uninit_parallel(dest, operand, nthreads);
1003 }
1004 }
1005 self.copy_operand_uninit_serial(dest, operand)
1006 }
1007
1008 fn copy_operand_uninit_serial<T>(
1009 &self,
1010 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1011 operand: &RawStridedRef<'_, T>,
1012 ) -> Result<()>
1013 where
1014 T: Copy,
1015 {
1016 if self.generic_copy.total == 0 {
1017 return Ok(());
1018 }
1019 let operand_ptr = operand.data().as_ptr();
1020 let dest_ptr = dest.data_mut().as_mut_ptr();
1021 let source_base = operand
1022 .offset()
1023 .checked_add(self.generic_copy.source_base_delta)
1024 .ok_or(StridedError::OffsetOverflow)?;
1025 let dest_base = dest
1026 .offset()
1027 .checked_add(self.generic_copy.dest_base_delta)
1028 .ok_or(StridedError::OffsetOverflow)?;
1029 let mut cursor = PadCopyState::new(source_base, dest_base, &self.generic_copy);
1030 for _ in 0..self.generic_copy.total {
1031 unsafe {
1032 (*dest_ptr.offset(cursor.dest_offset))
1037 .write(*operand_ptr.offset(cursor.source_offset));
1038 }
1039 cursor.advance(&self.generic_copy);
1040 }
1041 Ok(())
1042 }
1043
1044 #[cfg(feature = "parallel")]
1045 fn copy_operand_uninit_parallel<T>(
1046 &self,
1047 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1048 operand: &RawStridedRef<'_, T>,
1049 nthreads: usize,
1050 ) -> Result<()>
1051 where
1052 T: Copy + MaybeSendSync,
1053 {
1054 let operand_base = operand
1055 .offset()
1056 .checked_add(self.generic_copy.source_base_delta)
1057 .ok_or(StridedError::OffsetOverflow)?;
1058 let operand_ptr = crate::threading::SendPtr(operand.data().as_ptr() as *mut T);
1059 let dest_base = dest
1060 .offset()
1061 .checked_add(self.generic_copy.dest_base_delta)
1062 .ok_or(StridedError::OffsetOverflow)?;
1063 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
1064 crate::threading::parallel_map_reduce(
1065 0..self.generic_copy.total,
1066 nthreads,
1067 &|range| {
1068 let mut cursor =
1069 PadCopyState::decode(range.start, operand_base, dest_base, &self.generic_copy)?;
1070 let operand_ptr = operand_ptr.as_const();
1071 let dest_ptr = dest_ptr.as_ptr();
1072
1073 for _ in range {
1074 unsafe {
1075 (*dest_ptr.offset(cursor.dest_offset))
1080 .write(*operand_ptr.offset(cursor.source_offset));
1081 }
1082 cursor.advance(&self.generic_copy);
1083 }
1084 Ok(())
1085 },
1086 &|left, right| left.and(right),
1087 )
1088 }
1089
1090 fn copy_operand_serial<T>(
1091 &self,
1092 dest: &mut RawStridedMut<'_, T>,
1093 operand: &RawStridedRef<'_, T>,
1094 ) -> Result<()>
1095 where
1096 T: Copy,
1097 {
1098 if self.generic_copy.total == 0 {
1099 return Ok(());
1100 }
1101 let operand_ptr = operand.data().as_ptr();
1102 let dest_ptr = dest.data_mut().as_mut_ptr();
1103 let source_base = operand
1104 .offset()
1105 .checked_add(self.generic_copy.source_base_delta)
1106 .ok_or(StridedError::OffsetOverflow)?;
1107 let dest_base = dest
1108 .offset()
1109 .checked_add(self.generic_copy.dest_base_delta)
1110 .ok_or(StridedError::OffsetOverflow)?;
1111 let mut cursor = PadCopyState::new(source_base, dest_base, &self.generic_copy);
1112 for _ in 0..self.generic_copy.total {
1113 unsafe {
1114 *dest_ptr.offset(cursor.dest_offset) = *operand_ptr.offset(cursor.source_offset);
1119 }
1120 cursor.advance(&self.generic_copy);
1121 }
1122 Ok(())
1123 }
1124
1125 #[cfg(feature = "parallel")]
1126 fn copy_operand_parallel<T>(
1127 &self,
1128 dest: &mut RawStridedMut<'_, T>,
1129 operand: &RawStridedRef<'_, T>,
1130 nthreads: usize,
1131 ) -> Result<()>
1132 where
1133 T: Copy + MaybeSendSync,
1134 {
1135 let operand_base = operand
1136 .offset()
1137 .checked_add(self.generic_copy.source_base_delta)
1138 .ok_or(StridedError::OffsetOverflow)?;
1139 let operand_ptr = crate::threading::SendPtr(operand.data().as_ptr() as *mut T);
1140 let dest_base = dest
1141 .offset()
1142 .checked_add(self.generic_copy.dest_base_delta)
1143 .ok_or(StridedError::OffsetOverflow)?;
1144 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
1145 crate::threading::parallel_map_reduce(
1146 0..self.generic_copy.total,
1147 nthreads,
1148 &|range| {
1149 let mut cursor =
1150 PadCopyState::decode(range.start, operand_base, dest_base, &self.generic_copy)?;
1151 let operand_ptr = operand_ptr.as_const();
1152 let dest_ptr = dest_ptr.as_ptr();
1153
1154 for _ in range {
1155 unsafe {
1156 *dest_ptr.offset(cursor.dest_offset) =
1161 *operand_ptr.offset(cursor.source_offset);
1162 }
1163 cursor.advance(&self.generic_copy);
1164 }
1165 Ok(())
1166 },
1167 &|left, right| left.and(right),
1168 )
1169 }
1170
1171 fn check_call<D, T>(
1172 &self,
1173 dest: &RawStridedMut<'_, D>,
1174 operand: &RawStridedRef<'_, T>,
1175 ) -> Result<()> {
1176 if operand.dims() != &self.operand_dims[..]
1177 || operand.strides() != &self.operand_strides[..]
1178 || dest.dims() != &self.dest_dims[..]
1179 || dest.strides() != &self.dest_strides[..]
1180 {
1181 return Err(StridedError::PlanLayoutMismatch);
1182 }
1183 Ok(())
1184 }
1185}
1186
1187impl ConcatenatePlan {
1188 pub fn compile(
1190 input_dims: &[&[usize]],
1191 input_strides: &[&[isize]],
1192 dest_dims: &[usize],
1193 dest_strides: &[isize],
1194 axis: usize,
1195 ) -> Result<Self> {
1196 if input_dims.is_empty() {
1197 return Err(StridedError::UnsupportedArity {
1198 arity: 0,
1199 max: usize::MAX,
1200 });
1201 }
1202 if input_dims.len() != input_strides.len() {
1203 return Err(StridedError::RankMismatch(
1204 input_strides.len(),
1205 input_dims.len(),
1206 ));
1207 }
1208
1209 let rank = input_dims[0].len();
1210 if dest_dims.len() != rank || dest_strides.len() != rank {
1211 return Err(StridedError::StrideLengthMismatch);
1212 }
1213 if axis >= rank {
1214 return Err(StridedError::InvalidAxis { axis, rank });
1215 }
1216 checked_total_len(dest_dims)?;
1217 if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
1218 return Err(StridedError::NonInjectiveOutputLayout);
1219 }
1220
1221 let mut expected_dest_dims: AxisVec<usize> = input_dims[0].into();
1222 expected_dest_dims[axis] = 0;
1223 let mut stored_input_dims = crate::inline::OperandVec::with_capacity(input_dims.len());
1224 let mut stored_input_strides = crate::inline::OperandVec::with_capacity(input_dims.len());
1225 let mut dest_offset_deltas = crate::inline::OperandVec::with_capacity(input_dims.len());
1226 let mut copy_plans = crate::inline::OperandVec::with_capacity(input_dims.len());
1227 let mut segment_starts = crate::inline::OperandVec::with_capacity(input_dims.len() + 1);
1228 segment_starts.push(0usize);
1229 let mut axis_base = 0usize;
1230
1231 for (dims, strides) in input_dims.iter().zip(input_strides.iter()) {
1232 if dims.len() != rank {
1233 return Err(StridedError::RankMismatch(dims.len(), rank));
1234 }
1235 if strides.len() != rank {
1236 return Err(StridedError::StrideLengthMismatch);
1237 }
1238 let segment_total = checked_total_len(dims)?;
1239 let segment_end = segment_starts[segment_starts.len() - 1]
1240 .checked_add(segment_total)
1241 .ok_or(StridedError::OffsetOverflow)?;
1242 segment_starts.push(segment_end);
1243 for dim in 0..rank {
1244 if dim == axis {
1245 expected_dest_dims[axis] = expected_dest_dims[axis]
1246 .checked_add(dims[axis])
1247 .ok_or(StridedError::OffsetOverflow)?;
1248 } else if dims[dim] != input_dims[0][dim] {
1249 return Err(StridedError::ShapeMismatch(
1250 dims.to_vec(),
1251 input_dims[0].to_vec(),
1252 ));
1253 }
1254 }
1255 dest_offset_deltas.push(checked_offset_add(0, dest_strides[axis], axis_base)?);
1256 axis_base = axis_base
1257 .checked_add(dims[axis])
1258 .ok_or(StridedError::OffsetOverflow)?;
1259 copy_plans.push(CopyPlan::compile(dims, dest_strides, strides)?);
1260 stored_input_dims.push((*dims).into());
1261 stored_input_strides.push((*strides).into());
1262 }
1263
1264 if dest_dims != &expected_dest_dims[..] {
1265 return Err(StridedError::ShapeMismatch(
1266 dest_dims.to_vec(),
1267 expected_dest_dims.to_vec(),
1268 ));
1269 }
1270
1271 Ok(Self {
1272 input_dims: stored_input_dims,
1273 input_strides: stored_input_strides,
1274 dest_dims: dest_dims.into(),
1275 dest_strides: dest_strides.into(),
1276 dest_offset_deltas,
1277 segment_starts,
1278 copy_plans,
1279 })
1280 }
1281
1282 pub fn execute<T>(
1284 &self,
1285 dest: &mut RawStridedMut<'_, T>,
1286 inputs: &[RawStridedRef<'_, T>],
1287 ) -> Result<()>
1288 where
1289 T: Copy + MaybeSendSync,
1290 {
1291 self.check_dest_layout(dest)?;
1292 if inputs.len() != self.input_dims.len() {
1293 return Err(StridedError::RankMismatch(
1294 inputs.len(),
1295 self.input_dims.len(),
1296 ));
1297 }
1298 for (position, input) in inputs.iter().enumerate() {
1299 self.check_input_layout(position, input)?;
1300 self.segment_offset(position, dest.offset())?;
1301 }
1302 #[cfg(feature = "parallel")]
1303 if self.try_execute_parallel(dest, inputs, |dst: &mut T, value| *dst = value)? {
1304 return Ok(());
1305 }
1306 for (position, input) in inputs.iter().enumerate() {
1307 self.execute_segment(position, dest, input)?;
1308 }
1309 Ok(())
1310 }
1311
1312 pub fn execute_uninit<T>(
1314 &self,
1315 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1316 inputs: &[RawStridedRef<'_, T>],
1317 ) -> Result<()>
1318 where
1319 T: Copy + MaybeSendSync,
1320 {
1321 self.check_dest_layout(dest)?;
1322 if inputs.len() != self.input_dims.len() {
1323 return Err(StridedError::RankMismatch(
1324 inputs.len(),
1325 self.input_dims.len(),
1326 ));
1327 }
1328 for (position, input) in inputs.iter().enumerate() {
1329 self.check_input_layout(position, input)?;
1330 self.segment_offset(position, dest.offset())?;
1331 }
1332 #[cfg(feature = "parallel")]
1333 if self.try_execute_parallel(dest, inputs, |dst: &mut MaybeUninit<T>, value| {
1334 dst.write(value);
1335 })? {
1336 return Ok(());
1337 }
1338 for (position, input) in inputs.iter().enumerate() {
1339 self.execute_segment_uninit(position, dest, input)?;
1340 }
1341 Ok(())
1342 }
1343
1344 #[cfg(feature = "parallel")]
1355 fn try_execute_parallel<D, T, Apply>(
1356 &self,
1357 dest: &mut RawStridedMut<'_, D>,
1358 inputs: &[RawStridedRef<'_, T>],
1359 apply: Apply,
1360 ) -> Result<bool>
1361 where
1362 D: Copy + MaybeSendSync,
1363 T: Copy + MaybeSendSync,
1364 Apply: Fn(&mut D, T) + MaybeSendSync,
1365 {
1366 let total = self.segment_starts[self.segment_starts.len() - 1];
1367 let nthreads = crate::threading::parallel_threads_for_len(total);
1368 if nthreads <= 1 {
1369 return Ok(false);
1370 }
1371 let mut dest_offsets: crate::inline::OperandVec<isize> =
1376 crate::inline::OperandVec::with_capacity(inputs.len());
1377 for position in 0..inputs.len() {
1378 if self.copy_plans[position].fused_layout().is_none() {
1379 return Ok(false);
1380 }
1381 dest_offsets.push(self.segment_offset(position, dest.offset())?);
1382 }
1383 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
1384 let starts = &self.segment_starts;
1385 let dest_offsets = &dest_offsets;
1386 crate::threading::parallel_for_each(0..total, nthreads, &|range| {
1387 let mut position = starts[1..].partition_point(|&end| end <= range.start);
1389 while position < inputs.len() && starts[position] < range.end {
1390 let local_start = range.start.max(starts[position]) - starts[position];
1391 let local_end = range.end.min(starts[position + 1]) - starts[position];
1392 let Some(layout) = self.copy_plans[position].fused_layout() else {
1396 position += 1;
1397 continue;
1398 };
1399 let input = &inputs[position];
1400 unsafe {
1410 crate::raw_ops::apply_fused_range(
1411 dest_ptr.as_ptr(),
1412 dest_offsets[position],
1413 input.data().as_ptr(),
1414 input.offset(),
1415 layout,
1416 local_start,
1417 local_end - local_start,
1418 &apply,
1419 &|value| value,
1420 );
1421 }
1422 position += 1;
1423 }
1424 });
1425 Ok(true)
1426 }
1427
1428 pub(crate) fn check_dest_layout<T>(&self, dest: &RawStridedMut<'_, T>) -> Result<()> {
1429 if dest.dims() != &self.dest_dims[..] || dest.strides() != &self.dest_strides[..] {
1430 return Err(StridedError::PlanLayoutMismatch);
1431 }
1432 Ok(())
1433 }
1434
1435 pub(crate) fn check_input_layout<T>(
1436 &self,
1437 position: usize,
1438 input: &RawStridedRef<'_, T>,
1439 ) -> Result<()> {
1440 if position >= self.input_dims.len()
1441 || input.dims() != &self.input_dims[position][..]
1442 || input.strides() != &self.input_strides[position][..]
1443 {
1444 return Err(StridedError::PlanLayoutMismatch);
1445 }
1446 Ok(())
1447 }
1448
1449 pub(crate) fn input_count(&self) -> usize {
1450 self.input_dims.len()
1451 }
1452
1453 pub(crate) fn prefers_whole_plan(&self) -> bool {
1457 #[cfg(feature = "parallel")]
1458 {
1459 let total = self.segment_starts[self.segment_starts.len() - 1];
1460 crate::threading::parallel_threads_for_len(total) > 1
1461 }
1462 #[cfg(not(feature = "parallel"))]
1463 {
1464 false
1465 }
1466 }
1467
1468 pub(crate) fn segment_offset(&self, position: usize, dest_offset: isize) -> Result<isize> {
1469 dest_offset
1470 .checked_add(self.dest_offset_deltas[position])
1471 .ok_or(StridedError::OffsetOverflow)
1472 }
1473
1474 pub(crate) fn execute_segment<T>(
1475 &self,
1476 position: usize,
1477 dest: &mut RawStridedMut<'_, T>,
1478 input: &RawStridedRef<'_, T>,
1479 ) -> Result<()>
1480 where
1481 T: Copy + MaybeSendSync,
1482 {
1483 let segment_offset = self.segment_offset(position, dest.offset())?;
1484 let dest_data = dest.data_mut();
1485 let mut segment = unsafe {
1486 RawStridedMut::new_unchecked(
1487 dest_data,
1488 &self.input_dims[position],
1489 &self.dest_strides,
1490 segment_offset,
1491 )
1492 };
1493 self.copy_plans[position].execute(&mut segment, input)
1494 }
1495
1496 pub(crate) fn execute_segment_uninit<T>(
1497 &self,
1498 position: usize,
1499 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1500 input: &RawStridedRef<'_, T>,
1501 ) -> Result<()>
1502 where
1503 T: Copy + MaybeSendSync,
1504 {
1505 let segment_offset = self.segment_offset(position, dest.offset())?;
1506 let dest_data = dest.data_mut();
1507 let mut segment = unsafe {
1508 RawStridedMut::new_unchecked(
1509 dest_data,
1510 &self.input_dims[position],
1511 &self.dest_strides,
1512 segment_offset,
1513 )
1514 };
1515 self.copy_plans[position].execute_uninit(&mut segment, input)
1516 }
1517}
1518
1519impl ReversePlan {
1520 pub fn compile(
1522 operand_dims: &[usize],
1523 operand_strides: &[isize],
1524 dest_strides: &[isize],
1525 axes: &[usize],
1526 ) -> Result<Self> {
1527 let rank = operand_dims.len();
1528 if operand_strides.len() != rank || dest_strides.len() != rank {
1529 return Err(StridedError::StrideLengthMismatch);
1530 }
1531 checked_total_len(operand_dims)?;
1532
1533 let mut reverse_axis: AxisVec<bool> = (0..rank).map(|_| false).collect();
1534 for &axis in axes {
1535 if axis >= rank {
1536 return Err(StridedError::InvalidAxis { axis, rank });
1537 }
1538 reverse_axis[axis] = true;
1539 }
1540
1541 let mut source_strides: AxisVec<isize> = AxisVec::with_capacity(rank);
1542 let mut source_offset_delta = 0isize;
1543 for axis in 0..rank {
1544 if reverse_axis[axis] {
1545 source_strides.push(
1546 operand_strides[axis]
1547 .checked_neg()
1548 .ok_or(StridedError::OffsetOverflow)?,
1549 );
1550 if operand_dims[axis] > 0 {
1551 source_offset_delta = checked_offset_add(
1552 source_offset_delta,
1553 operand_strides[axis],
1554 operand_dims[axis] - 1,
1555 )?;
1556 }
1557 } else {
1558 source_strides.push(operand_strides[axis]);
1559 }
1560 }
1561 let copy_plan = CopyPlan::compile(operand_dims, dest_strides, &source_strides)?;
1562
1563 Ok(Self {
1564 operand_dims: operand_dims.into(),
1565 operand_strides: operand_strides.into(),
1566 dest_strides: dest_strides.into(),
1567 source_strides,
1568 source_offset_delta,
1569 copy_plan,
1570 })
1571 }
1572
1573 pub fn execute<T>(
1575 &self,
1576 dest: &mut RawStridedMut<'_, T>,
1577 operand: &RawStridedRef<'_, T>,
1578 ) -> Result<()>
1579 where
1580 T: Copy + MaybeSendSync,
1581 {
1582 self.check_call(dest, operand)?;
1583 let source_offset = operand
1584 .offset()
1585 .checked_add(self.source_offset_delta)
1586 .ok_or(StridedError::OffsetOverflow)?;
1587 let source = unsafe {
1588 RawStridedRef::new_unchecked(
1589 operand.data(),
1590 &self.operand_dims,
1591 &self.source_strides,
1592 source_offset,
1593 )
1594 };
1595 self.copy_plan.execute(dest, &source)
1596 }
1597
1598 pub fn execute_uninit<T>(
1600 &self,
1601 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1602 operand: &RawStridedRef<'_, T>,
1603 ) -> Result<()>
1604 where
1605 T: Copy + MaybeSendSync,
1606 {
1607 self.check_call(dest, operand)?;
1608 let source_offset = operand
1609 .offset()
1610 .checked_add(self.source_offset_delta)
1611 .ok_or(StridedError::OffsetOverflow)?;
1612 let source = unsafe {
1613 RawStridedRef::new_unchecked(
1614 operand.data(),
1615 &self.operand_dims,
1616 &self.source_strides,
1617 source_offset,
1618 )
1619 };
1620 self.copy_plan.execute_uninit(dest, &source)
1621 }
1622
1623 fn check_call<D, T>(
1624 &self,
1625 dest: &RawStridedMut<'_, D>,
1626 operand: &RawStridedRef<'_, T>,
1627 ) -> Result<()> {
1628 if operand.dims() != &self.operand_dims[..]
1629 || operand.strides() != &self.operand_strides[..]
1630 || dest.dims() != &self.operand_dims[..]
1631 || dest.strides() != &self.dest_strides[..]
1632 {
1633 return Err(StridedError::PlanLayoutMismatch);
1634 }
1635 Ok(())
1636 }
1637}
1638
1639fn checked_total_len(dims: &[usize]) -> Result<usize> {
1640 if dims.is_empty() {
1641 return Ok(1);
1642 }
1643 dims.iter()
1644 .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))
1645 .ok_or(StridedError::OffsetOverflow)
1646}
1647
1648fn checked_stride_mul(stride: isize, factor: usize) -> Result<isize> {
1649 let factor = isize::try_from(factor).map_err(|_| StridedError::OffsetOverflow)?;
1650 stride
1651 .checked_mul(factor)
1652 .ok_or(StridedError::OffsetOverflow)
1653}
1654
1655fn checked_pad_output_dim(
1656 input_extent: usize,
1657 edge_low: i64,
1658 edge_high: i64,
1659 interior_step: i64,
1660 axis: usize,
1661 rank: usize,
1662) -> Result<usize> {
1663 let base = if input_extent == 0 {
1664 0i128
1665 } else {
1666 (input_extent as i128 - 1)
1667 .checked_mul(i128::from(interior_step))
1668 .and_then(|value| value.checked_add(1))
1669 .ok_or(StridedError::OffsetOverflow)?
1670 };
1671 let dim = i128::from(edge_low)
1672 .checked_add(i128::from(edge_high))
1673 .and_then(|value| value.checked_add(base))
1674 .ok_or(StridedError::OffsetOverflow)?;
1675 usize::try_from(dim).map_err(|_| StridedError::InvalidAxis { axis, rank })
1676}
1677
1678fn compile_pad_fill_cursor(dims: &[usize], strides: &[isize]) -> Result<PadFillCursor> {
1679 let mut steps = AxisVec::with_capacity(dims.len());
1680 let mut resets = AxisVec::with_capacity(dims.len());
1681 for (&dim, &stride) in dims.iter().zip(strides) {
1682 steps.push(stride);
1683 resets.push(checked_cursor_reset(stride, dim)?);
1684 }
1685 check_offset_span(dims, &steps)?;
1686 Ok(PadFillCursor { steps, resets })
1687}
1688
1689fn compile_pad_copy_cursor(
1690 operand_dims: &[usize],
1691 operand_strides: &[isize],
1692 dest_dims: &[usize],
1693 dest_strides: &[isize],
1694 edge_padding_low: &[i64],
1695 interior_step: &[i64],
1696) -> Result<PadCopyCursor> {
1697 let mut shape = AxisVec::with_capacity(operand_dims.len());
1698 let mut source_steps = AxisVec::with_capacity(operand_dims.len());
1699 let mut source_resets = AxisVec::with_capacity(operand_dims.len());
1700 let mut dest_steps = AxisVec::with_capacity(operand_dims.len());
1701 let mut dest_resets = AxisVec::with_capacity(operand_dims.len());
1702 let mut source_base_delta = 0isize;
1703 let mut dest_base_delta = 0isize;
1704 let mut copy_empty = false;
1705
1706 for axis in 0..operand_dims.len() {
1707 let (start, end) = checked_pad_valid_interval(
1708 operand_dims[axis],
1709 dest_dims[axis],
1710 edge_padding_low[axis],
1711 interior_step[axis],
1712 )?;
1713 let extent = end - start;
1714 shape.push(extent);
1715 if !copy_empty && extent != 0 {
1716 source_base_delta =
1717 checked_offset_add(source_base_delta, operand_strides[axis], start)?;
1718 let output_start = i128::from(edge_padding_low[axis])
1719 .checked_add(
1720 i128::try_from(start)
1721 .map_err(|_| StridedError::OffsetOverflow)?
1722 .checked_mul(i128::from(interior_step[axis]))
1723 .ok_or(StridedError::OffsetOverflow)?,
1724 )
1725 .ok_or(StridedError::OffsetOverflow)?;
1726 let output_start =
1727 usize::try_from(output_start).map_err(|_| StridedError::OffsetOverflow)?;
1728 dest_base_delta =
1729 checked_offset_add(dest_base_delta, dest_strides[axis], output_start)?;
1730 }
1731 copy_empty |= extent == 0;
1732
1733 let source_step = operand_strides[axis];
1734 let dest_step = checked_stride_mul_i64(dest_strides[axis], interior_step[axis])?;
1735 source_steps.push(source_step);
1736 source_resets.push(checked_cursor_reset(source_step, extent)?);
1737 dest_steps.push(dest_step);
1738 dest_resets.push(checked_cursor_reset(dest_step, extent)?);
1739 }
1740
1741 check_offset_span(&shape, &source_steps)?;
1742 check_offset_span(&shape, &dest_steps)?;
1743 let total = if shape.iter().any(|&extent| extent == 0) {
1744 0
1745 } else {
1746 checked_total_len(&shape)?
1747 };
1748
1749 Ok(PadCopyCursor {
1750 shape,
1751 source_base_delta,
1752 dest_base_delta,
1753 source_steps,
1754 source_resets,
1755 dest_steps,
1756 dest_resets,
1757 total,
1758 })
1759}
1760
1761fn checked_pad_valid_interval(
1762 input_extent: usize,
1763 dest_extent: usize,
1764 edge_low: i64,
1765 step: i64,
1766) -> Result<(usize, usize)> {
1767 if input_extent == 0 || dest_extent == 0 {
1768 return Ok((0, 0));
1769 }
1770 let step = i128::from(step);
1771 let lower = ceil_div_positive(-i128::from(edge_low), step)?;
1772 let dest_last = i128::try_from(dest_extent)
1773 .map_err(|_| StridedError::OffsetOverflow)?
1774 .checked_sub(1)
1775 .ok_or(StridedError::OffsetOverflow)?;
1776 let upper = floor_div_positive(
1777 dest_last
1778 .checked_sub(i128::from(edge_low))
1779 .ok_or(StridedError::OffsetOverflow)?,
1780 step,
1781 )?
1782 .checked_add(1)
1783 .ok_or(StridedError::OffsetOverflow)?;
1784 let input_extent = i128::try_from(input_extent).map_err(|_| StridedError::OffsetOverflow)?;
1785 let lower = lower.clamp(0, input_extent);
1786 let upper = upper.clamp(0, input_extent);
1787 if lower >= upper {
1788 return Ok((0, 0));
1789 }
1790 Ok((
1791 usize::try_from(lower).map_err(|_| StridedError::OffsetOverflow)?,
1792 usize::try_from(upper).map_err(|_| StridedError::OffsetOverflow)?,
1793 ))
1794}
1795
1796fn ceil_div_positive(numerator: i128, denominator: i128) -> Result<i128> {
1797 if denominator <= 0 {
1798 return Err(StridedError::OffsetOverflow);
1799 }
1800 let quotient = numerator.div_euclid(denominator);
1801 let remainder = numerator.rem_euclid(denominator);
1802 quotient
1803 .checked_add(i128::from(remainder != 0))
1804 .ok_or(StridedError::OffsetOverflow)
1805}
1806
1807fn floor_div_positive(numerator: i128, denominator: i128) -> Result<i128> {
1808 if denominator <= 0 {
1809 return Err(StridedError::OffsetOverflow);
1810 }
1811 Ok(numerator.div_euclid(denominator))
1812}
1813
1814fn checked_stride_mul_i64(stride: isize, factor: i64) -> Result<isize> {
1815 let factor = isize::try_from(factor).map_err(|_| StridedError::OffsetOverflow)?;
1816 stride
1817 .checked_mul(factor)
1818 .ok_or(StridedError::OffsetOverflow)
1819}
1820
1821fn checked_cursor_reset(step: isize, extent: usize) -> Result<isize> {
1822 if extent == 0 {
1823 return Ok(0);
1824 }
1825 let last = isize::try_from(extent - 1).map_err(|_| StridedError::OffsetOverflow)?;
1826 step.checked_mul(last)
1827 .and_then(isize::checked_neg)
1828 .ok_or(StridedError::OffsetOverflow)
1829}
1830
1831fn check_offset_span(shape: &[usize], strides: &[isize]) -> Result<()> {
1832 let mut min = 0isize;
1833 let mut max = 0isize;
1834 for (&extent, &stride) in shape.iter().zip(strides) {
1835 if extent <= 1 {
1836 continue;
1837 }
1838 let last = isize::try_from(extent - 1).map_err(|_| StridedError::OffsetOverflow)?;
1839 let delta = stride
1840 .checked_mul(last)
1841 .ok_or(StridedError::OffsetOverflow)?;
1842 if delta < 0 {
1843 min = min.checked_add(delta).ok_or(StridedError::OffsetOverflow)?;
1844 } else {
1845 max = max.checked_add(delta).ok_or(StridedError::OffsetOverflow)?;
1846 }
1847 }
1848 let _ = (min, max);
1849 Ok(())
1850}
1851
1852fn is_dense_col_major(dims: &[usize], strides: &[isize]) -> bool {
1853 let mut expected = 1isize;
1854 for (&dim, &stride) in dims.iter().zip(strides.iter()) {
1855 if stride != expected {
1856 return false;
1857 }
1858 let Ok(dim) = isize::try_from(dim) else {
1859 return false;
1860 };
1861 let Some(next) = expected.checked_mul(dim) else {
1862 return false;
1863 };
1864 expected = next;
1865 }
1866 true
1867}
1868
1869fn compile_contiguous_pad_axis0_run(
1870 operand_dims: &[usize],
1871 operand_strides: &[isize],
1872 dest_dims: &[usize],
1873 dest_strides: &[isize],
1874 edge_padding_low: &[i64],
1875 interior_step: &[i64],
1876) -> Option<ContiguousPadAxis0Run> {
1877 if operand_dims.is_empty()
1878 || operand_strides[0] != 1
1879 || dest_strides[0] != 1
1880 || interior_step[0] != 1
1881 {
1882 return None;
1883 }
1884
1885 let operand_extent = operand_dims[0] as i128;
1886 let dest_extent = dest_dims[0] as i128;
1887 let edge_low = i128::from(edge_padding_low[0]);
1888 let operand_start = (-edge_low).clamp(0, operand_extent);
1889 let dest_start = edge_low.clamp(0, dest_extent);
1890 let len = (operand_extent - operand_start).min(dest_extent - dest_start);
1891 Some(ContiguousPadAxis0Run {
1892 operand_start: usize::try_from(operand_start).ok()?,
1893 dest_start: usize::try_from(dest_start).ok()?,
1894 len: usize::try_from(len).ok()?,
1895 })
1896}
1897
1898#[allow(clippy::too_many_arguments)]
1901fn compile_contiguous_pad_axis0_columns(
1902 operand_dims: &[usize],
1903 operand_strides: &[isize],
1904 dest_dims: &[usize],
1905 edge_padding_low: &[i64],
1906 edge_padding_high: &[i64],
1907 interior_step: &[i64],
1908 contiguous_dest_fill: bool,
1909 run: ContiguousPadAxis0Run,
1910) -> Option<ContiguousPadAxis0Columns> {
1911 if run.len == 0 || !contiguous_dest_fill || dest_dims.is_empty() {
1912 return None;
1913 }
1914 if edge_padding_low.iter().any(|&low| low < 0)
1915 || edge_padding_high.iter().any(|&high| high < 0)
1916 || interior_step.iter().any(|&step| step != 1)
1917 {
1918 return None;
1919 }
1920
1921 let rank = operand_dims.len();
1922 let dest_axis0_len = dest_dims[0];
1923 let dest_axis0_step = isize::try_from(dest_axis0_len).ok()?;
1924 let column_total = checked_total_len(&dest_dims[1..]).ok()?;
1925 let mut operand_outer_dims = AxisVec::with_capacity(rank - 1);
1926 let mut operand_outer_strides = AxisVec::with_capacity(rank - 1);
1927 let mut dest_outer_dims = AxisVec::with_capacity(rank - 1);
1928 let mut edge_low_outer = AxisVec::with_capacity(rank - 1);
1929 for axis in 1..rank {
1930 operand_outer_dims.push(operand_dims[axis]);
1931 operand_outer_strides.push(operand_strides[axis]);
1932 dest_outer_dims.push(dest_dims[axis]);
1933 edge_low_outer.push(usize::try_from(edge_padding_low[axis]).ok()?);
1934 }
1935
1936 let low0 = run.dest_start;
1937 let high0 = dest_axis0_len.checked_sub(low0)?.checked_sub(run.len)?;
1938 Some(ContiguousPadAxis0Columns {
1939 column_total,
1940 dest_axis0_len,
1941 dest_axis0_step,
1942 low0,
1943 high0,
1944 run_len: run.len,
1945 operand_outer_dims,
1946 operand_outer_strides,
1947 dest_outer_dims,
1948 edge_low_outer,
1949 })
1950}
1951
1952unsafe fn fill_raw_span<T: Copy + MaybeSendSync>(ptr: *mut T, len: usize, fill: T) {
1960 if len == 0 {
1961 return;
1962 }
1963 let span = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
1966 crate::threading::fill_contiguous(span, fill);
1967}
1968
1969unsafe fn fill_raw_span_uninit<T: Copy + MaybeSendSync>(ptr: *mut T, len: usize, fill: T) {
1976 if len == 0 {
1977 return;
1978 }
1979 unsafe { crate::threading::fill_contiguous_uninit(ptr, len, fill) };
1982}
1983
1984fn checked_offset_add(base: isize, stride: isize, coord: usize) -> Result<isize> {
1985 let coord = isize::try_from(coord).map_err(|_| StridedError::OffsetOverflow)?;
1986 let scaled = stride
1987 .checked_mul(coord)
1988 .ok_or(StridedError::OffsetOverflow)?;
1989 base.checked_add(scaled).ok_or(StridedError::OffsetOverflow)
1990}
1991
1992fn in_bounds_outer_coord(
1995 columns: &ContiguousPadAxis0Columns,
1996 axis: usize,
1997 coord: usize,
1998) -> Option<usize> {
1999 let q = coord.checked_sub(columns.edge_low_outer[axis])?;
2000 (q < columns.operand_outer_dims[axis]).then_some(q)
2001}
2002
2003fn advance_col_major_index(index: &mut [usize], shape: &[usize]) {
2004 for axis in 0..index.len() {
2005 index[axis] += 1;
2006 if index[axis] < shape[axis] {
2007 return;
2008 }
2009 index[axis] = 0;
2010 }
2011}
2012
2013#[cfg(feature = "parallel")]
2014fn fill_col_major_index(mut linear: usize, shape: &[usize], out: &mut [usize]) {
2015 for (axis, coord) in out.iter_mut().enumerate() {
2016 let dim = shape[axis];
2017 *coord = linear % dim;
2018 linear /= dim;
2019 }
2020}
2021
2022struct PadFillState {
2023 coords: CoordScratch,
2024 offset: isize,
2025}
2026
2027impl PadFillState {
2028 fn new(base: isize, shape: &[usize], _cursor: &PadFillCursor) -> Self {
2029 Self {
2030 coords: CoordScratch::new(shape.len()),
2031 offset: base,
2032 }
2033 }
2034
2035 #[cfg(feature = "parallel")]
2036 fn decode(linear: usize, base: isize, shape: &[usize], cursor: &PadFillCursor) -> Result<Self> {
2037 let mut state = Self::new(base, shape, cursor);
2038 fill_col_major_index(linear, shape, state.coords.as_mut_slice());
2039 for (&coord, &step) in state.coords.as_mut_slice().iter().zip(&cursor.steps) {
2040 state.offset = checked_offset_add(state.offset, step, coord)?;
2041 }
2042 Ok(state)
2043 }
2044
2045 #[inline]
2046 fn advance(&mut self, shape: &[usize], cursor: &PadFillCursor) {
2047 for axis in 0..shape.len() {
2051 let next = self.coords.as_mut_slice()[axis] + 1;
2052 if next < shape[axis] {
2053 self.coords.as_mut_slice()[axis] = next;
2054 self.offset += cursor.steps[axis];
2055 return;
2056 }
2057 self.coords.as_mut_slice()[axis] = 0;
2058 self.offset += cursor.resets[axis];
2059 }
2060 }
2061}
2062
2063struct PadCopyState {
2064 coords: CoordScratch,
2065 source_offset: isize,
2066 dest_offset: isize,
2067}
2068
2069impl PadCopyState {
2070 fn new(source_base: isize, dest_base: isize, cursor: &PadCopyCursor) -> Self {
2071 Self {
2072 coords: CoordScratch::new(cursor.shape.len()),
2073 source_offset: source_base,
2074 dest_offset: dest_base,
2075 }
2076 }
2077
2078 #[cfg(feature = "parallel")]
2079 fn decode(
2080 linear: usize,
2081 source_base: isize,
2082 dest_base: isize,
2083 cursor: &PadCopyCursor,
2084 ) -> Result<Self> {
2085 let mut state = Self::new(source_base, dest_base, cursor);
2086 fill_col_major_index(linear, &cursor.shape, state.coords.as_mut_slice());
2087 for axis in 0..cursor.shape.len() {
2088 let coord = state.coords.as_mut_slice()[axis];
2089 state.source_offset =
2090 checked_offset_add(state.source_offset, cursor.source_steps[axis], coord)?;
2091 state.dest_offset =
2092 checked_offset_add(state.dest_offset, cursor.dest_steps[axis], coord)?;
2093 }
2094 Ok(state)
2095 }
2096
2097 #[inline]
2098 fn advance(&mut self, cursor: &PadCopyCursor) {
2099 for axis in 0..cursor.shape.len() {
2103 let next = self.coords.as_mut_slice()[axis] + 1;
2104 if next < cursor.shape[axis] {
2105 self.coords.as_mut_slice()[axis] = next;
2106 self.source_offset += cursor.source_steps[axis];
2107 self.dest_offset += cursor.dest_steps[axis];
2108 return;
2109 }
2110 self.coords.as_mut_slice()[axis] = 0;
2111 self.source_offset += cursor.source_resets[axis];
2112 self.dest_offset += cursor.dest_resets[axis];
2113 }
2114 }
2115}
2116
2117struct CoordScratch {
2118 inline: [usize; crate::RAW_FUSED_RANK_LIMIT],
2119 heap: Option<Vec<usize>>,
2120 len: usize,
2121}
2122
2123impl CoordScratch {
2124 fn new(len: usize) -> Self {
2125 if len <= crate::RAW_FUSED_RANK_LIMIT {
2126 Self {
2127 inline: [0; crate::RAW_FUSED_RANK_LIMIT],
2128 heap: None,
2129 len,
2130 }
2131 } else {
2132 Self {
2133 inline: [0; crate::RAW_FUSED_RANK_LIMIT],
2134 heap: Some(vec![0; len]),
2135 len,
2136 }
2137 }
2138 }
2139
2140 fn as_mut_slice(&mut self) -> &mut [usize] {
2141 match &mut self.heap {
2142 Some(heap) => heap,
2143 None => &mut self.inline[..self.len],
2144 }
2145 }
2146}
2147
2148#[cfg(test)]
2149#[path = "static_indexing_plan/tests/tests.rs"]
2150mod tests;
2151
2152#[cfg(all(test, feature = "parallel"))]
2153#[path = "static_indexing_plan/tests/parallel_tests.rs"]
2154mod parallel_tests;