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