1use super::validation;
10use crate::error::{CoreError, CoreResult, ErrorContext, ErrorLocation};
11use ::ndarray::{Array, ArrayBase, Data, Dimension, IxDyn};
12use ::serde::{Deserialize, Serialize};
13use bincode::{config, serde};
14use memmap2::{Mmap, MmapMut, MmapOptions};
15use std::fs::{File, OpenOptions};
16use std::io::{Read, Write};
17use std::marker::PhantomData;
18use std::mem;
19use std::path::{Path, PathBuf};
20use std::slice;
21use tempfile::NamedTempFile;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum AccessMode {
26 ReadOnly,
28 ReadWrite,
30 Write,
32 CopyOnWrite,
34}
35
36impl AccessMode {
37 pub const fn as_str(&self) -> &'static str {
39 match self {
40 AccessMode::ReadOnly => "r",
41 AccessMode::ReadWrite => "r+",
42 AccessMode::Write => "w+",
43 AccessMode::CopyOnWrite => "c",
44 }
45 }
46}
47
48impl std::str::FromStr for AccessMode {
50 type Err = CoreError;
51
52 fn from_str(s: &str) -> Result<Self, Self::Err> {
53 match s {
54 "r" => Ok(AccessMode::ReadOnly),
55 "r+" => Ok(AccessMode::ReadWrite),
56 "w+" => Ok(AccessMode::Write),
57 "c" => Ok(AccessMode::CopyOnWrite),
58 _ => Err(CoreError::ValidationError(
59 ErrorContext::new(format!("Invalid access mode: {s}"))
60 .with_location(ErrorLocation::new(file!(), line!())),
61 )),
62 }
63 }
64}
65
66#[derive(Debug)]
68pub struct MemoryMappedArray<A>
69where
70 A: Clone + Copy + 'static + Send + Sync + Send + Sync,
71{
72 pub shape: Vec<usize>,
74 pub file_path: PathBuf,
76 pub mode: AccessMode,
78 pub offset: usize,
80 pub size: usize,
82 pub(crate) mmap_view: Option<Mmap>,
84 pub(crate) mmap_view_mut: Option<MmapMut>,
86 pub(crate) is_temp: bool,
88 pub(crate) phantom: PhantomData<A>,
90}
91
92#[derive(Serialize, Deserialize, Debug, Clone)]
94struct MemoryMappedHeader {
95 element_size: usize,
97 shape: Vec<usize>,
99 total_elements: usize,
101}
102
103impl<A> Clone for MemoryMappedArray<A>
104where
105 A: Clone + Copy + 'static + Send + Sync,
106{
107 fn clone(&self) -> Self {
108 Self::new::<crate::ndarray::OwnedRepr<A>, IxDyn>(
111 None,
112 &self.file_path,
113 self.mode,
114 self.offset,
115 )
116 .expect("Failed to clone memory mapped array")
117 }
118}
119
120impl<A> MemoryMappedArray<A>
121where
122 A: Clone + Copy + 'static + Send + Sync + Send + Sync,
123{
124 pub fn clone_ref(&self) -> CoreResult<Self> {
126 let element_size = mem::size_of::<A>();
129 let data_size = self.size * element_size;
130
131 match self.mode {
133 AccessMode::ReadOnly => {
134 let file = File::open(&self.file_path)
135 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
136
137 let mmap = unsafe {
138 MmapOptions::new()
139 .offset(self.offset as u64)
140 .len(data_size)
141 .map(&file)
142 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
143 };
144
145 Ok(Self {
146 shape: self.shape.clone(),
147 file_path: self.file_path.clone(),
148 mode: self.mode,
149 offset: self.offset,
150 size: self.size,
151 mmap_view: Some(mmap),
152 mmap_view_mut: None,
153 is_temp: false,
154 phantom: PhantomData,
155 })
156 }
157 AccessMode::ReadWrite | AccessMode::CopyOnWrite => {
158 let file = OpenOptions::new()
159 .read(true)
160 .write(true)
161 .open(&self.file_path)
162 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
163
164 let mmap = unsafe {
165 MmapOptions::new()
166 .offset(self.offset as u64)
167 .len(data_size)
168 .map_mut(&file)
169 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
170 };
171
172 Ok(Self {
173 shape: self.shape.clone(),
174 file_path: self.file_path.clone(),
175 mode: self.mode,
176 offset: self.offset,
177 size: self.size,
178 mmap_view: None,
179 mmap_view_mut: Some(mmap),
180 is_temp: false,
181 phantom: PhantomData,
182 })
183 }
184 AccessMode::Write => {
185 Err(CoreError::InvalidArgument(
187 ErrorContext::new("Cannot clone a write-only memory-mapped array".to_string())
188 .with_location(ErrorLocation::new(file!(), line!())),
189 ))
190 }
191 }
192 }
193 fn validate_and_create_slice<'a>(&self, ptr: *const A) -> Result<&'a [A], CoreError> {
198 if ptr.is_null() {
200 return Err(CoreError::MemoryError(
201 ErrorContext::new("Memory map pointer is null".to_string())
202 .with_location(ErrorLocation::new(file!(), line!())),
203 ));
204 }
205
206 if (ptr as usize) % std::mem::align_of::<A>() != 0 {
208 return Err(CoreError::MemoryError(
209 ErrorContext::new(format!(
210 "Memory map pointer is not properly aligned for type {} (alignment: {}, address: 0x{:x})",
211 std::any::type_name::<A>(),
212 std::mem::align_of::<A>(),
213 ptr as usize
214 ))
215 .with_location(ErrorLocation::new(file!(), line!())),
216 ));
217 }
218
219 let element_size = std::mem::size_of::<A>();
221 if element_size > 0 && self.size > isize::MAX as usize / element_size {
222 return Err(CoreError::MemoryError(
223 ErrorContext::new(format!(
224 "Array size {} exceeds maximum safe size for slice creation",
225 self.size
226 ))
227 .with_location(ErrorLocation::new(file!(), line!())),
228 ));
229 }
230
231 let total_bytes = self.size.checked_mul(element_size).ok_or_else(|| {
233 CoreError::MemoryError(
234 ErrorContext::new("Array size calculation overflows".to_string())
235 .with_location(ErrorLocation::new(file!(), line!())),
236 )
237 })?;
238
239 let mmap_len = if let Some(ref mmap) = self.mmap_view {
240 mmap.len()
241 } else if let Some(ref mmap_mut) = self.mmap_view_mut {
242 mmap_mut.len()
243 } else {
244 return Err(CoreError::MemoryError(
245 ErrorContext::new("No memory map available".to_string())
246 .with_location(ErrorLocation::new(file!(), line!())),
247 ));
248 };
249
250 if total_bytes > mmap_len {
251 return Err(CoreError::MemoryError(
252 ErrorContext::new(format!(
253 "Requested array size {total_bytes} bytes exceeds memory map size {mmap_len} bytes"
254 ))
255 .with_location(ErrorLocation::new(file!(), line!())),
256 ));
257 }
258
259 Ok(unsafe { slice::from_raw_parts(ptr, self.size) })
266 }
267
268 pub fn as_slice(&self) -> &[A] {
270 match (&self.mmap_view, &self.mmap_view_mut) {
271 (Some(view), _) => {
272 let ptr = view.as_ptr() as *const A;
273 unsafe { slice::from_raw_parts(ptr, self.size) }
275 }
276 (_, Some(view)) => {
277 let ptr = view.as_ptr() as *const A;
278 unsafe { slice::from_raw_parts(ptr, self.size) }
280 }
281 _ => &[],
282 }
283 }
284
285 pub fn path(filepath: &Path, shape: &[usize]) -> Result<Self, CoreError> {
287 let size = shape.iter().product();
289
290 let file = File::open(filepath)
292 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
293
294 let file_metadata = file
296 .metadata()
297 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
298 let file_size = file_metadata.len() as usize;
299
300 let element_size = mem::size_of::<A>();
302 let data_size = size * element_size;
303
304 if data_size > file_size {
306 return Err(CoreError::ValidationError(
307 ErrorContext::new(format!(
308 "File too small for specified shape: need {data_size} bytes, but file is only {file_size} bytes"
309 ))
310 .with_location(ErrorLocation::new(file!(), line!())),
311 ));
312 }
313
314 let mmap = unsafe {
316 MmapOptions::new()
317 .len(data_size)
318 .map(&file)
319 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
320 };
321
322 Ok(Self {
323 shape: shape.to_vec(),
324 file_path: filepath.to_path_buf(),
325 mode: AccessMode::ReadOnly,
326 offset: 0,
327 size,
328 mmap_view: Some(mmap),
329 mmap_view_mut: None,
330 is_temp: false,
331 phantom: PhantomData,
332 })
333 }
334
335 pub fn new<S, D>(
348 data: Option<&ArrayBase<S, D>>,
349 file_path: &Path,
350 mode: AccessMode,
351 offset: usize,
352 ) -> Result<Self, CoreError>
353 where
354 S: Data<Elem = A>,
355 D: Dimension,
356 {
357 let (shape, size) = if let Some(array) = data {
358 validation::check_not_empty(array)?;
359 (array.shape().to_vec(), array.len())
360 } else {
361 let (header_, _) = read_header::<A>(file_path)?;
363 (header_.shape, header_.total_elements)
364 };
365
366 let element_size = mem::size_of::<A>();
368 let data_size = size * element_size;
369
370 match mode {
372 AccessMode::ReadOnly => {
373 let file = File::open(file_path)
375 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
376
377 let file_metadata = file
379 .metadata()
380 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
381 let file_size = file_metadata.len() as usize;
382
383 if offset + data_size > file_size {
385 return Err(CoreError::ValidationError(
386 ErrorContext::new(format!(
387 "File too small: need {needed} bytes, but file is only {file_size} bytes",
388 needed = offset + data_size
389 ))
390 .with_location(ErrorLocation::new(file!(), line!())),
391 ));
392 }
393
394 let mmap = unsafe {
396 MmapOptions::new()
397 .offset(offset as u64)
398 .len(data_size)
399 .map(&file)
400 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
401 };
402
403 Ok(Self {
404 shape,
405 file_path: file_path.to_path_buf(),
406 mode,
407 offset,
408 size,
409 mmap_view: Some(mmap),
410 mmap_view_mut: None,
411 is_temp: false,
412 phantom: PhantomData,
413 })
414 }
415 AccessMode::ReadWrite => {
416 let file = OpenOptions::new()
418 .read(true)
419 .write(true)
420 .open(file_path)
421 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
422
423 let metadata = file
425 .metadata()
426 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
427 let file_size = metadata.len() as usize;
428
429 if offset + data_size > file_size {
431 file.set_len((offset + data_size) as u64)
432 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
433 }
434
435 let mut mmap = unsafe {
437 MmapOptions::new()
438 .offset(offset as u64)
439 .len(data_size)
440 .map_mut(&file)
441 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
442 };
443
444 if let Some(array) = data {
446 let bytes = unsafe {
448 slice::from_raw_parts(
449 array.as_ptr() as *const u8,
450 array.len() * mem::size_of::<A>(),
451 )
452 };
453
454 mmap[..].copy_from_slice(bytes);
456 }
457
458 Ok(Self {
459 shape,
460 file_path: file_path.to_path_buf(),
461 mode,
462 offset,
463 size,
464 mmap_view: None,
465 mmap_view_mut: Some(mmap),
466 is_temp: false,
467 phantom: PhantomData,
468 })
469 }
470 AccessMode::Write => {
471 let mut file = OpenOptions::new()
473 .read(true)
474 .write(true)
475 .create(true)
476 .truncate(true)
477 .open(file_path)
478 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
479
480 let header = MemoryMappedHeader {
482 element_size,
483 shape: shape.clone(),
484 total_elements: size,
485 };
486
487 let cfg = config::standard();
489 let header_bytes = serde::encode_to_vec(&header, cfg).map_err(|e| {
490 CoreError::ValidationError(
491 ErrorContext::new(format!("Failed to serialize header: {e}"))
492 .with_location(ErrorLocation::new(file!(), line!())),
493 )
494 })?;
495
496 let header_len = header_bytes.len() as u64;
498 file.write_all(&header_len.to_le_bytes())
499 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
500
501 file.write_all(&header_bytes)
503 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
504 let align = std::mem::align_of::<A>();
507 let mut padding_size = 0usize;
508 let header_size_unaligned = 8 + header_bytes.len();
509 if align > 1 {
510 let rem = header_size_unaligned % align;
511 if rem != 0 {
512 padding_size = align - rem;
513 let padding = vec![0u8; padding_size];
515 file.write_all(&padding).map_err(|e| {
516 CoreError::IoError(ErrorContext::new(format!(
517 "Failed to write header padding: {e}"
518 )))
519 })?;
520 }
521 }
522 let header_size = header_size_unaligned + padding_size; let total_size = header_size + data_size;
526
527 file.set_len(total_size as u64)
529 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
530
531 let data_offset = header_size + offset;
534 let mut mmap = unsafe {
535 MmapOptions::new()
536 .offset(data_offset as u64)
537 .len(data_size)
538 .map_mut(&file)
539 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
540 };
541
542 if let Some(array) = data {
544 let bytes = unsafe {
546 slice::from_raw_parts(
547 array.as_ptr() as *const u8,
548 array.len() * mem::size_of::<A>(),
549 )
550 };
551
552 mmap[..].copy_from_slice(bytes);
554 }
555
556 Ok(Self {
557 shape,
558 file_path: file_path.to_path_buf(),
559 mode,
560 offset: data_offset, size,
562 mmap_view: None,
563 mmap_view_mut: Some(mmap),
564 is_temp: false,
565 phantom: PhantomData,
566 })
567 }
568 AccessMode::CopyOnWrite => {
569 let file = File::open(file_path)
571 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
572
573 let mmap = unsafe {
575 MmapOptions::new()
576 .offset(offset as u64)
577 .len(data_size)
578 .map_copy(&file)
579 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
580 };
581
582 Ok(Self {
583 shape,
584 file_path: file_path.to_path_buf(),
585 mode,
586 offset,
587 size,
588 mmap_view: None,
589 mmap_view_mut: Some(mmap),
590 is_temp: false,
591 phantom: PhantomData,
592 })
593 }
594 }
595 }
596
597 pub fn new_temp<S, D>(
609 data: &ArrayBase<S, D>,
610 mode: AccessMode,
611 offset: usize,
612 ) -> Result<Self, CoreError>
613 where
614 S: Data<Elem = A>,
615 D: Dimension,
616 {
617 let temp_file = NamedTempFile::new()
618 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
619 let file_path = temp_file.path().to_path_buf();
620
621 let _file = temp_file
623 .persist(&file_path)
624 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
625
626 let mut result = Self::new(Some(data), &file_path, mode, offset)?;
627 result.is_temp = true;
628
629 Ok(result)
630 }
631
632 pub fn as_array<D>(&self) -> Result<Array<A, D>, CoreError>
638 where
639 D: Dimension,
640 {
641 let data_slice = match (&self.mmap_view, &self.mmap_view_mut) {
643 (Some(view), _) => {
644 let ptr = view.as_ptr() as *const A;
646 let required_bytes = self.size * std::mem::size_of::<A>();
649 if view.len() < required_bytes {
650 return Err(CoreError::ValidationError(
651 ErrorContext::new(format!(
652 "Memory map view too small: {} bytes available, {} bytes required",
653 view.len(),
654 required_bytes
655 ))
656 .with_location(ErrorLocation::new(file!(), line!())),
657 ));
658 }
659 if ptr as usize % std::mem::align_of::<A>() != 0 {
661 return Err(CoreError::ValidationError(
662 ErrorContext::new("Memory map pointer is not properly aligned")
663 .with_location(ErrorLocation::new(file!(), line!())),
664 ));
665 }
666 unsafe { std::slice::from_raw_parts(ptr, self.size) }
667 }
668 (_, Some(view)) => {
669 let ptr = view.as_ptr() as *const A;
671 let required_bytes = self.size * std::mem::size_of::<A>();
674 if view.len() < required_bytes {
675 return Err(CoreError::ValidationError(
676 ErrorContext::new(format!(
677 "Memory map view too small: {} bytes available, {} bytes required",
678 view.len(),
679 required_bytes
680 ))
681 .with_location(ErrorLocation::new(file!(), line!())),
682 ));
683 }
684 if ptr as usize % std::mem::align_of::<A>() != 0 {
686 return Err(CoreError::ValidationError(
687 ErrorContext::new("Memory map pointer is not properly aligned")
688 .with_location(ErrorLocation::new(file!(), line!())),
689 ));
690 }
691 unsafe { std::slice::from_raw_parts(ptr, self.size) }
692 }
693 _ => {
694 return Err(CoreError::ValidationError(
695 ErrorContext::new("Memory map is not initialized".to_string())
696 .with_location(ErrorLocation::new(file!(), line!())),
697 ));
698 }
699 };
700
701 let shape_vec = self.shape.clone();
704
705 let array = Array::from_shape_vec(shape_vec, data_slice.to_vec()).map_err(|e| {
707 CoreError::ShapeError(
708 ErrorContext::new(format!("error: {e}"))
709 .with_location(ErrorLocation::new(file!(), line!())),
710 )
711 })?;
712
713 let array = array.into_dimensionality::<D>().map_err(|e| {
715 CoreError::ShapeError(
716 ErrorContext::new(format!(
717 "Failed to convert array to requested dimension type: {e}"
718 ))
719 .with_location(ErrorLocation::new(file!(), line!())),
720 )
721 })?;
722
723 Ok(array)
724 }
725
726 pub fn as_array_mut<D>(&mut self) -> Result<crate::ndarray::ArrayViewMut<A, D>, CoreError>
736 where
737 D: Dimension,
738 {
739 if self.mode == AccessMode::ReadOnly {
740 return Err(CoreError::ValidationError(
741 ErrorContext::new(
742 "Cannot get mutable view of read-only memory-mapped array".to_string(),
743 )
744 .with_location(ErrorLocation::new(file!(), line!())),
745 ));
746 }
747
748 let data_slice = if let Some(view) = &mut self.mmap_view_mut {
750 let ptr = view.as_mut_ptr() as *mut A;
751
752 if ptr.is_null() {
754 return Err(CoreError::MemoryError(
755 ErrorContext::new("Memory map pointer is null".to_string())
756 .with_location(ErrorLocation::new(file!(), line!())),
757 ));
758 }
759
760 if (ptr as usize) % std::mem::align_of::<A>() != 0 {
762 return Err(CoreError::MemoryError(
763 ErrorContext::new(format!(
764 "Memory map pointer is not properly aligned for type {} (alignment: {}, address: 0x{:x})",
765 std::any::type_name::<A>(),
766 std::mem::align_of::<A>(),
767 ptr as usize
768 ))
769 .with_location(ErrorLocation::new(file!(), line!())),
770 ));
771 }
772
773 let element_size = std::mem::size_of::<A>();
775 if element_size > 0 && self.size > isize::MAX as usize / element_size {
776 return Err(CoreError::MemoryError(
777 ErrorContext::new(format!(
778 "Array size {} exceeds maximum safe size for slice creation",
779 self.size
780 ))
781 .with_location(ErrorLocation::new(file!(), line!())),
782 ));
783 }
784
785 let total_bytes = self.size.checked_mul(element_size).ok_or_else(|| {
787 CoreError::MemoryError(
788 ErrorContext::new("Array size calculation overflows".to_string())
789 .with_location(ErrorLocation::new(file!(), line!())),
790 )
791 })?;
792
793 if total_bytes > view.len() {
794 return Err(CoreError::MemoryError(
795 ErrorContext::new(format!(
796 "Requested array size {} bytes exceeds memory map size {} bytes",
797 total_bytes,
798 view.len()
799 ))
800 .with_location(ErrorLocation::new(file!(), line!())),
801 ));
802 }
803
804 unsafe { slice::from_raw_parts_mut(ptr, self.size) }
811 } else {
812 return Err(CoreError::ValidationError(
813 ErrorContext::new("Mutable memory map is not initialized".to_string())
814 .with_location(ErrorLocation::new(file!(), line!())),
815 ));
816 };
817
818 let array_view = crate::ndarray::ArrayViewMut::from_shape(self.shape.clone(), data_slice)
820 .map_err(|e| {
821 CoreError::ShapeError(
822 ErrorContext::new(format!("error: {e}"))
823 .with_location(ErrorLocation::new(file!(), line!())),
824 )
825 })?;
826
827 let array_view = array_view.into_dimensionality::<D>().map_err(|e| {
829 CoreError::ShapeError(
830 ErrorContext::new(format!(
831 "Failed to convert array to requested dimension type: {e}"
832 ))
833 .with_location(ErrorLocation::new(file!(), line!())),
834 )
835 })?;
836
837 Ok(array_view)
838 }
839
840 pub fn flush(&mut self) -> Result<(), CoreError> {
846 if let Some(view) = &mut self.mmap_view_mut {
847 view.flush()
848 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
849 }
850
851 Ok(())
852 }
853
854 pub fn reload(&mut self) -> Result<(), CoreError> {
863 let _ = self.flush();
865
866 let file_path = self.file_path.clone();
868 let mode = self.mode;
869 let offset = self.offset;
870
871 self.mmap_view = None;
873 self.mmap_view_mut = None;
874
875 match mode {
877 AccessMode::ReadOnly => {
878 let file = File::open(&file_path)
880 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
881
882 let mmap = unsafe {
884 MmapOptions::new()
885 .offset(offset as u64)
886 .map(&file)
887 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
888 };
889
890 self.mmap_view = Some(mmap);
891 }
892 AccessMode::ReadWrite | AccessMode::Write => {
893 let file = OpenOptions::new()
895 .read(true)
896 .write(true)
897 .open(&file_path)
898 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
899
900 let mmap = unsafe {
902 MmapOptions::new()
903 .offset(offset as u64)
904 .map_mut(&file)
905 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
906 };
907
908 self.mmap_view_mut = Some(mmap);
909 }
910 AccessMode::CopyOnWrite => {
911 let file = File::open(&file_path)
913 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
914
915 let mmap = unsafe {
917 MmapOptions::new()
918 .offset(offset as u64)
919 .map_copy(&file)
920 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?
921 };
922
923 self.mmap_view_mut = Some(mmap);
924 }
925 }
926
927 Ok(())
928 }
929
930 pub fn is_temp(&self) -> bool {
936 self.is_temp
937 }
938
939 pub fn as_bytes(&self) -> Result<&[u8], CoreError> {
945 match (&self.mmap_view, &self.mmap_view_mut) {
946 (Some(view), _) => {
947 Ok(view)
949 }
950 (_, Some(view)) => {
951 Ok(view)
953 }
954 _ => Err(CoreError::ValidationError(
955 ErrorContext::new("Memory map is not initialized".to_string())
956 .with_location(ErrorLocation::new(file!(), line!())),
957 )),
958 }
959 }
960
961 pub fn as_bytes_mut(&mut self) -> Result<&mut [u8], CoreError> {
967 if self.mode == AccessMode::ReadOnly {
968 return Err(CoreError::ValidationError(
969 ErrorContext::new(
970 "Cannot get mutable view of read-only memory-mapped array".to_string(),
971 )
972 .with_location(ErrorLocation::new(file!(), line!())),
973 ));
974 }
975
976 match &mut self.mmap_view_mut {
977 Some(view) => {
978 Ok(view)
980 }
981 _ => Err(CoreError::ValidationError(
982 ErrorContext::new("Mutable memory map is not initialized".to_string())
983 .with_location(ErrorLocation::new(file!(), line!())),
984 )),
985 }
986 }
987}
988
989impl<A> Drop for MemoryMappedArray<A>
990where
991 A: Clone + Copy + 'static + Send + Sync + Send + Sync,
992{
993 fn drop(&mut self) {
994 if let Some(view) = &mut self.mmap_view_mut {
996 let _ = view.flush();
997 }
998
999 if self.is_temp {
1001 let _ = std::fs::remove_file(&self.file_path);
1002 }
1003 }
1004}
1005
1006#[allow(dead_code)]
1008fn read_header<A: Clone + Copy + 'static + Send + Sync>(
1009 file_path: &Path,
1010) -> Result<(MemoryMappedHeader, usize), CoreError> {
1011 let mut file =
1013 File::open(file_path).map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
1014
1015 let file_metadata = file
1018 .metadata()
1019 .map_err(|e| CoreError::IoError(ErrorContext::new(e.to_string())))?;
1020 let file_size = file_metadata.len() as usize;
1021
1022 if file_size < 8 {
1023 let element_size = std::mem::size_of::<A>();
1025 let total_elements = file_size / element_size;
1026
1027 let header = MemoryMappedHeader {
1028 element_size,
1029 shape: vec![total_elements],
1030 total_elements,
1031 };
1032
1033 return Ok((header, 0)); }
1035
1036 let mut header_len_bytes = [0u8; 8];
1039 if file.read_exact(&mut header_len_bytes).is_err() {
1040 let element_size = std::mem::size_of::<A>();
1042 let total_elements = file_size / element_size;
1043
1044 let header = MemoryMappedHeader {
1045 element_size,
1046 shape: vec![total_elements],
1047 total_elements,
1048 };
1049
1050 return Ok((header, 0)); }
1052
1053 let header_len = u64::from_ne_bytes(header_len_bytes) as usize;
1054
1055 if header_len > file_size || header_len > 1024 * 1024 {
1057 let element_size = std::mem::size_of::<A>();
1059 let total_elements = file_size / element_size;
1060
1061 let header = MemoryMappedHeader {
1062 element_size,
1063 shape: vec![total_elements],
1064 total_elements,
1065 };
1066
1067 return Ok((header, 0)); }
1069
1070 let mut header_bytes = vec![0u8; header_len];
1072 if file.read_exact(&mut header_bytes).is_err() {
1073 let element_size = std::mem::size_of::<A>();
1075 let total_elements = file_size / element_size;
1076
1077 let header = MemoryMappedHeader {
1078 element_size,
1079 shape: vec![total_elements],
1080 total_elements,
1081 };
1082
1083 return Ok((header, 0)); }
1085
1086 let cfg = config::standard();
1088 match serde::decode_from_slice::<MemoryMappedHeader, _>(&header_bytes, cfg) {
1089 Ok((header, _len)) => {
1090 let element_size_expected = std::mem::size_of::<A>();
1091 if header.element_size == element_size_expected {
1092 let base_header_size = 8 + header_len; let align = std::mem::align_of::<A>();
1095 let padding = if align > 1 {
1096 (align - (base_header_size % align)) % align
1097 } else {
1098 0
1099 };
1100 let mut aligned_header_size = base_header_size;
1101 if padding > 0 && base_header_size + padding <= file_size {
1102 let mut padding_buf = vec![0u8; padding];
1104 match file.read_exact(&mut padding_buf) {
1106 Ok(_) => {
1107 if padding_buf.iter().all(|b| *b == 0) {
1109 aligned_header_size += padding;
1110 } else {
1111 aligned_header_size = base_header_size; }
1117 }
1118 Err(_) => {
1119 aligned_header_size = base_header_size; }
1121 }
1122 }
1123 Ok((header, aligned_header_size))
1124 } else {
1125 let element_size = element_size_expected;
1127 let total_elements = file_size / element_size;
1128 let fallback_header = MemoryMappedHeader {
1129 element_size,
1130 shape: vec![total_elements],
1131 total_elements,
1132 };
1133 Ok((fallback_header, 0))
1134 }
1135 }
1136 Err(_) => {
1137 let element_size = std::mem::size_of::<A>();
1139 let total_elements = file_size / element_size;
1140 let header = MemoryMappedHeader {
1141 element_size,
1142 shape: vec![total_elements],
1143 total_elements,
1144 };
1145 Ok((header, 0))
1146 }
1147 }
1148}
1149
1150#[allow(dead_code)]
1162pub fn open_mmap<A, D>(
1163 file_path: &Path,
1164 mode: AccessMode,
1165 offset: usize,
1166) -> Result<MemoryMappedArray<A>, CoreError>
1167where
1168 A: Clone + Copy + Send + Sync + 'static,
1169 D: Dimension,
1170{
1171 let (header, header_size) = read_header::<A>(file_path)?;
1173
1174 let element_size = std::mem::size_of::<A>();
1176 if header.element_size != element_size {
1177 return Err(CoreError::ValidationError(
1178 ErrorContext::new(format!(
1179 "Element size mismatch: file has {} bytes, but type requires {} bytes",
1180 header.element_size, element_size
1181 ))
1182 .with_location(ErrorLocation::new(file!(), line!())),
1183 ));
1184 }
1185
1186 let effective_offset = header_size + offset;
1188
1189 MemoryMappedArray::<A>::new::<crate::ndarray::OwnedRepr<A>, D>(
1191 None,
1192 file_path,
1193 mode,
1194 effective_offset,
1195 )
1196}
1197
1198#[allow(dead_code)]
1211pub fn create_mmap<A, S, D>(
1212 data: &ArrayBase<S, D>,
1213 file_path: &Path,
1214 mode: AccessMode,
1215 offset: usize,
1216) -> Result<MemoryMappedArray<A>, CoreError>
1217where
1218 A: Clone + Copy + 'static + Send + Sync + Send + Sync,
1219 S: Data<Elem = A>,
1220 D: Dimension,
1221{
1222 MemoryMappedArray::new(Some(data), file_path, mode, offset)
1223}
1224
1225#[allow(dead_code)]
1237pub fn create_temp_mmap<A, S, D>(
1238 data: &ArrayBase<S, D>,
1239 mode: AccessMode,
1240 offset: usize,
1241) -> Result<MemoryMappedArray<A>, CoreError>
1242where
1243 A: Clone + Copy + 'static + Send + Sync + Send + Sync,
1244 S: Data<Elem = A>,
1245 D: Dimension,
1246{
1247 MemoryMappedArray::new_temp(data, mode, offset)
1248}