ranged_mmap/file/mmap_file_inner.rs
1//! Unsafe lock-free file implementation based on memmap2
2//!
3//! 基于 memmap2 的 Unsafe 无锁文件实现
4
5use memmap2::MmapMut;
6use std::cell::UnsafeCell;
7use std::fs::OpenOptions;
8use std::path::Path;
9use std::sync::Arc;
10use std::num::NonZeroU64;
11use super::error::{Error, Result};
12
13/// High-performance memory-mapped file (Unsafe lock-free version)
14///
15/// 基于内存映射的高性能文件(Unsafe 无锁版本)
16///
17/// ⚠️ **Warning**: This is an unsafe version. Users must ensure that concurrent writes
18/// do not overlap.
19///
20/// ⚠️ **警告**:这是 unsafe 版本,用户需自行保证并发写入的范围不重叠。
21///
22/// It is recommended to use [`MmapFile`](super::MmapFile) + [`RangeAllocator`](super::RangeAllocator)
23/// for compile-time safety.
24///
25/// 推荐使用 [`MmapFile`](super::MmapFile) + [`RangeAllocator`](super::RangeAllocator) 实现编译期安全。
26///
27/// This file implementation is optimized for concurrent random write scenarios.
28///
29/// 专为并发随机写入场景优化的文件实现。
30///
31/// # Features
32///
33/// - **Zero-copy writes**: Write operations directly modify mapped memory without system calls
34/// - **Lock-free concurrency**: Concurrent writes to different regions require no locking for maximum performance
35/// - **Reference counting**: Can be cloned and shared among multiple workers
36/// - **Manual flushing**: Control when data is synchronized to disk for optimized batch operations
37/// - **Runtime agnostic**: Does not depend on any specific async runtime
38///
39/// # 特性
40///
41/// - **零拷贝写入**:写入操作直接修改映射内存,无需系统调用
42/// - **无锁并发**:不同区域的并发写入无需加锁,极致性能
43/// - **引用计数**:可以克隆并在多个 worker 间共享
44/// - **手动刷盘**:控制何时将数据同步到磁盘,优化批量操作
45/// - **运行时无关**:不依赖特定异步运行时,可用于任何场景
46///
47/// # Limitations
48///
49/// - File size must be specified at creation and cannot be dynamically expanded
50/// - Maximum file size is limited by system virtual memory
51/// - ⚠️ Users must ensure that concurrent writes do not overlap (runtime responsibility)
52///
53/// # 限制
54///
55/// - 创建时必须指定文件大小,不支持动态扩展
56/// - 文件大小上限受系统虚拟内存限制
57/// - ⚠️ 用户需要确保不会并发写入重叠的内存区域(运行时责任)
58///
59/// # Safety Notes
60///
61/// This implementation uses `UnsafeCell` to allow lock-free concurrent writes. As long as:
62/// - Different threads write to non-overlapping memory regions
63/// - No reads occur to the same region during writes
64///
65/// It is completely safe. However, these guarantees must be maintained by the user.
66///
67/// # 安全性说明
68///
69/// 这个实现使用 `UnsafeCell` 来允许无锁并发写入。只要:
70/// - 不同线程写入不重叠的内存区域
71/// - 不在写入同时读取同一区域
72///
73/// 那么就是完全安全的。但这些保证需要用户自行维护。
74///
75/// # Examples
76///
77/// ```
78/// # use ranged_mmap::{MmapFileInner, Result};
79/// # use tempfile::tempdir;
80/// # fn main() -> Result<()> {
81/// # let dir = tempdir()?;
82/// # let path = dir.path().join("download.bin");
83/// # use std::num::NonZeroU64;
84/// let file = MmapFileInner::create(&path, NonZeroU64::new(1024).unwrap())?;
85///
86/// // ⚠️ Users must ensure concurrent writes do not overlap
87/// // ⚠️ 用户需自行保证不会并发写入重叠区域
88/// let file1 = file.clone();
89/// let file2 = file.clone();
90///
91/// std::thread::scope(|s| {
92/// // Safety: Two threads write to non-overlapping regions [0, 512) and [512, 1024)
93/// // Safety: 两个线程写入不重叠的区域 [0, 512) 和 [512, 1024)
94/// s.spawn(|| unsafe { file1.write_at(0, &[1; 512]) });
95/// s.spawn(|| unsafe { file2.write_at(512, &[2; 512]) });
96/// });
97///
98/// unsafe { file.flush()?; }
99/// # Ok(())
100/// # }
101/// ```
102#[derive(Clone)]
103pub struct MmapFileInner {
104 /// Mutable reference to memory mapping, using UnsafeCell for interior mutability
105 ///
106 /// 内存映射的可变引用,使用 UnsafeCell 允许内部可变性
107 ///
108 /// # Safety
109 /// Safe as long as different threads write to non-overlapping regions
110 ///
111 /// # Safety
112 /// 只要不同线程写入不重叠的区域,就是安全的
113 mmap: Arc<UnsafeCell<MmapMut>>,
114
115 /// File size in bytes
116 ///
117 /// 文件大小
118 size: NonZeroU64,
119}
120
121impl MmapFileInner {
122 /// Create a new file and map it to memory
123 ///
124 /// 创建新文件并映射到内存
125 ///
126 /// If the file already exists, it will be truncated. The file will be pre-allocated
127 /// to the specified size.
128 ///
129 /// 如果文件已存在会被截断。文件会被预分配到指定大小。
130 ///
131 /// # Parameters
132 /// - `path`: File path
133 /// - `size`: File size in bytes, must be > 0
134 ///
135 /// # 参数
136 /// - `path`: 文件路径
137 /// - `size`: 文件大小(字节),必须大于 0
138 ///
139 /// # Examples
140 ///
141 /// ```
142 /// # use ranged_mmap::{MmapFileInner, Result};
143 /// # use tempfile::tempdir;
144 /// # fn main() -> Result<()> {
145 /// # let dir = tempdir()?;
146 /// # let path = dir.path().join("output.bin");
147 /// # use std::num::NonZeroU64;
148 /// // Create a 10MB file
149 /// // 创建 10MB 的文件
150 /// let file = MmapFileInner::create(&path, NonZeroU64::new(10 * 1024 * 1024).unwrap())?;
151 /// # Ok(())
152 /// # }
153 /// ```
154 ///
155 /// # Errors
156 /// - Returns `InvalidFileSize` error if size is 0
157 /// - Returns corresponding I/O errors if file creation or memory mapping fails
158 ///
159 /// # Errors
160 /// - 如果 size 为 0,返回 `InvalidFileSize` 错误
161 /// - 如果无法创建文件或映射内存,返回相应的 I/O 错误
162 pub fn create(path: impl AsRef<Path>, size: NonZeroU64) -> Result<Self> {
163
164 let path = path.as_ref();
165
166 // Create file and pre-allocate size
167 // 创建文件并预分配大小
168 let file = OpenOptions::new()
169 .read(true)
170 .write(true)
171 .create(true)
172 .truncate(true)
173 .open(path)?;
174
175 file.set_len(size.get())?;
176
177 // Create memory mapping
178 // 创建内存映射
179 let mmap = unsafe { MmapMut::map_mut(&file)? };
180
181 Ok(Self {
182 #[allow(clippy::arc_with_non_send_sync)]
183 mmap: Arc::new(UnsafeCell::new(mmap)),
184 size,
185 })
186 }
187
188 /// Open an existing file and map it to memory
189 ///
190 /// 打开已存在的文件并映射到内存
191 ///
192 /// The file must already exist and have a size > 0.
193 ///
194 /// 文件必须已存在且大小大于 0。
195 ///
196 /// # Parameters
197 /// - `path`: File path
198 ///
199 /// # 参数
200 /// - `path`: 文件路径
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// # use ranged_mmap::{MmapFileInner, Result};
206 /// # use tempfile::tempdir;
207 /// # fn main() -> Result<()> {
208 /// # let dir = tempdir()?;
209 /// # let path = dir.path().join("existing.bin");
210 /// # use std::num::NonZeroU64;
211 /// # // Create file first
212 /// # // 先创建文件
213 /// # let _ = MmapFileInner::create(&path, NonZeroU64::new(1024).unwrap())?;
214 /// let file = MmapFileInner::open(&path)?;
215 /// # Ok(())
216 /// # }
217 /// ```
218 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
219 let path = path.as_ref();
220
221 let file = OpenOptions::new()
222 .read(true)
223 .write(true)
224 .open(path)?;
225
226 let size = match file.metadata()?.len() {
227 0 => return Err(Error::EmptyFile),
228 size => NonZeroU64::new(size).unwrap(),
229 };
230
231 let mmap = unsafe { MmapMut::map_mut(&file)? };
232
233 Ok(Self {
234 #[allow(clippy::arc_with_non_send_sync)]
235 mmap: Arc::new(UnsafeCell::new(mmap)),
236 size,
237 })
238 }
239
240 /// Write data at the specified position (lock-free operation)
241 ///
242 /// 在指定位置写入数据(无锁操作)
243 ///
244 /// This is an extremely fast operation that writes directly to mapped memory
245 /// without requiring any locks.
246 ///
247 /// 这是一个极快的操作,直接写入映射内存,不需要任何锁。
248 ///
249 /// # Safety
250 ///
251 /// The caller must ensure:
252 /// - Different threads do not write to overlapping memory regions concurrently
253 /// - No reads occur to the same region during writes
254 ///
255 /// Violating these constraints leads to data races, which is undefined behavior.
256 ///
257 /// # Safety
258 ///
259 /// 调用者需要确保:
260 /// - 不同线程不会并发写入重叠的内存区域
261 /// - 不会在写入时读取同一区域
262 ///
263 /// 违反这些约束会导致数据竞争,这是未定义行为。
264 ///
265 /// # Parameters
266 /// - `offset`: Write position (byte offset from file start)
267 /// - `data`: Data to write
268 ///
269 /// # Returns
270 /// Number of bytes actually written
271 ///
272 /// # 参数
273 /// - `offset`: 写入位置(从文件开头的字节偏移)
274 /// - `data`: 要写入的数据
275 ///
276 /// # 返回值
277 /// 返回实际写入的字节数
278 ///
279 /// # Examples
280 ///
281 /// ```
282 /// # use ranged_mmap::{MmapFileInner, Result};
283 /// # use tempfile::tempdir;
284 /// # fn main() -> Result<()> {
285 /// # let dir = tempdir()?;
286 /// # let path = dir.path().join("output.bin");
287 /// # use std::num::NonZeroU64;
288 /// let file = MmapFileInner::create(&path, NonZeroU64::new(1024).unwrap())?;
289 ///
290 /// // Concurrent writes to non-overlapping regions using std::thread
291 /// // 使用 std::thread 并发写入不重叠区域
292 /// let file1 = file.clone();
293 /// let file2 = file.clone();
294 ///
295 /// std::thread::scope(|s| {
296 /// // Safety: Two threads write to non-overlapping regions [0, 5) and [100, 105)
297 /// // Safety: 两个线程写入不重叠的区域 [0, 5) 和 [100, 105)
298 /// s.spawn(|| unsafe { file1.write_at(0, b"hello") });
299 /// s.spawn(|| unsafe { file2.write_at(100, b"world") });
300 /// });
301 ///
302 /// unsafe { file.flush()?; }
303 /// # Ok(())
304 /// # }
305 /// ```
306 ///
307 #[inline]
308 pub unsafe fn write_at(&self, offset: u64, data: &[u8]) -> usize {
309 let offset_usize = offset as usize;
310 let len = data.len();
311
312 debug_assert!(
313 offset_usize.saturating_add(len) <= self.size.get() as usize,
314 "Write would exceed file size: offset={}, len={}, file_size={}",
315 offset, len, self.size.get()
316 );
317
318 // Safety: We assume the caller ensures different threads don't write to overlapping regions
319 // Safety: 我们假设调用者确保不同线程不会写入重叠区域
320 unsafe {
321 let mmap = &mut *self.mmap.get();
322 mmap[offset_usize..offset_usize + len].copy_from_slice(data);
323 }
324
325 len
326 }
327
328 /// Write all data at the specified position
329 ///
330 /// 在指定位置写入所有数据
331 ///
332 /// This method guarantees that all data is written, or returns an error if
333 /// insufficient space is available.
334 ///
335 /// 这个方法保证写入所有数据,如果空间不足会返回错误。
336 ///
337 /// # Safety
338 ///
339 /// The caller must ensure:
340 /// - Different threads do not write to overlapping memory regions concurrently
341 /// - No reads occur to the same region during writes
342 ///
343 /// # Safety
344 ///
345 /// 调用者需要确保:
346 /// - 不同线程不会并发写入重叠的内存区域
347 /// - 不会在写入时读取同一区域
348 ///
349 /// # Parameters
350 /// - `offset`: Write position
351 /// - `data`: Data to write
352 ///
353 /// # 参数
354 /// - `offset`: 写入位置
355 /// - `data`: 要写入的数据
356 ///
357 #[inline]
358 pub unsafe fn write_all_at(&self, offset: u64, data: &[u8]) {
359 unsafe { self.write_at(offset, data); }
360 }
361
362 /// Read data at the specified position
363 ///
364 /// 在指定位置读取数据
365 ///
366 /// Reads data from the memory mapping into the buffer.
367 ///
368 /// 从内存映射中读取数据到缓冲区。
369 ///
370 /// # Safety
371 ///
372 /// The caller must ensure no writes occur to the same region during reads.
373 /// Concurrent reads are safe, but concurrent read-write to the same region
374 /// leads to data races.
375 ///
376 /// # Safety
377 ///
378 /// 调用者需要确保不会在读取时写入同一区域。
379 /// 并发读取是安全的,但读写同一区域会导致数据竞争。
380 ///
381 /// # Parameters
382 /// - `offset`: Read position
383 /// - `buf`: Buffer to receive data
384 ///
385 /// # Returns
386 /// Number of bytes actually read
387 ///
388 /// # 参数
389 /// - `offset`: 读取位置
390 /// - `buf`: 接收数据的缓冲区
391 ///
392 /// # 返回值
393 /// 返回实际读取的字节数
394 pub unsafe fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
395 let offset_usize = offset as usize;
396 let len = buf.len();
397
398 if offset_usize >= self.size.get() as usize {
399 return Ok(0);
400 }
401
402 let available = (self.size.get() as usize).saturating_sub(offset_usize).min(len);
403
404 // Safety: Read operation is safe as long as no concurrent writes to the same region
405 // Safety: 读取操作,只要不和写入同一区域并发就是安全的
406 unsafe {
407 let mmap = &*self.mmap.get();
408 buf[..available].copy_from_slice(&mmap[offset_usize..offset_usize + available]);
409 }
410
411 Ok(available)
412 }
413
414 /// Flush data to disk asynchronously
415 ///
416 /// 异步刷新数据到磁盘
417 ///
418 /// Initiates an asynchronous flush operation without blocking for completion.
419 /// The operating system will write data to disk in the background.
420 ///
421 /// 发起异步刷新操作,不会阻塞等待完成。操作系统会在后台将数据写入磁盘。
422 ///
423 /// # Safety
424 ///
425 /// During the flush, the caller must ensure no other threads are modifying the
426 /// mapped memory. While flush itself is a safe operation, it is marked unsafe
427 /// for API consistency as it operates on data modified through unsafe methods.
428 ///
429 /// # Safety
430 ///
431 /// 在刷新期间,调用者需要确保没有其他线程正在修改映射的内存。
432 /// 虽然 flush 本身是安全的操作,但为了保持 API 一致性,
433 /// 它被标记为 unsafe,因为它操作的是通过 unsafe 方法修改的数据。
434 ///
435 /// # Examples
436 ///
437 /// ```
438 /// # use ranged_mmap::{MmapFileInner, Result};
439 /// # use tempfile::tempdir;
440 /// # fn main() -> Result<()> {
441 /// # let dir = tempdir()?;
442 /// # let path = dir.path().join("output.bin");
443 /// # use std::num::NonZeroU64;
444 /// let file = MmapFileInner::create(&path, NonZeroU64::new(1024).unwrap())?;
445 /// unsafe {
446 /// file.write_all_at(0, b"important data");
447 /// file.flush()?; // Flush asynchronously to disk
448 /// // 异步刷新到磁盘
449 /// }
450 /// # Ok(())
451 /// # }
452 /// ```
453 pub unsafe fn flush(&self) -> Result<()> {
454 unsafe {
455 let mmap = &*self.mmap.get();
456 Ok(mmap.flush_async()?)
457 }
458 }
459
460 /// Flush data to disk synchronously
461 ///
462 /// 同步刷新数据到磁盘
463 ///
464 /// Synchronously flushes data in memory to disk, blocking until completion.
465 /// This is slower than `flush()` but guarantees data has been written to disk.
466 ///
467 /// 同步将内存中的数据刷新到磁盘,阻塞直到完成。
468 /// 这比 `flush()` 慢,但保证数据已经写入磁盘。
469 ///
470 /// # Safety
471 ///
472 /// During the flush, the caller must ensure no other threads are modifying the
473 /// mapped memory. While sync itself is a safe operation, it is marked unsafe
474 /// for API consistency as it operates on data modified through unsafe methods.
475 ///
476 /// # Safety
477 ///
478 /// 在刷新期间,调用者需要确保没有其他线程正在修改映射的内存。
479 /// 虽然 sync 本身是安全的操作,但为了保持 API 一致性,
480 /// 它被标记为 unsafe,因为它操作的是通过 unsafe 方法修改的数据。
481 ///
482 /// # Examples
483 ///
484 /// ```
485 /// # use ranged_mmap::{MmapFileInner, Result};
486 /// # use tempfile::tempdir;
487 /// # fn main() -> Result<()> {
488 /// # let dir = tempdir()?;
489 /// # let path = dir.path().join("output.bin");
490 /// # use std::num::NonZeroU64;
491 /// let file = MmapFileInner::create(&path, NonZeroU64::new(1024).unwrap())?;
492 /// unsafe {
493 /// file.write_all_at(0, b"critical data");
494 /// file.sync_all()?; // Ensure data is written to disk
495 /// // 确保数据已写入磁盘
496 /// }
497 /// # Ok(())
498 /// # }
499 /// ```
500 pub unsafe fn sync_all(&self) -> Result<()> {
501 unsafe {
502 let mmap = &*self.mmap.get();
503 Ok(mmap.flush()?)
504 }
505 }
506
507 /// Flush a specific range to disk
508 ///
509 /// 刷新指定区域到磁盘
510 ///
511 /// Flushes only a portion of the file to disk, which can improve performance.
512 ///
513 /// 只刷新文件的一部分到磁盘,可以提高性能。
514 ///
515 /// # Safety
516 ///
517 /// During the flush, the caller must ensure no other threads are modifying
518 /// memory in that region.
519 ///
520 /// # Safety
521 ///
522 /// 在刷新期间,调用者需要确保没有其他线程正在修改该区域的内存。
523 ///
524 /// # Parameters
525 /// - `offset`: Start position of the flush range
526 /// - `len`: Length of the flush range
527 ///
528 /// # 参数
529 /// - `offset`: 刷新区域的起始位置
530 /// - `len`: 刷新区域的长度
531 pub unsafe fn flush_range(&self, offset: u64, len: usize) -> Result<()> {
532 let offset_usize = offset as usize;
533
534 debug_assert!(
535 offset_usize.saturating_add(len) <= self.size.get() as usize,
536 "Flush range exceeds file size: offset={}, len={}, file_size={}",
537 offset, len, self.size.get()
538 );
539
540 unsafe {
541 let mmap = &*self.mmap.get();
542 Ok(mmap.flush_async_range(offset_usize, len)?)
543 }
544 }
545
546 /// Get file size
547 ///
548 /// 获取文件大小
549 #[inline]
550 pub fn size(&self) -> NonZeroU64 {
551 self.size
552 }
553
554 /// Fill the entire file with a specified byte
555 ///
556 /// 填充整个文件为指定字节
557 ///
558 /// Efficiently fills the entire file with the specified value.
559 ///
560 /// 高效地将整个文件填充为指定值。
561 ///
562 /// # Safety
563 ///
564 /// The caller must ensure no other threads are reading or writing any part
565 /// of the file during the fill. This operation modifies the entire file content.
566 ///
567 /// # Safety
568 ///
569 /// 调用者需要确保在填充期间没有其他线程正在读写文件的任何部分。
570 /// 此操作会修改整个文件内容。
571 ///
572 /// # Parameters
573 /// - `byte`: Fill byte value
574 ///
575 /// # 参数
576 /// - `byte`: 填充字节
577 pub unsafe fn fill(&self, byte: u8) -> Result<()> {
578 unsafe {
579 let mmap = &mut *self.mmap.get();
580 mmap.fill(byte);
581 }
582 Ok(())
583 }
584
585 /// Zero out the entire file
586 ///
587 /// 清零整个文件
588 ///
589 /// Fills the entire file with 0.
590 ///
591 /// 将整个文件填充为 0。
592 ///
593 /// # Safety
594 ///
595 /// The caller must ensure no other threads are reading or writing any part
596 /// of the file during the zeroing. This operation modifies the entire file content.
597 ///
598 /// # Safety
599 ///
600 /// 调用者需要确保在清零期间没有其他线程正在读写文件的任何部分。
601 /// 此操作会修改整个文件内容。
602 pub unsafe fn zero(&self) -> Result<()> {
603 unsafe { self.fill(0) }
604 }
605
606 /// Read a specific region into a new Vec
607 ///
608 /// 读取指定区域到新的 Vec
609 ///
610 /// This copies data into a new Vec.
611 ///
612 /// 这会拷贝数据到一个新的 Vec 中。
613 ///
614 /// # Safety
615 ///
616 /// The caller must ensure no other threads are writing to the region during the read.
617 ///
618 /// # Safety
619 ///
620 /// 调用者需要确保在读取期间没有其他线程正在写入该区域。
621 ///
622 /// # Parameters
623 /// - `offset`: Read start position
624 /// - `len`: Read length
625 ///
626 /// # 参数
627 /// - `offset`: 读取起始位置
628 /// - `len`: 读取长度
629 pub unsafe fn read_slice(&self, offset: u64, len: usize) -> Result<Vec<u8>> {
630 let mut buf = vec![0u8; len];
631 let bytes_read = unsafe { self.read_at(offset, &mut buf)? };
632 buf.truncate(bytes_read);
633 Ok(buf)
634 }
635
636 /// Get a raw pointer to the underlying mmap
637 ///
638 /// 获取底层 mmap 的原始指针
639 ///
640 /// # Safety
641 ///
642 /// The caller must ensure:
643 /// - No multiple mutable references are created
644 /// - The pointer lifetime does not exceed MmapFileInner
645 ///
646 /// # Safety
647 ///
648 /// 调用者需要确保:
649 /// - 不会创建多个可变引用
650 /// - 指针的生命周期不会超过 MmapFileInner
651 #[inline]
652 pub fn as_ptr(&self) -> *const u8 {
653 unsafe {
654 let mmap = &*self.mmap.get();
655 mmap.as_ptr()
656 }
657 }
658
659 /// Get a mutable raw pointer to the underlying mmap
660 ///
661 /// 获取底层 mmap 的可变原始指针
662 ///
663 /// # Safety
664 ///
665 /// The caller must ensure:
666 /// - No multiple mutable references are created
667 /// - The pointer lifetime does not exceed MmapFileInner
668 /// - No concurrent access to overlapping memory regions
669 ///
670 /// # Safety
671 ///
672 /// 调用者需要确保:
673 /// - 不会创建多个可变引用
674 /// - 指针的生命周期不会超过 MmapFileInner
675 /// - 不会并发访问重叠的内存区域
676 #[inline]
677 pub unsafe fn as_mut_ptr(&self) -> *mut u8 {
678 unsafe {
679 let mmap = &mut *self.mmap.get();
680 mmap.as_mut_ptr()
681 }
682 }
683}
684
685/// Implement Debug for MmapFileInner
686///
687/// 为 MmapFileInner 实现 Debug
688impl std::fmt::Debug for MmapFileInner {
689 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
690 f.debug_struct("MmapFileInner")
691 .field("size", &self.size)
692 .field("mmap", &"MmapMut")
693 .finish()
694 }
695}
696
697// Implement Send and Sync
698// Safety: Safe as long as users ensure different threads write to non-overlapping regions
699//
700// 实现 Send 和 Sync
701// Safety: 只要用户确保不同线程写入不重叠区域,就是安全的
702unsafe impl Send for MmapFileInner {}
703unsafe impl Sync for MmapFileInner {}
704