1#![allow(unsafe_code)]
17
18use crate::error::Result;
19use std::fs::File;
20use std::path::Path;
21
22pub struct MappedFile {
25 backing: Backing,
26}
27
28enum Backing {
29 Map(memmap2::Mmap),
30 Heap(bytes::Bytes),
31}
32
33impl std::fmt::Debug for MappedFile {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 let kind = match self.backing {
36 Backing::Map(_) => "mmap",
37 Backing::Heap(_) => "heap",
38 };
39 f.debug_struct("MappedFile").field("kind", &kind).field("len", &self.bytes().len()).finish()
40 }
41}
42
43impl MappedFile {
44 pub fn open(path: &Path) -> Result<Self> {
46 let file = File::open(path)?;
47 let mmap = unsafe { memmap2::Mmap::map(&file)? };
51 Ok(Self { backing: Backing::Map(mmap) })
52 }
53
54 #[must_use]
56 pub fn from_bytes(bytes: bytes::Bytes) -> Self {
57 Self { backing: Backing::Heap(bytes) }
58 }
59
60 #[must_use]
62 pub fn bytes(&self) -> &[u8] {
63 match &self.backing {
64 Backing::Map(m) => m,
65 Backing::Heap(b) => b,
66 }
67 }
68}
69
70pub fn cast_f32(bytes: &[u8]) -> Result<&[f32]> {
74 const _: () = assert!(cfg!(target_endian = "little"), "images are little-endian");
75 if !bytes.len().is_multiple_of(4) {
76 return Err(crate::Error::format("f32 section length is not a multiple of 4"));
77 }
78 if !(bytes.as_ptr() as usize).is_multiple_of(std::mem::align_of::<f32>()) {
79 return Err(crate::Error::format("f32 section is not aligned"));
80 }
81 #[allow(clippy::cast_ptr_alignment)] Ok(unsafe { std::slice::from_raw_parts(bytes.as_ptr().cast::<f32>(), bytes.len() / 4) })
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn heap_images_are_served_in_place() {
93 let b = bytes::Bytes::from_static(b"image");
94 let m = MappedFile::from_bytes(b.clone());
95 assert_eq!(m.bytes().as_ptr(), b.as_ptr(), "no copy");
96 }
97
98 #[test]
99 fn mmap_reads_file_bytes() {
100 let dir = tempfile::tempdir().unwrap();
101 let p = dir.path().join("f");
102 std::fs::write(&p, b"abc123").unwrap();
103 let m = MappedFile::open(&p).unwrap();
104 assert_eq!(m.bytes(), b"abc123");
105 }
106
107 #[test]
108 fn f32_views_check_length_and_alignment() {
109 let v: Vec<f32> = vec![1.0, -2.5, 3.25];
110 let raw: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
111 let mut aligned = vec![0u32; 4];
112 let buf = as_bytes(&mut aligned);
113 buf[..12].copy_from_slice(&raw);
114 assert_eq!(cast_f32(&buf[..12]).unwrap(), &v[..]);
115 assert!(cast_f32(&buf[..11]).is_err());
116 assert!(cast_f32(&buf[1..13]).is_err());
117 }
118
119 fn as_bytes(words: &mut [u32]) -> &mut [u8] {
120 unsafe { std::slice::from_raw_parts_mut(words.as_mut_ptr().cast::<u8>(), words.len() * 4) }
122 }
123}