1pub mod config;
8pub mod error;
10
11#[cfg(test)]
12mod tests;
13
14pub use config::{MmapConfig, MmapMode};
15pub use error::{MmapError, MmapResult};
16
17use std::fs::{File, OpenOptions};
18use std::io::{self, Write};
19use std::ops::Deref;
20use std::path::Path;
21use std::sync::Arc;
22
23use memmap2::{Mmap, MmapMut, MmapOptions as Memmap2Options};
24use rkyv::Portable;
25use rkyv::api::high::{HighValidator, access};
26use rkyv::bytecheck::CheckBytes;
27use rkyv::rancor::Error as RkyvError;
28
29pub const RKYV_ALIGNMENT: usize = 16;
31
32enum MmapInner {
33 ReadOnly(Mmap),
34 Mutable(MmapMut),
35}
36
37impl MmapInner {
38 fn as_slice(&self) -> &[u8] {
39 match self {
40 MmapInner::ReadOnly(m) => m.deref(),
41 MmapInner::Mutable(m) => m.deref(),
42 }
43 }
44
45 fn as_mut_slice(&mut self) -> Option<&mut [u8]> {
46 match self {
47 MmapInner::ReadOnly(_) => None,
48 MmapInner::Mutable(m) => Some(m.as_mut()),
49 }
50 }
51
52 fn len(&self) -> usize {
53 self.as_slice().len()
54 }
55
56 fn flush(&self) -> io::Result<()> {
57 match self {
58 MmapInner::ReadOnly(_) => Ok(()),
59 MmapInner::Mutable(m) => m.flush(),
60 }
61 }
62
63 fn flush_async(&self) -> io::Result<()> {
64 match self {
65 MmapInner::ReadOnly(_) => Ok(()),
66 MmapInner::Mutable(m) => m.flush_async(),
67 }
68 }
69
70 fn flush_range(&self, offset: usize, len: usize) -> io::Result<()> {
71 match self {
72 MmapInner::ReadOnly(_) => Ok(()),
73 MmapInner::Mutable(m) => m.flush_range(offset, len),
74 }
75 }
76}
77
78pub struct MmapFile {
80 mmap: MmapInner,
81 file: File,
82 config: MmapConfig,
83 path: std::path::PathBuf,
84}
85
86impl MmapFile {
87 pub fn open<P: AsRef<Path>>(path: P, config: MmapConfig) -> MmapResult<Self> {
89 let path = path.as_ref();
90
91 let file = match config.mode {
92 MmapMode::ReadOnly | MmapMode::CopyOnWrite => {
93 OpenOptions::new().read(true).open(path)?
94 }
95 MmapMode::ReadWrite => OpenOptions::new().read(true).write(true).open(path)?,
96 };
97
98 let metadata = file.metadata()?;
99 let file_len = metadata.len() as usize;
100
101 if file_len == 0 {
102 return Err(MmapError::EmptyFile);
103 }
104
105 let mmap = Self::create_mapping(&file, &config)?;
106
107 Ok(Self {
108 mmap,
109 file,
110 config,
111 path: path.to_path_buf(),
112 })
113 }
114
115 pub fn create<P: AsRef<Path>>(
117 path: P,
118 size: usize,
119 mut config: MmapConfig,
120 ) -> MmapResult<Self> {
121 let path = path.as_ref();
122 config.mode = MmapMode::ReadWrite;
123
124 let file = OpenOptions::new()
125 .read(true)
126 .write(true)
127 .create(true)
128 .truncate(true)
129 .open(path)?;
130
131 file.set_len(size as u64)?;
132
133 let mmap = Self::create_mapping(&file, &config)?;
134
135 Ok(Self {
136 mmap,
137 file,
138 config,
139 path: path.to_path_buf(),
140 })
141 }
142
143 fn create_mapping(file: &File, config: &MmapConfig) -> MmapResult<MmapInner> {
144 let mut opts = Memmap2Options::new();
145
146 if let Some(offset) = config.offset {
147 opts.offset(offset);
148 }
149
150 if let Some(len) = config.len {
151 opts.len(len);
152 }
153
154 if config.populate {
155 opts.populate();
156 }
157
158 let mmap = match config.mode {
159 MmapMode::ReadOnly => {
160 let m = unsafe { opts.map(file)? };
163 MmapInner::ReadOnly(m)
164 }
165 MmapMode::ReadWrite => {
166 let m = unsafe { opts.map_mut(file)? };
169 MmapInner::Mutable(m)
170 }
171 MmapMode::CopyOnWrite => {
172 let m = unsafe { opts.map_copy(file)? };
174 MmapInner::Mutable(m)
175 }
176 };
177
178 Ok(mmap)
179 }
180
181 pub fn as_slice(&self) -> &[u8] {
183 self.mmap.as_slice()
184 }
185
186 pub fn as_mut_slice(&mut self) -> Option<&mut [u8]> {
188 self.mmap.as_mut_slice()
189 }
190
191 pub fn len(&self) -> usize {
193 self.mmap.len()
194 }
195
196 pub fn is_empty(&self) -> bool {
198 self.len() == 0
199 }
200
201 pub fn is_writable(&self) -> bool {
203 matches!(
204 self.config.mode,
205 MmapMode::ReadWrite | MmapMode::CopyOnWrite
206 )
207 }
208
209 pub fn flush(&self) -> MmapResult<()> {
211 self.mmap.flush()?;
212 Ok(())
213 }
214
215 pub fn flush_async(&self) -> MmapResult<()> {
217 self.mmap.flush_async()?;
218 Ok(())
219 }
220
221 pub fn flush_range(&self, offset: usize, len: usize) -> MmapResult<()> {
223 self.mmap.flush_range(offset, len)?;
224 Ok(())
225 }
226
227 pub fn access_archived<T>(&self) -> MmapResult<&T>
229 where
230 T: Portable + for<'a> CheckBytes<HighValidator<'a, RkyvError>>,
231 {
232 self.access_archived_at::<T>(0)
233 }
234
235 pub fn access_archived_at<T>(&self, offset: usize) -> MmapResult<&T>
237 where
238 T: Portable + for<'a> CheckBytes<HighValidator<'a, RkyvError>>,
239 {
240 let data = self.as_slice();
241
242 if offset >= data.len() {
243 return Err(MmapError::FileTooSmall {
244 expected: offset + 1,
245 actual: data.len(),
246 });
247 }
248
249 let slice = &data[offset..];
250
251 let ptr = slice.as_ptr();
252 if !(ptr as usize).is_multiple_of(RKYV_ALIGNMENT) {
253 return Err(MmapError::AlignmentError {
254 offset,
255 alignment: RKYV_ALIGNMENT,
256 });
257 }
258
259 access::<T, RkyvError>(slice).map_err(|e| MmapError::ValidationFailed(format!("{:?}", e)))
260 }
261
262 pub fn as_ptr(&self) -> *const u8 {
264 self.as_slice().as_ptr()
265 }
266
267 pub fn as_mut_ptr(&mut self) -> Option<*mut u8> {
269 self.as_mut_slice().map(|s| s.as_mut_ptr())
270 }
271
272 pub fn grow(&mut self, new_size: usize) -> MmapResult<()> {
274 if self.config.mode == MmapMode::ReadOnly {
275 return Err(MmapError::ResizeFailed(
276 "Cannot grow read-only mapping".to_string(),
277 ));
278 }
279
280 let current_size = self.len();
281 if new_size <= current_size {
282 return Err(MmapError::ResizeFailed(format!(
283 "New size {} must be larger than current size {}",
284 new_size, current_size
285 )));
286 }
287
288 self.flush()?;
289
290 self.file.set_len(new_size as u64)?;
291
292 self.mmap = Self::create_mapping(&self.file, &self.config)?;
293
294 Ok(())
295 }
296
297 pub fn shrink(&mut self, new_size: usize) -> MmapResult<()> {
299 if self.config.mode == MmapMode::ReadOnly {
300 return Err(MmapError::ResizeFailed(
301 "Cannot shrink read-only mapping".to_string(),
302 ));
303 }
304
305 if new_size == 0 {
306 return Err(MmapError::ResizeFailed(
307 "Cannot shrink to zero size".to_string(),
308 ));
309 }
310
311 let current_size = self.len();
312 if new_size >= current_size {
313 return Err(MmapError::ResizeFailed(format!(
314 "New size {} must be smaller than current size {}",
315 new_size, current_size
316 )));
317 }
318
319 self.flush()?;
320
321 self.file.set_len(new_size as u64)?;
322
323 self.mmap = Self::create_mapping(&self.file, &self.config)?;
324
325 Ok(())
326 }
327
328 pub fn resize(&mut self, new_size: usize) -> MmapResult<()> {
330 let current_size = self.len();
331
332 if new_size > current_size {
333 self.grow(new_size)
334 } else if new_size < current_size {
335 self.shrink(new_size)
336 } else {
337 Ok(())
338 }
339 }
340
341 pub fn path(&self) -> &Path {
343 &self.path
344 }
345
346 pub fn mode(&self) -> MmapMode {
348 self.config.mode
349 }
350}
351
352#[derive(Clone)]
353pub struct MmapFileHandle {
355 inner: Arc<Mmap>,
356 path: Arc<std::path::PathBuf>,
357}
358
359impl std::fmt::Debug for MmapFileHandle {
360 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361 f.debug_struct("MmapFileHandle")
362 .field("path", &self.path)
363 .field("len", &self.len())
364 .field("strong_count", &self.strong_count())
365 .finish()
366 }
367}
368
369impl MmapFileHandle {
370 pub fn open<P: AsRef<Path>>(path: P) -> MmapResult<Self> {
372 let path = path.as_ref();
373 let file = File::open(path)?;
374
375 let metadata = file.metadata()?;
376 if metadata.len() == 0 {
377 return Err(MmapError::EmptyFile);
378 }
379
380 let mmap = unsafe { Mmap::map(&file)? };
383
384 Ok(Self {
385 inner: Arc::new(mmap),
386 path: Arc::new(path.to_path_buf()),
387 })
388 }
389
390 pub fn as_slice(&self) -> &[u8] {
392 self.inner.deref()
393 }
394
395 pub fn len(&self) -> usize {
397 self.inner.len()
398 }
399
400 pub fn is_empty(&self) -> bool {
402 self.len() == 0
403 }
404
405 pub fn strong_count(&self) -> usize {
407 Arc::strong_count(&self.inner)
408 }
409
410 pub fn access_archived<T>(&self) -> MmapResult<&T>
412 where
413 T: Portable + for<'a> CheckBytes<HighValidator<'a, RkyvError>>,
414 {
415 self.access_archived_at::<T>(0)
416 }
417
418 pub fn access_archived_at<T>(&self, offset: usize) -> MmapResult<&T>
420 where
421 T: Portable + for<'a> CheckBytes<HighValidator<'a, RkyvError>>,
422 {
423 let data = self.as_slice();
424
425 if offset >= data.len() {
426 return Err(MmapError::FileTooSmall {
427 expected: offset + 1,
428 actual: data.len(),
429 });
430 }
431
432 let slice = &data[offset..];
433
434 let ptr = slice.as_ptr();
435 if !(ptr as usize).is_multiple_of(RKYV_ALIGNMENT) {
436 return Err(MmapError::AlignmentError {
437 offset,
438 alignment: RKYV_ALIGNMENT,
439 });
440 }
441
442 access::<T, RkyvError>(slice).map_err(|e| MmapError::ValidationFailed(format!("{:?}", e)))
443 }
444
445 pub fn as_ptr(&self) -> *const u8 {
447 self.as_slice().as_ptr()
448 }
449
450 pub fn path(&self) -> &Path {
452 &self.path
453 }
454}
455
456pub struct AlignedMmapBuilder {
458 path: std::path::PathBuf,
459}
460
461impl AlignedMmapBuilder {
462 pub fn new<P: AsRef<Path>>(path: P) -> Self {
464 Self {
465 path: path.as_ref().to_path_buf(),
466 }
467 }
468
469 pub fn write(self, data: &[u8]) -> MmapResult<MmapFile> {
471 let mut file = OpenOptions::new()
472 .read(true)
473 .write(true)
474 .create(true)
475 .truncate(true)
476 .open(&self.path)?;
477
478 file.write_all(data)?;
479 file.flush()?;
480 drop(file);
481
482 MmapFile::open(&self.path, MmapConfig::read_write())
483 }
484
485 pub fn write_readonly(self, data: &[u8]) -> MmapResult<MmapFileHandle> {
487 let mut file = OpenOptions::new()
488 .read(true)
489 .write(true)
490 .create(true)
491 .truncate(true)
492 .open(&self.path)?;
493
494 file.write_all(data)?;
495 file.flush()?;
496 drop(file);
497
498 MmapFileHandle::open(&self.path)
499 }
500}