ranged_mmap/file/
mmap_file.rs

1//! Type-safe memory-mapped file implementation
2//! 
3//! 基于类型安全的内存映射文件实现
4
5use crate::allocator;
6
7use super::allocator::RangeAllocator;
8use super::mmap_file_inner::MmapFileInner;
9use super::range::{AllocatedRange, WriteReceipt};
10use super::error::{Error, Result};
11use std::path::Path;
12use std::num::NonZeroU64;
13
14/// Type-safe memory-mapped file
15/// 
16/// 基于内存映射的安全文件
17/// 
18/// Achieves compile-time safety through [`RangeAllocator`] and [`AllocatedRange`].
19/// 
20/// 通过 [`RangeAllocator`] 和 [`AllocatedRange`] 实现编译期安全。
21/// 
22/// This version requires all write operations to provide an [`AllocatedRange`] parameter,
23/// which can only be allocated through [`RangeAllocator`], thus guaranteeing at compile-time:
24/// - All write ranges are valid (do not exceed file size)
25/// - All ranges are non-overlapping (allocator allocates sequentially)
26/// 
27/// 这个版本要求所有写入操作提供 [`AllocatedRange`] 参数,
28/// 该范围只能通过 [`RangeAllocator`] 分配,从而在编译期保证:
29/// - 所有写入的范围都是有效的(不超出文件大小)
30/// - 所有范围互不重叠(分配器顺序分配)
31/// 
32/// # Features
33/// 
34/// - **Compile-time safety**: Prevents overlapping writes through the type system
35/// - **Zero-copy writes**: Write operations directly modify mapped memory
36/// - **Lock-free concurrency**: Concurrent writes to different ranges require no locking
37/// - **Reference counting**: Can be cloned and shared among multiple workers
38/// - **Runtime agnostic**: Does not depend on any specific async runtime
39/// 
40/// # 特性
41/// 
42/// - **编译期安全**:通过类型系统防止重叠写入
43/// - **零拷贝写入**:写入操作直接修改映射内存
44/// - **无锁并发**:不同范围的并发写入无需加锁
45/// - **引用计数**:可以克隆并在多个 worker 间共享
46/// - **运行时无关**:不依赖特定异步运行时
47/// 
48/// # Usage Example
49/// 
50/// ```
51/// # use ranged_mmap::{MmapFile, Result, allocator::ALIGNMENT};
52/// # use tempfile::tempdir;
53/// # fn main() -> Result<()> {
54/// # let dir = tempdir()?;
55/// # let path = dir.path().join("output.bin");
56/// # use std::num::NonZeroU64;
57/// // Create file and allocator (4K aligned)
58/// // 创建文件和分配器(4K对齐)
59/// let (file, mut allocator) = MmapFile::create_default(&path, NonZeroU64::new(ALIGNMENT * 2).unwrap())?;
60///
61/// // Allocate ranges in the main thread (4K aligned)
62/// // 在主线程分配范围(4K对齐)
63/// let range1 = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
64/// let range2 = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
65///
66/// // Concurrent writes to different ranges (compile-time safe!)
67/// // 并发写入不同范围(编译期安全!)
68/// std::thread::scope(|s| {
69///     let f1 = file.clone();
70///     let f2 = file.clone();
71///     s.spawn(move || { f1.write_range(range1, &vec![1u8; ALIGNMENT as usize]); });
72///     s.spawn(move || { f2.write_range(range2, &vec![2u8; ALIGNMENT as usize]); });
73/// });
74///
75/// unsafe { file.sync_all()?; }
76/// # Ok(())
77/// # }
78/// ```
79#[derive(Clone)]
80pub struct MmapFile {
81    /// Underlying MmapFileInner implementation
82    /// 
83    /// 底层的 MmapFileInner 实现
84    /// 
85    /// # Safety
86    /// AllocatedRange guarantees different threads write to non-overlapping regions
87    /// 
88    /// # Safety
89    /// 通过 AllocatedRange 保证不同线程写入不重叠的区域
90    inner: MmapFileInner,
91}
92
93impl MmapFile {
94    /// Create a new file and return (MmapFile, A) where A implements RangeAllocator
95    /// 
96    /// 创建新文件并返回 (MmapFile, A),其中 A 实现 RangeAllocator
97    /// 
98    /// If the file already exists, it will be truncated. The file will be pre-allocated
99    /// to the specified size.
100    /// 
101    /// 如果文件已存在会被截断。文件会被预分配到指定大小。
102    /// 
103    /// The returned tuple contains:
104    /// - `MmapFile`: File handle that can be shared among multiple workers
105    /// - `A`: Allocator used to allocate ranges
106    /// 
107    /// 返回的元组包含:
108    /// - `MmapFile`: 可以被多个 worker 共享的文件句柄
109    /// - `A`: 用于分配范围的分配器
110    /// 
111    /// # Parameters
112    /// - `path`: File path
113    /// - `size`: File size in bytes, must be > 0
114    /// 
115    /// # 参数
116    /// - `path`: 文件路径
117    /// - `size`: 文件大小(字节),必须大于 0
118    /// 
119    /// # Examples
120    /// 
121    /// ```
122    /// # use ranged_mmap::{MmapFile, allocator::{sequential::Allocator, ALIGNMENT}, Result};
123    /// # use tempfile::tempdir;
124    /// # fn main() -> Result<()> {
125    /// # let dir = tempdir()?;
126    /// # let path = dir.path().join("output.bin");
127    /// # use std::num::NonZeroU64;
128    /// // Create with allocator::sequential::Allocator (default)
129    /// // 使用 allocator::sequential::Allocator 创建(默认)
130    /// let (file, mut allocator) = MmapFile::create::<Allocator>(
131    ///     &path,
132    ///     NonZeroU64::new(ALIGNMENT * 2).unwrap()
133    /// )?;
134    ///
135    /// // Allocate some ranges (4K aligned)
136    /// // 分配一些范围(4K对齐)
137    /// let range1 = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
138    /// let range2 = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
139    ///
140    /// // Use file for concurrent writes
141    /// // 使用 file 进行并发写入
142    /// file.write_range(range1, &vec![0u8; ALIGNMENT as usize]);
143    /// file.write_range(range2, &vec![1u8; ALIGNMENT as usize]);
144    /// # Ok(())
145    /// # }
146    /// ```
147    /// 
148    /// # Errors
149    /// - Returns `InvalidFileSize` error if size is 0
150    /// - Returns corresponding I/O errors if file creation or memory mapping fails
151    /// 
152    /// # Errors
153    /// - 如果 size 为 0,返回 `InvalidFileSize` 错误
154    /// - 如果无法创建文件或映射内存,返回相应的 I/O 错误
155    #[inline]
156    pub fn create<A: RangeAllocator>(path: impl AsRef<Path>, size: NonZeroU64) -> Result<(Self, A)> {
157        let inner = MmapFileInner::create(path, size)?;
158        let allocator = A::new(size);
159        Ok((Self { inner }, allocator))
160    }
161
162    /// Create a new file with default allocator::sequential::Allocator
163    /// 
164    /// 使用默认的 allocator::sequential::Allocator 创建新文件
165    /// 
166    /// This is a convenience method equivalent to `create::<allocator::sequential::Allocator>(path, size)`.
167    /// 
168    /// 这是一个便捷方法,等价于 `create::<allocator::sequential::Allocator>(path, size)`。
169    #[inline]
170    pub fn create_default(path: impl AsRef<Path>, size: NonZeroU64) -> Result<(Self, allocator::sequential::Allocator)> {
171        Self::create::<allocator::sequential::Allocator>(path, size)
172    }
173
174    /// Open an existing file and map it to memory
175    /// 
176    /// 打开已存在的文件并映射到内存
177    /// 
178    /// The file must already exist and have a size > 0.
179    /// 
180    /// 文件必须已存在且大小大于 0。
181    /// 
182    /// # Parameters
183    /// - `path`: File path
184    /// 
185    /// # 参数
186    /// - `path`: 文件路径
187    /// 
188    /// # Examples
189    /// 
190    /// ```
191    /// # use ranged_mmap::{MmapFile, Result, allocator::sequential::Allocator};
192    /// # use tempfile::tempdir;
193    /// # fn main() -> Result<()> {
194    /// # let dir = tempdir()?;
195    /// # let path = dir.path().join("existing.bin");
196    /// # use std::num::NonZeroU64;
197    /// # // Create file first
198    /// # // 先创建文件
199    /// # let _ = MmapFile::create::<Allocator>(&path, NonZeroU64::new(1024).unwrap())?;
200    /// let (file, mut allocator) = MmapFile::open::<Allocator>(&path)?;
201    /// # Ok(())
202    /// # }
203    /// ```
204    #[inline]
205    pub fn open<A: RangeAllocator>(path: impl AsRef<Path>) -> Result<(Self, A)> {
206        let inner = MmapFileInner::open(path)?;
207        let size = inner.size();
208        let allocator = A::new(size);
209        Ok((Self { inner }, allocator))
210    }
211
212    /// Open an existing file with default allocator::sequential::Allocator
213    /// 
214    /// 使用默认的 allocator::sequential::Allocator 打开已存在的文件
215    /// 
216    /// This is a convenience method equivalent to `open::<allocator::sequential::Allocator>(path)`.
217    /// 
218    /// 这是一个便捷方法,等价于 `open::<allocator::sequential::Allocator>(path)`。
219    #[inline]
220    pub fn open_default(path: impl AsRef<Path>) -> Result<(Self, allocator::sequential::Allocator)> {
221        Self::open::<allocator::sequential::Allocator>(path)
222    }
223
224    /// Write to an allocated range
225    /// 
226    /// 写入已分配的范围
227    /// 
228    /// The type system guarantees that ranges are valid and non-overlapping.
229    /// Data length must exactly match the range length.
230    /// 
231    /// 通过类型系统保证范围是有效且不重叠的。
232    /// 数据长度必须与范围长度完全匹配。
233    /// 
234    /// Returns a [`WriteReceipt`] upon successful write, which can be used for subsequent
235    /// flush operations.
236    /// 
237    /// 成功写入后返回 [`WriteReceipt`] 凭据,可用于后续的刷新操作。
238    /// 
239    /// # Safety
240    /// 
241    /// This is a safe method because:
242    /// - `AllocatedRange` can only be created through `RangeAllocator`
243    /// - `RangeAllocator` allocates sequentially, guaranteeing non-overlapping ranges
244    /// - Compile-time type checking prevents all potential data races
245    /// 
246    /// # Safety
247    /// 
248    /// 这是一个安全的方法,因为:
249    /// - `AllocatedRange` 只能通过 `RangeAllocator` 创建
250    /// - `RangeAllocator` 顺序分配,保证范围不重叠
251    /// - 编译期类型检查防止了所有潜在的数据竞争
252    /// 
253    /// # Parameters
254    /// - `range`: Allocated file range
255    /// - `data`: Data to write, length must equal `range.len()`
256    /// 
257    /// # Returns
258    /// Returns [`WriteReceipt`] proving the range has been successfully written
259    /// 
260    /// # 参数
261    /// - `range`: 已分配的文件范围
262    /// - `data`: 要写入的数据,长度必须等于 `range.len()`
263    /// 
264    /// # 返回值
265    /// 返回 [`WriteReceipt`] 凭据,证明该范围已被成功写入
266    /// 
267    /// # Examples
268    /// 
269    /// ```
270    /// # use ranged_mmap::{MmapFile, Result, allocator::ALIGNMENT};
271    /// # use tempfile::tempdir;
272    /// # fn main() -> Result<()> {
273    /// # let dir = tempdir()?;
274    /// # let path = dir.path().join("output.bin");
275    /// # use std::num::NonZeroU64;
276    /// let (file, mut allocator) = MmapFile::create_default(&path, NonZeroU64::new(ALIGNMENT * 3).unwrap())?;
277    ///
278    /// // Allocate and write, obtaining a receipt (4K aligned)
279    /// // 分配并写入,获得凭据(4K对齐)
280    /// let range = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
281    /// let receipt = file.write_range(range, &vec![42u8; ALIGNMENT as usize]);
282    ///
283    /// // Use receipt to flush
284    /// // 使用凭据刷新
285    /// file.flush_range(receipt)?;
286    ///
287    /// // Concurrent writes to different ranges
288    /// // 并发写入不同范围
289    /// let range1 = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
290    /// let range2 = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
291    ///
292    /// let f1 = file.clone();
293    /// let f2 = file.clone();
294    ///
295    /// std::thread::scope(|s| {
296    ///     s.spawn(move || {
297    ///         let receipt = f1.write_range(range1, &vec![1u8; ALIGNMENT as usize]);
298    ///         f1.flush_range(receipt).unwrap();
299    ///     });
300    ///     s.spawn(move || {
301    ///         let receipt = f2.write_range(range2, &vec![2u8; ALIGNMENT as usize]);
302    ///         f2.flush_range(receipt).unwrap();
303    ///     });
304    /// });
305    /// # Ok(())
306    /// # }
307    /// ```
308    /// 
309    #[inline]
310    pub fn write_range(&self, range: AllocatedRange, data: &[u8]) -> WriteReceipt {
311        // Check data length matches
312        // 检查数据长度匹配
313        debug_assert!(
314            data.len() as u64 == range.len(),
315            "Data length {} doesn't match range length {}",
316            data.len(), range.len()
317        );
318
319        // Safety: RangeAllocator guarantees non-overlapping ranges
320        // Safety: RangeAllocator 保证范围不重叠
321        unsafe { self.inner.write_at(range.start(), data); }
322
323        // Return write receipt
324        // 返回写入凭据
325        WriteReceipt::new(range)
326    }
327
328    /// Write all data to the specified range
329    /// 
330    /// 在指定范围写入所有数据
331    /// 
332    /// This method is a convenience wrapper for `write_range`.
333    /// 
334    /// 这个方法是 `write_range` 的便捷版本。
335    /// 
336    /// # Parameters
337    /// - `range`: Allocated file range
338    /// - `data`: Data to write
339    /// 
340    /// # Returns
341    /// Returns [`WriteReceipt`] proving the range has been successfully written
342    /// 
343    /// # 参数
344    /// - `range`: 已分配的文件范围
345    /// - `data`: 要写入的数据
346    /// 
347    /// # 返回值
348    /// 返回 [`WriteReceipt`] 凭据,证明该范围已被成功写入
349    /// 
350    #[inline]
351    pub fn write_range_all(&self, range: AllocatedRange, data: &[u8]) -> WriteReceipt {
352        self.write_range(range, data)
353    }
354
355    /// Get file size
356    /// 
357    /// 获取文件大小
358    #[inline]
359    pub fn size(&self) -> NonZeroU64 {
360        self.inner.size()
361    }
362
363    /// Read data from the specified range
364    /// 
365    /// 在指定范围读取数据
366    /// 
367    /// Reads data from the memory mapping into the buffer.
368    /// 
369    /// 从内存映射中读取数据到缓冲区。
370    /// 
371    /// # Parameters
372    /// - `range`: Range to read
373    /// - `buf`: Buffer to receive data, length must be at least `range.len()`
374    /// 
375    /// # Returns
376    /// Number of bytes actually read
377    /// 
378    /// # 参数
379    /// - `range`: 要读取的范围
380    /// - `buf`: 接收数据的缓冲区,长度必须至少为 `range.len()`
381    /// 
382    /// # 返回值
383    /// 返回实际读取的字节数
384    pub fn read_range(&self, range: AllocatedRange, buf: &mut [u8]) -> Result<usize> {
385        let len = range.len() as usize;
386
387        if buf.len() < len {
388            return Err(Error::BufferTooSmall {
389                buffer_len: buf.len(),
390                range_len: range.len(),
391            });
392        }
393
394        // Safety: Read operations are safe
395        // Safety: 读取操作是安全的
396        unsafe { self.inner.read_at(range.start(), &mut buf[..len]) }
397    }
398
399    /// Flush data to disk asynchronously
400    /// 
401    /// 异步刷新数据到磁盘
402    /// 
403    /// Initiates an asynchronous flush operation without blocking for completion.
404    /// The operating system will write data to disk in the background.
405    /// 
406    /// 发起异步刷新操作,不会阻塞等待完成。操作系统会在后台将数据写入磁盘。
407    pub fn flush(&self) -> Result<()> {
408        unsafe { self.inner.flush() }
409    }
410
411    /// Flush data to disk synchronously
412    /// 
413    /// 同步刷新数据到磁盘
414    /// 
415    /// Synchronously flushes data in memory to disk, blocking until completion.
416    /// This is slower than `flush()` but guarantees data has been written to disk.
417    /// 
418    /// 同步将内存中的数据刷新到磁盘,阻塞直到完成。
419    /// 这比 `flush()` 慢,但保证数据已经写入磁盘。
420    /// 
421    /// # Safety
422    /// 
423    /// During the flush, the caller must ensure no other threads are modifying the
424    /// mapped memory. While sync itself is a safe operation, it is marked unsafe
425    /// for API consistency as it operates on data modified through unsafe methods.
426    /// 
427    /// # Safety
428    /// 
429    /// 在刷新期间,调用者需要确保没有其他线程正在修改映射的内存。
430    /// 虽然 sync 本身是安全的操作,但为了保持 API 一致性,
431    /// 它被标记为 unsafe,因为它操作的是通过 unsafe 方法修改的数据。
432    pub unsafe fn sync_all(&self) -> Result<()> {
433        unsafe { self.inner.sync_all() }
434    }
435
436    /// Flush a specific range to disk
437    /// 
438    /// 刷新指定区域到磁盘
439    /// 
440    /// Flushes only a portion of the file to disk, which can improve performance.
441    /// 
442    /// 只刷新文件的一部分到磁盘,可以提高性能。
443    /// 
444    /// By requiring a [`WriteReceipt`], this ensures only written ranges can be flushed,
445    /// providing compile-time safety guarantees.
446    /// 
447    /// 通过要求 [`WriteReceipt`] 凭据,确保只能刷新已写入的范围,
448    /// 提供编译期安全保证。
449    /// 
450    /// # Parameters
451    /// - `receipt`: Write receipt proving the range has been successfully written
452    /// 
453    /// # 参数
454    /// - `receipt`: 写入凭据,证明该范围已被成功写入
455    /// 
456    /// # Examples
457    /// 
458    /// ```
459    /// # use ranged_mmap::{MmapFile, Result, allocator::ALIGNMENT};
460    /// # use tempfile::tempdir;
461    /// # fn main() -> Result<()> {
462    /// # let dir = tempdir()?;
463    /// # let path = dir.path().join("output.bin");
464    /// # use std::num::NonZeroU64;
465    /// let (file, mut allocator) = MmapFile::create_default(&path, NonZeroU64::new(ALIGNMENT).unwrap())?;
466    /// let range = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
467    ///
468    /// // Write and get receipt
469    /// // 写入并获得凭据
470    /// let receipt = file.write_range(range, &vec![42u8; ALIGNMENT as usize]);
471    ///
472    /// // Can only flush ranges that have been written
473    /// // 只能刷新已写入的范围
474    /// file.flush_range(receipt)?;
475    /// # Ok(())
476    /// # }
477    /// ```
478    pub fn flush_range(&self, receipt: WriteReceipt) -> Result<()> {
479        let range = receipt.range();
480        unsafe { self.inner.flush_range(range.start(), range.len() as usize) }
481    }
482}
483
484/// Implement Debug for MmapFile
485/// 
486/// 为 MmapFile 实现 Debug
487impl std::fmt::Debug for MmapFile {
488    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489        f.debug_struct("MmapFile")
490            .field("inner", &self.inner)
491            .finish()
492    }
493}
494
495// Implement Send and Sync
496// Safety: AllocatedRange guarantees different threads write to non-overlapping regions
497// MmapFileInner already implements Send and Sync
498// 
499// 实现 Send 和 Sync
500// Safety: AllocatedRange 保证不同线程写入不重叠区域
501// MmapFileInner 已经实现了 Send 和 Sync
502unsafe impl Send for MmapFile {}
503unsafe impl Sync for MmapFile {}
504