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}