ranged_mmap/file/range.rs
1//! File range and write receipt types
2//!
3//! 文件范围和写入凭据类型
4
5use std::ops::Range;
6use super::allocator::{align_up, align_down};
7
8/// Result of `split_at_align_up`
9///
10/// `split_at_align_up` 的返回结果
11///
12/// # Variants
13///
14/// - `Split`: Successfully split into two non-empty ranges (low, high)
15/// - `Low`: Only low range exists (split point >= end)
16/// - `OutOfBounds`: Position exceeds range length (pos > len)
17///
18/// # 变体
19///
20/// - `Split`: 成功拆分为两个非空范围 (low, high)
21/// - `Low`: 仅存在低范围(分割点 >= end)
22/// - `OutOfBounds`: 位置超出范围长度 (pos > len)
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub enum SplitUpResult {
26 /// Successfully split into two ranges
27 ///
28 /// 成功拆分为两个范围
29 Split {
30 /// Lower range [start, split_point)
31 ///
32 /// 低范围 [start, split_point)
33 low: AllocatedRange,
34 /// Higher range [split_point, end)
35 ///
36 /// 高范围 [split_point, end)
37 high: AllocatedRange,
38 },
39 /// Only low range exists (split point aligned to or beyond end)
40 ///
41 /// 仅存在低范围(分割点对齐到 end 或超出)
42 Low(AllocatedRange),
43 /// Position out of bounds (pos > len)
44 ///
45 /// 位置越界 (pos > len)
46 OutOfBounds(AllocatedRange),
47}
48
49impl SplitUpResult {
50 /// Returns true if the range was successfully split into two parts
51 ///
52 /// 如果范围成功拆分为两部分则返回 true
53 #[inline]
54 pub fn is_split(&self) -> bool {
55 matches!(self, SplitUpResult::Split { .. })
56 }
57
58 /// Returns true if position was out of bounds
59 ///
60 /// 如果位置越界则返回 true
61 #[inline]
62 pub fn is_out_of_bounds(&self) -> bool {
63 matches!(self, SplitUpResult::OutOfBounds(_))
64 }
65
66 /// Get the low range (always available except OutOfBounds)
67 ///
68 /// 获取低范围(除 OutOfBounds 外始终可用)
69 #[inline]
70 pub fn low(&self) -> Option<AllocatedRange> {
71 match self {
72 SplitUpResult::Split { low, .. } => Some(*low),
73 SplitUpResult::Low(range) => Some(*range),
74 SplitUpResult::OutOfBounds(_) => None,
75 }
76 }
77
78 /// Get the high range if split succeeded
79 ///
80 /// 获取高范围(仅拆分成功时可用)
81 #[inline]
82 pub fn high(&self) -> Option<AllocatedRange> {
83 match self {
84 SplitUpResult::Split { high, .. } => Some(*high),
85 _ => None,
86 }
87 }
88}
89
90/// Result of `split_at_align_down`
91///
92/// `split_at_align_down` 的返回结果
93///
94/// # Variants
95///
96/// - `Split`: Successfully split into two non-empty ranges (low, high)
97/// - `High`: Only high range exists (split point <= start)
98/// - `OutOfBounds`: Position exceeds range length (pos > len)
99///
100/// # 变体
101///
102/// - `Split`: 成功拆分为两个非空范围 (low, high)
103/// - `High`: 仅存在高范围(分割点 <= start)
104/// - `OutOfBounds`: 位置超出范围长度 (pos > len)
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
107pub enum SplitDownResult {
108 /// Successfully split into two ranges
109 ///
110 /// 成功拆分为两个范围
111 Split {
112 /// Lower range [start, split_point)
113 ///
114 /// 低范围 [start, split_point)
115 low: AllocatedRange,
116 /// Higher range [split_point, end)
117 ///
118 /// 高范围 [split_point, end)
119 high: AllocatedRange,
120 },
121 /// Only high range exists (split point aligned to or before start)
122 ///
123 /// 仅存在高范围(分割点对齐到 start 或之前)
124 High(AllocatedRange),
125 /// Position out of bounds (pos > len)
126 ///
127 /// 位置越界 (pos > len)
128 OutOfBounds(AllocatedRange),
129}
130
131impl SplitDownResult {
132 /// Returns true if the range was successfully split into two parts
133 ///
134 /// 如果范围成功拆分为两部分则返回 true
135 #[inline]
136 pub fn is_split(&self) -> bool {
137 matches!(self, SplitDownResult::Split { .. })
138 }
139
140 /// Returns true if position was out of bounds
141 ///
142 /// 如果位置越界则返回 true
143 #[inline]
144 pub fn is_out_of_bounds(&self) -> bool {
145 matches!(self, SplitDownResult::OutOfBounds(_))
146 }
147
148 /// Get the low range if split succeeded
149 ///
150 /// 获取低范围(仅拆分成功时可用)
151 #[inline]
152 pub fn low(&self) -> Option<AllocatedRange> {
153 match self {
154 SplitDownResult::Split { low, .. } => Some(*low),
155 _ => None,
156 }
157 }
158
159 /// Get the high range (always available except OutOfBounds)
160 ///
161 /// 获取高范围(除 OutOfBounds 外始终可用)
162 #[inline]
163 pub fn high(&self) -> Option<AllocatedRange> {
164 match self {
165 SplitDownResult::Split { high, .. } => Some(*high),
166 SplitDownResult::High(range) => Some(*range),
167 SplitDownResult::OutOfBounds(_) => None,
168 }
169 }
170}
171
172/// Allocated file range
173///
174/// 已分配的文件范围
175///
176/// Represents a valid range `[start, end)` allocated through [`RangeAllocator`](super::RangeAllocator).
177/// This type can only be created through the allocator, guaranteeing that all ranges are non-overlapping.
178///
179/// 表示通过 [`RangeAllocator`](super::RangeAllocator) 分配的有效范围 `[start, end)`。
180/// 此类型只能通过分配器创建,保证所有范围不重叠。
181///
182/// # Range Format
183///
184/// Uses half-open interval `[start, end)`:
185/// - `start`: Inclusive start position
186/// - `end`: Exclusive end position
187///
188/// For example: `AllocatedRange { start: 0, end: 10 }` represents bytes 0-9 (10 bytes total)
189///
190/// # 范围格式
191///
192/// 使用左闭右开区间 `[start, end)`:
193/// - `start`: 包含的起始位置
194/// - `end`: 不包含的结束位置
195///
196/// 例如:`AllocatedRange { start: 0, end: 10 }` 表示字节 0-9(共 10 字节)
197///
198/// # Safety Guarantees
199///
200/// - `start` is always ≤ `end`
201/// - Can only be created through the allocator, preventing overlaps
202/// - Provides immutable access, preventing modification
203///
204/// # 安全性保证
205///
206/// - `start` 总是小于等于 `end`
207/// - 只能通过分配器创建,防止重叠
208/// - 提供不可变访问,防止修改
209///
210/// # Examples
211///
212/// ```
213/// # use ranged_mmap::{MmapFile, Result, allocator::ALIGNMENT};
214/// # use tempfile::tempdir;
215/// # fn main() -> Result<()> {
216/// # let dir = tempdir()?;
217/// # let path = dir.path().join("output.bin");
218/// # use std::num::NonZeroU64;
219/// let (file, mut allocator) = MmapFile::create_default(&path, NonZeroU64::new(ALIGNMENT).unwrap())?;
220/// let range = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
221///
222/// // Get range information (4K aligned)
223/// // 获取范围信息(4K对齐)
224/// assert_eq!(range.start(), 0);
225/// assert_eq!(range.end(), ALIGNMENT);
226/// assert_eq!(range.len(), ALIGNMENT);
227///
228/// let (start, end) = range.as_range_tuple();
229/// assert_eq!(start, 0);
230/// assert_eq!(end, ALIGNMENT);
231/// # Ok(())
232/// # }
233/// ```
234#[derive(Debug, Clone, Copy, PartialEq, Eq)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
236pub struct AllocatedRange {
237 /// Range start position (inclusive)
238 ///
239 /// 范围起始位置(包含)
240 start: u64,
241
242 /// Range end position (exclusive)
243 ///
244 /// 范围结束位置(不包含)
245 end: u64,
246}
247
248impl AllocatedRange {
249 /// Internal constructor (crate-visible only, no validation)
250 ///
251 /// 内部构造函数(仅 crate 内可见,不进行验证)
252 ///
253 /// Creates a range using half-open interval `[start, end)`. No validation is performed.
254 ///
255 /// 使用左闭右开区间 `[start, end)` 创建范围。不进行验证。
256 #[inline]
257 pub(crate) fn from_range_unchecked(start: u64, end: u64) -> Self {
258 Self { start, end }
259 }
260
261 /// Get the start position
262 ///
263 /// 获取起始位置
264 ///
265 /// # Returns
266 /// The start position of the range (inclusive)
267 ///
268 /// # 返回值
269 /// 返回范围的起始位置(包含)
270 #[inline]
271 pub fn start(&self) -> u64 {
272 self.start
273 }
274
275 /// Get the end position
276 ///
277 /// 获取结束位置
278 ///
279 /// # Returns
280 /// The end position of the range (exclusive)
281 ///
282 /// # 返回值
283 /// 返回范围的结束位置(不包含)
284 #[inline]
285 pub fn end(&self) -> u64 {
286 self.end
287 }
288
289 /// Get the length of the range in bytes
290 ///
291 /// 获取范围的长度(字节数)
292 #[inline]
293 pub fn len(&self) -> u64 {
294 self.end - self.start
295 }
296
297 /// Check if the range is empty
298 ///
299 /// 检查范围是否为空
300 #[inline]
301 pub fn is_empty(&self) -> bool {
302 self.start == self.end
303 }
304
305
306 /// Split the range at the given relative position with 4K upper alignment
307 ///
308 /// 在给定相对位置以4K上对齐方式拆分范围
309 ///
310 /// The split point is calculated as `align_up(start + pos)`.
311 ///
312 /// 分割点计算为 `align_up(start + pos)`。
313 ///
314 /// # Parameters
315 /// - `pos`: Relative offset from the start of the range.
316 ///
317 /// # Returns
318 /// - `SplitUpResult::Split { low, high }`: Successfully split into [start, split) and [split, end)
319 /// - `SplitUpResult::Low`: Only low range exists (split point >= end)
320 /// - `SplitUpResult::OutOfBounds`: Position exceeds range length (pos > len)
321 ///
322 /// # 参数
323 /// - `pos`: 从范围起始位置开始的相对偏移量。
324 ///
325 /// # 返回值
326 /// - `SplitUpResult::Split { low, high }`: 成功拆分为 [start, split) 和 [split, end)
327 /// - `SplitUpResult::Low`: 仅存在低范围(分割点 >= end)
328 /// - `SplitUpResult::OutOfBounds`: 位置超出范围长度 (pos > len)
329 ///
330 /// # Examples
331 /// ```ignore
332 /// # use ranged_mmap::file::range::{AllocatedRange, SplitUpResult};
333 /// let range = AllocatedRange::from_range_unchecked(0, 8192);
334 /// match range.split_at_align_up(100) {
335 /// SplitUpResult::Split { low, high } => {
336 /// assert_eq!(low.end(), 4096); // Aligned up from 100
337 /// assert_eq!(high.start(), 4096);
338 /// }
339 /// _ => panic!("expected split"),
340 /// }
341 /// ```
342 #[inline]
343 pub fn split_at_align_up(&self, pos: u64) -> SplitUpResult {
344 let start = self.start;
345 let end = self.end;
346 let len = self.len();
347
348 if pos > len {
349 return SplitUpResult::OutOfBounds(*self);
350 }
351
352 let split_point = align_up(start + pos);
353
354 if split_point >= end {
355 SplitUpResult::Low(*self)
356 } else {
357 SplitUpResult::Split {
358 low: AllocatedRange::from_range_unchecked(start, split_point),
359 high: AllocatedRange::from_range_unchecked(split_point, end),
360 }
361 }
362 }
363
364 /// Split the range at the given relative position with 4K lower alignment
365 ///
366 /// 在给定相对位置以4K下对齐方式拆分范围
367 ///
368 /// The split point is calculated as `align_down(start + pos)`.
369 ///
370 /// 分割点计算为 `align_down(start + pos)`。
371 ///
372 /// # Parameters
373 /// - `pos`: Relative offset from the start of the range.
374 ///
375 /// # Returns
376 /// - `SplitDownResult::Split { low, high }`: Successfully split into [start, split) and [split, end)
377 /// - `SplitDownResult::High`: Only high range exists (split point <= start)
378 /// - `SplitDownResult::OutOfBounds`: Position exceeds range length (pos > len)
379 ///
380 /// # 参数
381 /// - `pos`: 从范围起始位置开始的相对偏移量。
382 ///
383 /// # 返回值
384 /// - `SplitDownResult::Split { low, high }`: 成功拆分为 [start, split) 和 [split, end)
385 /// - `SplitDownResult::High`: 仅存在高范围(分割点 <= start)
386 /// - `SplitDownResult::OutOfBounds`: 位置超出范围长度 (pos > len)
387 ///
388 /// # Examples
389 /// ```ignore
390 /// # use ranged_mmap::file::range::{AllocatedRange, SplitDownResult};
391 /// let range = AllocatedRange::from_range_unchecked(0, 8192);
392 /// match range.split_at_align_down(5000) {
393 /// SplitDownResult::Split { low, high } => {
394 /// assert_eq!(low.end(), 4096); // Aligned down from 5000
395 /// assert_eq!(high.start(), 4096);
396 /// }
397 /// _ => panic!("expected split"),
398 /// }
399 /// ```
400 #[inline]
401 pub fn split_at_align_down(&self, pos: u64) -> SplitDownResult {
402 let start = self.start;
403 let end = self.end;
404 let len = self.len();
405
406 if pos > len {
407 return SplitDownResult::OutOfBounds(*self);
408 }
409
410 let split_point = align_down(start + pos);
411
412 if split_point <= start {
413 SplitDownResult::High(*self)
414 } else {
415 SplitDownResult::Split {
416 low: AllocatedRange::from_range_unchecked(start, split_point),
417 high: AllocatedRange::from_range_unchecked(split_point, end),
418 }
419 }
420 }
421
422 /// Get the range as a tuple (start, end)
423 ///
424 /// 获取范围的元组表示 (start, end)
425 ///
426 /// Returns half-open interval `(start, end)`.
427 ///
428 /// 返回左闭右开区间 `(start, end)`。
429 #[inline]
430 pub fn as_range_tuple(&self) -> (u64, u64) {
431 (self.start, self.end)
432 }
433
434 /// Convert to standard Range<u64>
435 ///
436 /// 转换为标准 Range<u64>
437 ///
438 /// Returns half-open interval `start..end`.
439 ///
440 /// 返回左闭右开区间 `start..end`。
441 #[inline]
442 pub fn as_range(&self) -> Range<u64> {
443 self.start..self.end
444 }
445}
446
447impl From<AllocatedRange> for Range<u64> {
448 #[inline]
449 fn from(range: AllocatedRange) -> Self {
450 range.as_range()
451 }
452}
453
454/// Write receipt
455///
456/// 写入凭据
457///
458/// A receipt proving that a range has been successfully written.
459///
460/// This receipt can only be obtained after successfully writing through
461/// [`MmapFile::write_range`](super::MmapFile::write_range), and can only be used to flush
462/// the corresponding range. This provides compile-time safety guarantees:
463/// - Can only flush ranges that have been written
464/// - Cannot flush ranges that have not been written
465///
466/// 证明某个范围已被成功写入的凭据。
467///
468/// 只有通过 [`MmapFile::write_range`](super::MmapFile::write_range) 成功写入后才能获得此凭据,
469/// 并且只能用于刷新对应的范围。这提供了编译期的安全保证:
470/// - 只能刷新已写入的范围
471/// - 不能刷新未写入的范围
472///
473/// # Examples
474///
475/// ```
476/// # use ranged_mmap::{MmapFile, Result, allocator::ALIGNMENT};
477/// # use tempfile::tempdir;
478/// # fn main() -> Result<()> {
479/// # let dir = tempdir()?;
480/// # let path = dir.path().join("output.bin");
481/// # use std::num::NonZeroU64;
482/// let (file, mut allocator) = MmapFile::create_default(&path, NonZeroU64::new(ALIGNMENT).unwrap())?;
483/// let range = allocator.allocate(NonZeroU64::new(ALIGNMENT).unwrap()).unwrap();
484///
485/// // Write and get receipt
486/// // 写入并获得凭据
487/// let receipt = file.write_range(range, &vec![42u8; ALIGNMENT as usize]);
488///
489/// // Use receipt to flush the range
490/// // 使用凭据刷新该范围
491/// file.flush_range(receipt)?;
492/// # Ok(())
493/// # }
494/// ```
495#[derive(Debug, Clone, Copy, PartialEq, Eq)]
496#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
497pub struct WriteReceipt {
498 /// The range that was written
499 ///
500 /// 已写入的范围
501 range: AllocatedRange,
502}
503
504impl WriteReceipt {
505 /// Internal constructor (crate-visible only)
506 ///
507 /// 内部构造函数(仅 crate 内可见)
508 #[inline]
509 pub(crate) fn new(range: AllocatedRange) -> Self {
510 Self { range }
511 }
512
513 /// Get the range corresponding to this receipt
514 ///
515 /// 获取凭据对应的范围
516 #[inline]
517 pub fn range(&self) -> AllocatedRange {
518 self.range
519 }
520
521 /// Get the start position of the range
522 ///
523 /// 获取范围的起始位置
524 #[inline]
525 pub fn start(&self) -> u64 {
526 self.range.start()
527 }
528
529 /// Get the end position of the range
530 ///
531 /// 获取范围的结束位置
532 #[inline]
533 pub fn end(&self) -> u64 {
534 self.range.end()
535 }
536
537 /// Get the length of the range
538 ///
539 /// 获取范围的长度
540 #[inline]
541 pub fn len(&self) -> u64 {
542 self.range.len()
543 }
544
545 /// Check if the range is empty
546 ///
547 /// 检查范围是否为空
548 #[inline]
549 pub fn is_empty(&self) -> bool {
550 self.range.is_empty()
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557 use super::super::allocator::ALIGNMENT;
558
559 // ========== split_at_align_up tests ==========
560
561 #[test]
562 fn test_split_at_align_up_basic() {
563 // Range [0, 8192), split at pos 100 -> align_up(100) = 4096
564 let range = AllocatedRange::from_range_unchecked(0, 8192);
565 match range.split_at_align_up(100) {
566 SplitUpResult::Split { low, high } => {
567 assert_eq!(low.start(), 0);
568 assert_eq!(low.end(), ALIGNMENT);
569 assert_eq!(high.start(), ALIGNMENT);
570 assert_eq!(high.end(), 8192);
571 }
572 _ => panic!("expected split"),
573 }
574 }
575
576 #[test]
577 fn test_split_at_align_up_already_aligned() {
578 // Range [0, 8192), split at pos 4096 -> align_up(4096) = 4096
579 let range = AllocatedRange::from_range_unchecked(0, 8192);
580 match range.split_at_align_up(ALIGNMENT) {
581 SplitUpResult::Split { low, high } => {
582 assert_eq!(low.start(), 0);
583 assert_eq!(low.end(), ALIGNMENT);
584 assert_eq!(high.start(), ALIGNMENT);
585 assert_eq!(high.end(), 8192);
586 }
587 _ => panic!("expected split"),
588 }
589 }
590
591 #[test]
592 fn test_split_at_align_up_returns_low() {
593 // Range [0, 4096), split at pos 100 -> align_up(100) = 4096 >= end
594 let range = AllocatedRange::from_range_unchecked(0, ALIGNMENT);
595 match range.split_at_align_up(100) {
596 SplitUpResult::Low(low) => {
597 assert_eq!(low.start(), 0);
598 assert_eq!(low.end(), ALIGNMENT);
599 }
600 _ => panic!("expected Low"),
601 }
602 }
603
604 #[test]
605 fn test_split_at_align_up_pos_beyond_len() {
606 // Range [0, 4096), split at pos 5000 -> pos > len, returns OutOfBounds
607 let range = AllocatedRange::from_range_unchecked(0, ALIGNMENT);
608 match range.split_at_align_up(5000) {
609 SplitUpResult::OutOfBounds(r) => {
610 assert_eq!(r.start(), 0);
611 assert_eq!(r.end(), ALIGNMENT);
612 }
613 _ => panic!("expected OutOfBounds"),
614 }
615 }
616
617 #[test]
618 fn test_split_at_align_up_pos_zero() {
619 // Range [0, 8192), split at pos 0 -> align_up(0) = 0
620 // split_point = 0, which is not >= end (8192), so should split with empty low
621 let range = AllocatedRange::from_range_unchecked(0, 8192);
622 match range.split_at_align_up(0) {
623 SplitUpResult::Split { low, high } => {
624 assert_eq!(low.start(), 0);
625 assert_eq!(low.end(), 0);
626 assert!(low.is_empty());
627 assert_eq!(high.start(), 0);
628 assert_eq!(high.end(), 8192);
629 }
630 _ => panic!("expected Split"),
631 }
632 }
633
634 #[test]
635 fn test_split_at_align_up_non_zero_start() {
636 // Range [4096, 12288), split at pos 100 -> align_up(4096 + 100) = 8192
637 let range = AllocatedRange::from_range_unchecked(ALIGNMENT, 3 * ALIGNMENT);
638 match range.split_at_align_up(100) {
639 SplitUpResult::Split { low, high } => {
640 assert_eq!(low.start(), ALIGNMENT);
641 assert_eq!(low.end(), 2 * ALIGNMENT);
642 assert_eq!(high.start(), 2 * ALIGNMENT);
643 assert_eq!(high.end(), 3 * ALIGNMENT);
644 }
645 _ => panic!("expected split"),
646 }
647 }
648
649 // ========== split_at_align_down tests ==========
650
651 #[test]
652 fn test_split_at_align_down_basic() {
653 // Range [0, 8192), split at pos 5000 -> align_down(5000) = 4096
654 let range = AllocatedRange::from_range_unchecked(0, 8192);
655 match range.split_at_align_down(5000) {
656 SplitDownResult::Split { low, high } => {
657 assert_eq!(low.start(), 0);
658 assert_eq!(low.end(), ALIGNMENT);
659 assert_eq!(high.start(), ALIGNMENT);
660 assert_eq!(high.end(), 8192);
661 }
662 _ => panic!("expected split"),
663 }
664 }
665
666 #[test]
667 fn test_split_at_align_down_already_aligned() {
668 // Range [0, 8192), split at pos 4096 -> align_down(4096) = 4096
669 let range = AllocatedRange::from_range_unchecked(0, 8192);
670 match range.split_at_align_down(ALIGNMENT) {
671 SplitDownResult::Split { low, high } => {
672 assert_eq!(low.start(), 0);
673 assert_eq!(low.end(), ALIGNMENT);
674 assert_eq!(high.start(), ALIGNMENT);
675 assert_eq!(high.end(), 8192);
676 }
677 _ => panic!("expected split"),
678 }
679 }
680
681 #[test]
682 fn test_split_at_align_down_returns_high() {
683 // Range [0, 8192), split at pos 100 -> align_down(100) = 0 <= start
684 let range = AllocatedRange::from_range_unchecked(0, 8192);
685 match range.split_at_align_down(100) {
686 SplitDownResult::High(high) => {
687 assert_eq!(high.start(), 0);
688 assert_eq!(high.end(), 8192);
689 }
690 _ => panic!("expected High"),
691 }
692 }
693
694 #[test]
695 fn test_split_at_align_down_pos_beyond_len() {
696 // Range [0, 4096), split at pos 5000 -> pos > len, returns OutOfBounds
697 let range = AllocatedRange::from_range_unchecked(0, ALIGNMENT);
698 match range.split_at_align_down(5000) {
699 SplitDownResult::OutOfBounds(r) => {
700 assert_eq!(r.start(), 0);
701 assert_eq!(r.end(), ALIGNMENT);
702 }
703 _ => panic!("expected OutOfBounds"),
704 }
705 }
706
707 #[test]
708 fn test_split_at_align_down_pos_zero() {
709 // Range [0, 8192), split at pos 0 -> align_down(0) = 0 <= start
710 let range = AllocatedRange::from_range_unchecked(0, 8192);
711 match range.split_at_align_down(0) {
712 SplitDownResult::High(high) => {
713 assert_eq!(high.start(), 0);
714 assert_eq!(high.end(), 8192);
715 }
716 _ => panic!("expected High"),
717 }
718 }
719
720 #[test]
721 fn test_split_at_align_down_non_zero_start() {
722 // Range [4096, 12288), split at pos 5000 -> align_down(4096 + 5000) = 8192
723 let range = AllocatedRange::from_range_unchecked(ALIGNMENT, 3 * ALIGNMENT);
724 match range.split_at_align_down(5000) {
725 SplitDownResult::Split { low, high } => {
726 assert_eq!(low.start(), ALIGNMENT);
727 assert_eq!(low.end(), 2 * ALIGNMENT);
728 assert_eq!(high.start(), 2 * ALIGNMENT);
729 assert_eq!(high.end(), 3 * ALIGNMENT);
730 }
731 _ => panic!("expected split"),
732 }
733 }
734
735 // ========== Helper method tests ==========
736
737 #[test]
738 fn test_split_up_result_helpers() {
739 let range = AllocatedRange::from_range_unchecked(0, 3 * ALIGNMENT);
740
741 // Test with successful split
742 let result = range.split_at_align_up(5000);
743 assert!(result.is_split());
744 assert!(!result.is_out_of_bounds());
745
746 let low = result.low().unwrap();
747 let high = result.high().unwrap();
748 assert_eq!(low.start(), range.start());
749 assert_eq!(high.end(), range.end());
750 assert_eq!(low.end(), high.start());
751 }
752
753 #[test]
754 fn test_split_up_result_low_helpers() {
755 let range = AllocatedRange::from_range_unchecked(0, ALIGNMENT);
756
757 // Test with Low (cannot split, only low range)
758 let result = range.split_at_align_up(100);
759 assert!(!result.is_split());
760 assert!(!result.is_out_of_bounds());
761
762 assert_eq!(result.low(), Some(range));
763 assert_eq!(result.high(), None);
764 }
765
766 #[test]
767 fn test_split_down_result_helpers() {
768 let range = AllocatedRange::from_range_unchecked(0, 3 * ALIGNMENT);
769
770 // Test with successful split
771 let result = range.split_at_align_down(5000);
772 assert!(result.is_split());
773 assert!(!result.is_out_of_bounds());
774
775 let low = result.low().unwrap();
776 let high = result.high().unwrap();
777 assert_eq!(low.start(), range.start());
778 assert_eq!(high.end(), range.end());
779 assert_eq!(low.end(), high.start());
780 }
781
782 #[test]
783 fn test_split_down_result_high_helpers() {
784 let range = AllocatedRange::from_range_unchecked(0, 8192);
785
786 // Test with High (cannot split, only high range)
787 let result = range.split_at_align_down(100);
788 assert!(!result.is_split());
789 assert!(!result.is_out_of_bounds());
790
791 assert_eq!(result.low(), None);
792 assert_eq!(result.high(), Some(range));
793 }
794}