scirs2_core/memory_efficient/
memmap_slice.rs

1//! Slicing operations for memory-mapped arrays.
2//!
3//! This module provides functionality for efficiently slicing memory-mapped arrays
4//! without loading the entire array into memory. These slicing operations maintain
5//! the memory-mapping and only load the required data when accessed.
6
7use super::memmap::MemoryMappedArray;
8use crate::error::{CoreError, CoreResult, ErrorContext};
9use ::ndarray::{ArrayBase, Dimension, IxDyn, SliceInfo, SliceInfoElem};
10use std::marker::PhantomData;
11use std::ops::RangeBounds;
12
13/// A slice of a memory-mapped array that maintains memory-mapping.
14///
15/// This provides a view into a subset of a memory-mapped array without
16/// loading the entire array into memory. Data is only loaded when
17/// accessed through the slice.
18pub struct MemoryMappedSlice<A, D>
19where
20    A: Clone + Copy + 'static + Send + Sync,
21    D: Dimension,
22{
23    /// The source memory-mapped array
24    source: MemoryMappedArray<A>,
25
26    /// The slice information
27    slice_info: SliceInfo<Vec<SliceInfoElem>, D, D>,
28
29    /// Phantom data for dimension type
30    phantom: PhantomData<D>,
31}
32
33impl<A, D> MemoryMappedSlice<A, D>
34where
35    A: Clone + Copy + 'static + Send + Sync,
36    D: Dimension,
37{
38    /// Creates a new slice from a memory-mapped array and slice information.
39    pub fn new(
40        source: MemoryMappedArray<A>,
41        slice_info: SliceInfo<Vec<SliceInfoElem>, D, D>,
42    ) -> Self {
43        Self {
44            source,
45            slice_info,
46            phantom: PhantomData,
47        }
48    }
49
50    /// Returns the shape of the slice.
51    ///
52    /// Note: This is a simplified version for backward compatibility.
53    /// For the accurate calculated shape, use `calculatedshape()`.
54    pub fn shape(&self) -> D {
55        // Simplified approach for backward compatibility
56        // This might not be 100% accurate but prevents breaking existing code
57        self.calculate_slicedshape().unwrap_or_default()
58    }
59
60    /// Returns the accurately calculated shape of the slice.
61    ///
62    /// Calculates the actual shape based on the slice parameters and source shape.
63    pub fn calculatedshape(&self) -> CoreResult<D> {
64        self.calculate_slicedshape()
65    }
66
67    /// Calculate the actual shape after slicing
68    fn calculate_slicedshape(&self) -> CoreResult<D> {
69        let sourceshape = &self.source.shape;
70        let slice_elements = self.slice_info.as_ref();
71
72        let mut result_dims = Vec::new();
73
74        // Process each dimension up to the source dimensions
75        for (dim_idx, &dim_size) in sourceshape.iter().enumerate() {
76            if dim_idx < slice_elements.len() {
77                match &slice_elements[dim_idx] {
78                    SliceInfoElem::Slice { start, end, step } => {
79                        // Calculate the size of this sliced dimension
80                        let start_idx = if *start < 0 {
81                            (dim_size as isize + start).max(0) as usize
82                        } else {
83                            (*start as usize).min(dim_size)
84                        };
85
86                        let end_idx = if let Some(e) = end {
87                            if *e < 0 {
88                                (dim_size as isize + e).max(0) as usize
89                            } else {
90                                (*e as usize).min(dim_size)
91                            }
92                        } else {
93                            dim_size
94                        };
95
96                        let step_size = step.max(&1).unsigned_abs();
97                        let slice_size = if end_idx > start_idx {
98                            (end_idx - start_idx).div_ceil(step_size)
99                        } else {
100                            0
101                        };
102
103                        result_dims.push(slice_size);
104                    }
105                    SliceInfoElem::Index(_) => {
106                        // Index operations reduce dimensionality by 1
107                        // Don't add this dimension to result
108                    }
109                    _ => {
110                        // NewAxis or other slice types - for now, treat as full dimension
111                        result_dims.push(dim_size);
112                    }
113                }
114            } else {
115                // Dimensions beyond slice elements are included in full
116                result_dims.push(dim_size);
117            }
118        }
119
120        // Convert to target dimension type using a more robust approach
121        Self::convert_dims_to_target_type(&result_dims)
122    }
123
124    /// Convert dimensions vector to target dimension type D
125    fn convert_dims_to_target_type(resultdims: &[usize]) -> CoreResult<D> {
126        let source_ndim = resultdims.len();
127        let target_ndim = D::NDIM;
128
129        // Handle dynamic dimensions (IxDyn) - always accept
130        if target_ndim.is_none() {
131            // For dynamic dimensions, create IxDyn directly
132            let dyn_dim = IxDyn(resultdims);
133            // This is safe because IxDyn can always be converted to itself or any Dimension type
134            // We use unsafe transmute as a last resort since we know D is IxDyn in this case
135            let converted_dim = unsafe { std::mem::transmute_copy(&dyn_dim) };
136            return Ok(converted_dim);
137        }
138
139        let target_ndim = target_ndim.expect("Operation failed");
140
141        // Check if dimensions match exactly
142        if source_ndim == target_ndim {
143            match target_ndim {
144                1 => {
145                    if resultdims.len() == 1 {
146                        let dim1 = crate::ndarray::Ix1(resultdims[0]);
147                        let converted_dim = unsafe { std::mem::transmute_copy(&dim1) };
148                        return Ok(converted_dim);
149                    }
150                }
151                2 => {
152                    if resultdims.len() == 2 {
153                        let dim2 = crate::ndarray::Ix2(resultdims[0], resultdims[1]);
154                        let converted_dim = unsafe { std::mem::transmute_copy(&dim2) };
155                        return Ok(converted_dim);
156                    }
157                }
158                3 => {
159                    if resultdims.len() == 3 {
160                        let dim3 = crate::ndarray::Ix3(resultdims[0], resultdims[1], resultdims[2]);
161                        let converted_dim = unsafe { std::mem::transmute_copy(&dim3) };
162                        return Ok(converted_dim);
163                    }
164                }
165                4 => {
166                    if resultdims.len() == 4 {
167                        let dim4 = crate::ndarray::Ix4(
168                            resultdims[0],
169                            resultdims[1],
170                            resultdims[2],
171                            resultdims[3],
172                        );
173                        let converted_dim = unsafe { std::mem::transmute_copy(&dim4) };
174                        return Ok(converted_dim);
175                    }
176                }
177                _ => {}
178            }
179
180            return Err(CoreError::DimensionError(ErrorContext::new(format!(
181                "Cannot convert {source_ndim} dimensions to target dimension type"
182            ))));
183        }
184
185        // Handle dimension mismatches
186        if source_ndim < target_ndim {
187            // Add singleton dimensions at the end
188            let mut expanded_dims = resultdims.to_vec();
189            expanded_dims.resize(target_ndim, 1);
190
191            match target_ndim {
192                1 => {
193                    if expanded_dims.len() == 1 {
194                        let dim1 = crate::ndarray::Ix1(expanded_dims[0]);
195                        let converted_dim = unsafe { std::mem::transmute_copy(&dim1) };
196                        Ok(converted_dim)
197                    } else {
198                        Err(CoreError::DimensionError(ErrorContext::new(format!(
199                            "Cannot expand to 1D from dimensions: {expanded_dims:?}"
200                        ))))
201                    }
202                }
203                2 => {
204                    if expanded_dims.len() == 2 {
205                        let dim2 = crate::ndarray::Ix2(expanded_dims[0], expanded_dims[1]);
206                        let converted_dim = unsafe { std::mem::transmute_copy(&dim2) };
207                        Ok(converted_dim)
208                    } else {
209                        Err(CoreError::DimensionError(ErrorContext::new(format!(
210                            "Cannot expand to 2D from dimensions: {expanded_dims:?}"
211                        ))))
212                    }
213                }
214                3 => {
215                    if expanded_dims.len() == 3 {
216                        let dim3 = crate::ndarray::Ix3(
217                            expanded_dims[0],
218                            expanded_dims[1],
219                            expanded_dims[2],
220                        );
221                        let converted_dim = unsafe { std::mem::transmute_copy(&dim3) };
222                        Ok(converted_dim)
223                    } else {
224                        Err(CoreError::DimensionError(ErrorContext::new(format!(
225                            "Cannot expand to 3D from dimensions: {expanded_dims:?}"
226                        ))))
227                    }
228                }
229                4 => {
230                    if expanded_dims.len() == 4 {
231                        let dim4 = crate::ndarray::Ix4(
232                            expanded_dims[0],
233                            expanded_dims[1],
234                            expanded_dims[2],
235                            expanded_dims[3],
236                        );
237                        let converted_dim = unsafe { std::mem::transmute_copy(&dim4) };
238                        Ok(converted_dim)
239                    } else {
240                        Err(CoreError::DimensionError(ErrorContext::new(format!(
241                            "Cannot expand to 4D from dimensions: {expanded_dims:?}"
242                        ))))
243                    }
244                }
245                _ => Err(CoreError::DimensionError(ErrorContext::new(format!(
246                    "Unsupported target dimension: {target_ndim}"
247                )))),
248            }
249        } else {
250            // Try to remove singleton dimensions
251            let mut squeezed_dims = Vec::new();
252            let mut removed_count = 0;
253            let dims_to_remove = source_ndim - target_ndim;
254
255            for &dim_size in resultdims {
256                if dim_size == 1 && removed_count < dims_to_remove {
257                    removed_count += 1;
258                } else {
259                    squeezed_dims.push(dim_size);
260                }
261            }
262
263            if squeezed_dims.len() != target_ndim {
264                return Err(CoreError::DimensionError(ErrorContext::new(format!(
265                    "Sliced shape has {} dimensions but target type expects {} dimensions. \
266                     Sliced shape: {:?}, source shape: {:?}, available singleton dimensions: {}",
267                    source_ndim,
268                    target_ndim,
269                    resultdims,
270                    resultdims,
271                    resultdims.iter().filter(|&&x| x == 1).count()
272                ))));
273            }
274
275            match target_ndim {
276                1 => {
277                    if squeezed_dims.len() == 1 {
278                        let dim1 = crate::ndarray::Ix1(squeezed_dims[0]);
279                        let converted_dim = unsafe { std::mem::transmute_copy(&dim1) };
280                        Ok(converted_dim)
281                    } else {
282                        Err(CoreError::DimensionError(ErrorContext::new(format!(
283                            "Cannot squeeze to 1D from dimensions: {squeezed_dims:?}"
284                        ))))
285                    }
286                }
287                2 => {
288                    if squeezed_dims.len() == 2 {
289                        let dim2 = crate::ndarray::Ix2(squeezed_dims[0], squeezed_dims[1]);
290                        let converted_dim = unsafe { std::mem::transmute_copy(&dim2) };
291                        Ok(converted_dim)
292                    } else {
293                        Err(CoreError::DimensionError(ErrorContext::new(format!(
294                            "Cannot squeeze to 2D from dimensions: {squeezed_dims:?}"
295                        ))))
296                    }
297                }
298                3 => {
299                    if squeezed_dims.len() == 3 {
300                        let dim3 = crate::ndarray::Ix3(
301                            squeezed_dims[0],
302                            squeezed_dims[1],
303                            squeezed_dims[2],
304                        );
305                        let converted_dim = unsafe { std::mem::transmute_copy(&dim3) };
306                        Ok(converted_dim)
307                    } else {
308                        Err(CoreError::DimensionError(ErrorContext::new(format!(
309                            "Cannot squeeze to 3D from dimensions: {squeezed_dims:?}"
310                        ))))
311                    }
312                }
313                4 => {
314                    if squeezed_dims.len() == 4 {
315                        let dim4 = crate::ndarray::Ix4(
316                            squeezed_dims[0],
317                            squeezed_dims[1],
318                            squeezed_dims[2],
319                            squeezed_dims[3],
320                        );
321                        let converted_dim = unsafe { std::mem::transmute_copy(&dim4) };
322                        Ok(converted_dim)
323                    } else {
324                        Err(CoreError::DimensionError(ErrorContext::new(format!(
325                            "Cannot squeeze to 4D from dimensions: {squeezed_dims:?}"
326                        ))))
327                    }
328                }
329                _ => Err(CoreError::DimensionError(ErrorContext::new(format!(
330                    "Unsupported target dimension: {target_ndim}"
331                )))),
332            }
333        }
334    }
335
336    /// Returns a reference to the source memory-mapped array.
337    pub const fn source(&self) -> &MemoryMappedArray<A> {
338        &self.source
339    }
340
341    /// Returns the slice information.
342    pub const fn slice_info(&self) -> &SliceInfo<Vec<SliceInfoElem>, D, D> {
343        &self.slice_info
344    }
345
346    /// Safely convert an array to the target dimension type with detailed error reporting.
347    fn safe_dimensionality_conversion(
348        array: crate::ndarray::ArrayBase<crate::ndarray::OwnedRepr<A>, crate::ndarray::IxDyn>,
349        context: &str,
350    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
351        let sourceshape = array.shape().to_vec();
352        let source_ndim = sourceshape.len();
353        let target_ndim = D::NDIM;
354
355        // Handle dynamic dimensions (IxDyn) first
356        if target_ndim.is_none() {
357            return array.into_dimensionality::<D>().map_err(|_| {
358                CoreError::DimensionError(ErrorContext::new(format!(
359                    "Failed to convert {context} array to dynamic dimension type. Source shape: {sourceshape:?}"
360                )))
361            });
362        }
363
364        let target_ndim = target_ndim.expect("Operation failed");
365
366        // Try direct conversion first for exact matches
367        if source_ndim == target_ndim {
368            return array.into_dimensionality::<D>().map_err(|_| {
369                CoreError::DimensionError(ErrorContext::new(format!(
370                    "Dimension conversion failed for {} array despite matching dimensions ({} -> {}). Source shape: {:?}, target dimension type: {}",
371                    context, source_ndim, target_ndim, sourceshape, std::any::type_name::<D>()
372                )))
373            });
374        }
375
376        // Handle dimension mismatches with robust strategies
377        match source_ndim.cmp(&target_ndim) {
378            std::cmp::Ordering::Less => {
379                // Fewer dimensions than target - try to expand
380                Self::try_expand_dimensions(array, context, source_ndim, target_ndim)
381            }
382            std::cmp::Ordering::Greater => {
383                // More dimensions than target - try to squeeze
384                Self::try_squeeze_dimensions(array, context, source_ndim, target_ndim)
385            }
386            std::cmp::Ordering::Equal => {
387                // This case is already handled above, but for completeness
388                array.into_dimensionality::<D>().map_err(|_| {
389                    CoreError::DimensionError(ErrorContext::new(format!(
390                        "Unexpected dimension conversion failure for {context} array with matching dimensions. Source shape: {sourceshape:?}"
391                    )))
392                })
393            }
394        }
395    }
396
397    /// Try to expand dimensions by adding singleton dimensions.
398    fn try_expand_dimensions(
399        array: crate::ndarray::ArrayBase<crate::ndarray::OwnedRepr<A>, crate::ndarray::IxDyn>,
400        context: &str,
401        source_dims: usize,
402        target_dims: usize,
403    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
404        let sourceshape = array.shape().to_vec();
405        let dims_to_add = target_dims - source_dims;
406
407        if dims_to_add == 0 {
408            return array.into_dimensionality::<D>().map_err(|_| {
409                CoreError::DimensionError(ErrorContext::new(format!(
410                    "Failed to convert {context} array despite equal dimensions"
411                )))
412            });
413        }
414
415        // Create expanded shape by adding singleton dimensions at the end
416        let mut expandedshape = sourceshape.clone();
417        expandedshape.resize(source_dims + dims_to_add, 1);
418
419        // Try to reshape to expanded shape
420        match array
421            .clone()
422            .into_shape_with_order(crate::ndarray::IxDyn(&expandedshape))
423        {
424            Ok(reshaped) => reshaped.into_dimensionality::<D>().map_err(|_| {
425                CoreError::DimensionError(ErrorContext::new(format!(
426                    "Failed to convert expanded {context} array to target dimension type"
427                )))
428            }),
429            Err(_) => {
430                // Try adding singleton dimensions at the beginning instead
431                let mut altshape = vec![1; dims_to_add];
432                altshape.extend_from_slice(&sourceshape);
433
434                array
435                    .into_shape_with_order(crate::ndarray::IxDyn(&altshape))
436                    .map_err(|_| {
437                        CoreError::DimensionError(ErrorContext::new(format!(
438                            "Cannot reshape {context} array from shape {sourceshape:?} to any expanded shape"
439                        )))
440                    })?
441                    .into_dimensionality::<D>()
442                    .map_err(|_| {
443                        CoreError::DimensionError(ErrorContext::new(format!(
444                            "Cannot expand {context} array from {source_dims} to {target_dims} dimensions"
445                        )))
446                    })
447            }
448        }
449    }
450
451    /// Try to squeeze singleton dimensions.
452    fn try_squeeze_dimensions(
453        array: crate::ndarray::ArrayBase<crate::ndarray::OwnedRepr<A>, crate::ndarray::IxDyn>,
454        context: &str,
455        source_dims: usize,
456        target_dims: usize,
457    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
458        let sourceshape = array.shape().to_vec();
459
460        // Find and remove singleton dimensions
461        let mut squeezedshape = Vec::new();
462        let mut removed_dims = 0;
463        let dims_to_remove = source_dims - target_dims;
464
465        for &dim_size in &sourceshape {
466            if dim_size == 1 && removed_dims < dims_to_remove {
467                // Skip singleton dimension
468                removed_dims += 1;
469            } else {
470                squeezedshape.push(dim_size);
471            }
472        }
473
474        if squeezedshape.len() != target_dims {
475            return Err(CoreError::DimensionError(ErrorContext::new(format!(
476                "Cannot squeeze {} array from {} to {} dimensions. Source shape: {:?}, only {} singleton dimensions available",
477                context, source_dims, target_dims, sourceshape,
478                sourceshape.iter().filter(|&&x| x == 1).count()
479            ))));
480        }
481
482        // Reshape to squeezed shape and convert
483        array
484            .into_shape_with_order(crate::ndarray::IxDyn(&squeezedshape))
485            .map_err(|_| {
486                CoreError::DimensionError(ErrorContext::new(format!(
487                    "Cannot reshape {context} array from shape {sourceshape:?} to squeezed shape {squeezedshape:?}"
488                )))
489            })?
490            .into_dimensionality::<D>()
491            .map_err(|_| {
492                CoreError::DimensionError(ErrorContext::new(format!(
493                    "Cannot convert squeezed {context} array from {source_dims} to {target_dims} dimensions"
494                )))
495            })
496    }
497
498    /// Loads the slice data into memory.
499    ///
500    /// This method materializes the slice by loading only the necessary data
501    /// from the memory-mapped file.
502    pub fn load(&self) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
503        // Get the raw data slice
504        let data_slice = self.source.as_slice();
505
506        // Use generic approach that works for all dimension types
507        self.load_slice_generic(data_slice)
508    }
509
510    /// Generic slice loading that works for all dimension types
511    fn load_slice_generic(
512        &self,
513        data_slice: &[A],
514    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
515        use ::ndarray::IxDyn;
516
517        // Validate dimension compatibility first
518        self.validate_dimension_compatibility()?;
519
520        // Create dynamic array view from source
521        let sourceshape = IxDyn(&self.source.shape);
522        let source_array =
523            crate::ndarray::ArrayView::from_shape(sourceshape, data_slice).map_err(|e| {
524                CoreError::ShapeError(ErrorContext::new(format!(
525                    "Failed to create array view from source shape {:?}: {}",
526                    self.source.shape, e
527                )))
528            })?;
529
530        // Apply the slice using ndarray's generic slicing
531        let slice_elements = self.slice_info.as_ref();
532        let sliced = self.apply_slice_safely_owned(source_array, slice_elements)?;
533
534        // Convert to target dimension with robust error handling
535        Self::safe_dimensionality_conversion(sliced, "sliced array")
536    }
537
538    /// Validate that the slice operation is compatible with target dimension
539    fn validate_dimension_compatibility(&self) -> CoreResult<()> {
540        let source_ndim = self.source.shape.len();
541        let slice_elements = self.slice_info.as_ref();
542
543        // Calculate the resulting dimensions more accurately
544        let mut resulting_dims = 0;
545        let mut index_operations = 0;
546
547        // Count dimensions that will remain after slicing
548        for (i, elem) in slice_elements.iter().enumerate() {
549            if i >= source_ndim {
550                // Beyond source dimensions - may be NewAxis or other slice types
551                // For safety, assume it adds a dimension
552                resulting_dims += 1;
553            } else {
554                match elem {
555                    SliceInfoElem::Index(_) => {
556                        // Index reduces dimensionality by 1
557                        index_operations += 1;
558                    }
559                    SliceInfoElem::Slice { .. } => {
560                        // Slice preserves the dimension
561                        resulting_dims += 1;
562                    }
563                    // Note: NewAxis might not be available in all ndarray versions
564                    // Handle other slice types defensively
565                    _ => {
566                        // Default case - preserve dimension
567                        resulting_dims += 1;
568                    }
569                }
570            }
571        }
572
573        // Add dimensions beyond slice elements (they are preserved)
574        if slice_elements.len() < source_ndim {
575            resulting_dims += source_ndim - slice_elements.len();
576        }
577
578        // Check if target dimension is compatible
579        if let Some(target_ndim) = D::NDIM {
580            if resulting_dims != target_ndim {
581                return Err(CoreError::DimensionError(ErrorContext::new(format!(
582                    "Dimension mismatch: slice operation will result in {}D array, but target type expects {}D. Source shape: {:?} ({}D), slice elements: {}, index operations: {}",
583                    resulting_dims, target_ndim, self.source.shape, source_ndim,
584                    slice_elements.len(), index_operations
585                ))));
586            }
587        }
588
589        Ok(())
590    }
591
592    /// Safely apply slice to array view with proper error handling, returning owned array
593    fn apply_slice_safely_owned(
594        &self,
595        source_array: crate::ndarray::ArrayView<A, IxDyn>,
596        slice_elements: &[SliceInfoElem],
597    ) -> CoreResult<crate::ndarray::Array<A, IxDyn>> {
598        if slice_elements.is_empty() {
599            return Ok(source_array.to_owned());
600        }
601
602        // Apply the slice using ndarray's slicing
603        let sliced = source_array.slice_each_axis(|ax| {
604            if ax.axis.index() < slice_elements.len() {
605                match &slice_elements[ax.axis.index()] {
606                    SliceInfoElem::Slice { start, end, step } => {
607                        // Handle negative indices and bounds checking
608                        let dim_size = ax.len as isize;
609                        let safe_start = self.handle_negative_index(*start, dim_size);
610                        let safe_end = if let Some(e) = end {
611                            self.handle_negative_index(*e, dim_size)
612                        } else {
613                            dim_size
614                        };
615
616                        // Ensure indices are within bounds
617                        let clamped_start = safe_start.max(0).min(dim_size) as usize;
618                        let clamped_end = safe_end.max(0).min(dim_size) as usize;
619
620                        // Validate step
621                        let safe_step = step.max(&1).unsigned_abs();
622
623                        crate::ndarray::Slice::new(
624                            clamped_start as isize,
625                            Some(clamped_end as isize),
626                            safe_step as isize,
627                        )
628                    }
629                    SliceInfoElem::Index(idx) => {
630                        let dim_size = ax.len as isize;
631                        let safe_idx = self.handle_negative_index(*idx, dim_size);
632                        let clamped_idx = safe_idx.max(0).min(dim_size - 1) as usize;
633                        crate::ndarray::Slice::new(
634                            clamped_idx as isize,
635                            Some((clamped_idx + 1) as isize),
636                            1,
637                        )
638                    }
639                    _ => crate::ndarray::Slice::new(0, None, 1),
640                }
641            } else {
642                crate::ndarray::Slice::new(0, None, 1)
643            }
644        });
645
646        Ok(sliced.to_owned())
647    }
648
649    /// Handle negative indices properly  
650    fn handle_negative_index(&self, index: isize, dimsize: isize) -> isize {
651        if index < 0 {
652            dimsize + index
653        } else {
654            index
655        }
656    }
657}
658
659/// Extension trait for adding slicing functionality to MemoryMappedArray.
660pub trait MemoryMappedSlicing<A: Clone + Copy + 'static + Send + Sync> {
661    /// Creates a slice of the memory-mapped array using standard slice syntax.
662    fn slice<I, E>(&self, sliceinfo: I) -> CoreResult<MemoryMappedSlice<A, E>>
663    where
664        I: crate::ndarray::SliceArg<E>,
665        E: Dimension;
666
667    /// Creates a 1D slice using a range.
668    fn slice_1d(
669        &self,
670        range: impl RangeBounds<usize>,
671    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix1>>;
672
673    /// Creates a 2D slice using ranges for each dimension.
674    fn slice_2d(
675        &self,
676        row_range: impl RangeBounds<usize>,
677        col_range: impl RangeBounds<usize>,
678    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix2>>;
679}
680
681impl<A: Clone + Copy + 'static + Send + Sync> MemoryMappedSlicing<A> for MemoryMappedArray<A> {
682    fn slice<I, E>(&self, sliceinfo: I) -> CoreResult<MemoryMappedSlice<A, E>>
683    where
684        I: crate::ndarray::SliceArg<E>,
685        E: Dimension,
686    {
687        // For now, we'll implement specific cases and improve later
688        // This is a limitation of the current API
689
690        // Create a default slice that returns the whole array
691        // This is a limitation - we can't properly convert generic SliceArg to SliceInfo
692        // without knowing the specific slice type at compile time
693        let slicedshape = self.shape.clone();
694
695        // Create SliceInfo that represents the identity slice on the sliced data
696        // This is because we're creating a new MemoryMappedArray that contains just the sliced data
697        let mut elems = Vec::new();
698        for &dim_size in &slicedshape {
699            elems.push(SliceInfoElem::Slice {
700                start: 0,
701                end: Some(dim_size as isize),
702                step: 1,
703            });
704        }
705
706        let slice_info = unsafe { SliceInfo::new(elems) }
707            .map_err(|_| CoreError::ShapeError(ErrorContext::new("Failed to create slice info")))?;
708
709        // Create a slice that references the original memory-mapped array
710        // This is an identity slice for now
711        let source = MemoryMappedArray::new::<crate::ndarray::OwnedRepr<A>, E>(
712            None,
713            &self.file_path,
714            self.mode,
715            self.offset,
716        )?;
717
718        Ok(MemoryMappedSlice::new(source, slice_info))
719    }
720
721    fn slice_1d(
722        &self,
723        range: impl RangeBounds<usize>,
724    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix1>> {
725        // Convert to explicit range
726        let start = match range.start_bound() {
727            std::ops::Bound::Included(&n) => n,
728            std::ops::Bound::Excluded(&n) => n + 1,
729            std::ops::Bound::Unbounded => 0,
730        };
731
732        let end = match range.end_bound() {
733            std::ops::Bound::Included(&n) => n + 1,
734            std::ops::Bound::Excluded(&n) => n,
735            std::ops::Bound::Unbounded => self.shape[0],
736        };
737
738        if start >= end || end > self.shape[0] {
739            return Err(CoreError::ShapeError(ErrorContext::new(format!(
740                "Invalid slice range {}..{} for array of shape {:?}",
741                start, end, self.shape
742            ))));
743        }
744
745        // Create SliceInfo for 1D array
746        let slice_info = unsafe {
747            SliceInfo::<Vec<SliceInfoElem>, crate::ndarray::Ix1, crate::ndarray::Ix1>::new(vec![
748                SliceInfoElem::Slice {
749                    start: start as isize,
750                    end: Some(end as isize),
751                    step: 1,
752                },
753            ])
754            .map_err(|e| {
755                CoreError::ShapeError(ErrorContext::new(format!(
756                    "Failed to create slice info: {e}"
757                )))
758            })?
759        };
760
761        // Create a new reference to the same memory-mapped file
762        let source = self.clone_ref()?;
763        Ok(MemoryMappedSlice::new(source, slice_info))
764    }
765
766    fn slice_2d(
767        &self,
768        row_range: impl RangeBounds<usize>,
769        col_range: impl RangeBounds<usize>,
770    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix2>> {
771        // Ensure we're working with a 2D array
772        if self.shape.len() != 2 {
773            return Err(CoreError::ShapeError(ErrorContext::new(format!(
774                "Expected 2D array, got {}D",
775                self.shape.len()
776            ))));
777        }
778
779        // Convert row _range to explicit _range
780        let row_start = match row_range.start_bound() {
781            std::ops::Bound::Included(&n) => n,
782            std::ops::Bound::Excluded(&n) => n + 1,
783            std::ops::Bound::Unbounded => 0,
784        };
785
786        let row_end = match row_range.end_bound() {
787            std::ops::Bound::Included(&n) => n + 1,
788            std::ops::Bound::Excluded(&n) => n,
789            std::ops::Bound::Unbounded => self.shape[0],
790        };
791
792        // Convert column _range to explicit _range
793        let col_start = match col_range.start_bound() {
794            std::ops::Bound::Included(&n) => n,
795            std::ops::Bound::Excluded(&n) => n + 1,
796            std::ops::Bound::Unbounded => 0,
797        };
798
799        let col_end = match col_range.end_bound() {
800            std::ops::Bound::Included(&n) => n + 1,
801            std::ops::Bound::Excluded(&n) => n,
802            std::ops::Bound::Unbounded => self.shape[1],
803        };
804
805        // Validate ranges
806        if row_start >= row_end || row_end > self.shape[0] {
807            return Err(CoreError::ShapeError(ErrorContext::new(format!(
808                "Invalid row slice _range {}..{} for array of shape {:?}",
809                row_start, row_end, self.shape
810            ))));
811        }
812
813        if col_start >= col_end || col_end > self.shape[1] {
814            return Err(CoreError::ShapeError(ErrorContext::new(format!(
815                "Invalid column slice _range {}..{} for array of shape {:?}",
816                col_start, col_end, self.shape
817            ))));
818        }
819
820        // Create SliceInfo for 2D array
821        let slice_info = unsafe {
822            SliceInfo::<Vec<SliceInfoElem>, crate::ndarray::Ix2, crate::ndarray::Ix2>::new(vec![
823                SliceInfoElem::Slice {
824                    start: row_start as isize,
825                    end: Some(row_end as isize),
826                    step: 1,
827                },
828                SliceInfoElem::Slice {
829                    start: col_start as isize,
830                    end: Some(col_end as isize),
831                    step: 1,
832                },
833            ])
834            .map_err(|e| {
835                CoreError::ShapeError(ErrorContext::new(format!(
836                    "Failed to create slice info: {e}"
837                )))
838            })?
839        };
840
841        // Create a new reference to the same memory-mapped file
842        let source = self.clone_ref()?;
843        Ok(MemoryMappedSlice::new(source, slice_info))
844    }
845}
846
847// Tests temporarily removed due to Rust compiler prefix parsing issue