Skip to main content

customizable_buddy/
bitmap.rs

1use crate::{BuddyCollection, BuddyLine, OligarchyCollection};
2use core::fmt;
3
4/// 用一个 usize 作为位图保存占用情况的伙伴行。
5///
6/// - 非侵入式
7/// - 静态分配,容量有限(最多 64 或 128 个块,取决于平台)
8/// - 查找和插入时间复杂度为 O(1)
9pub struct UsizeBuddy {
10    /// 位图,1 表示空闲,0 表示已分配。
11    bits: usize,
12    /// 基序号,用于将本地索引转换为全局索引。
13    base: usize,
14}
15
16impl UsizeBuddy {
17    const SIZE: usize = usize::BITS as usize;
18
19    #[inline]
20    fn take(&mut self, idx: usize) -> bool {
21        let bit = 1usize << idx;
22        let bits = self.bits;
23        self.bits &= !bit;
24        bits & bit == bit
25    }
26}
27
28impl BuddyLine for UsizeBuddy {
29    const EMPTY: Self = Self { bits: 0, base: 0 };
30
31    #[inline]
32    fn init(&mut self, _order: usize, base: usize) {
33        self.base = base;
34    }
35
36    #[inline]
37    fn take(&mut self, idx: usize) -> bool {
38        self.take(idx - self.base)
39    }
40}
41
42impl OligarchyCollection for UsizeBuddy {
43    #[inline]
44    fn take_any(&mut self, align_order: usize, count: usize) -> Option<usize> {
45        if count == 0 {
46            return None;
47        }
48        if count == 1 {
49            // 单个块,直接使用 BuddyCollection 的逻辑
50            return BuddyCollection::take_any(self, align_order);
51        }
52
53        // 需要找到连续的 count 个位
54        // mask 是 count 个连续的 1
55        let mask = (1usize << count) - 1;
56        let align = 1usize << align_order;
57        let mut i = 0;
58        while i + count <= usize::BITS as usize {
59            let bits_mask = mask << i;
60            if self.bits & bits_mask == bits_mask {
61                self.bits &= !bits_mask;
62                return Some(self.base + i);
63            }
64            i += align;
65        }
66        None
67    }
68
69    #[inline]
70    fn put(&mut self, idx: usize) {
71        self.bits |= 1 << (idx - self.base);
72    }
73}
74
75impl BuddyCollection for UsizeBuddy {
76    #[inline]
77    fn take_any(&mut self, align_order: usize) -> Option<usize> {
78        // 将位图对齐到指定对齐阶数
79        // align_order=1 要求索引是 2 的倍数(0, 2, 4...)
80        // 需要清除 bit 0, 1, ..., align_order-1 中不满足对齐的位
81        // 正确做法:保留每 2^align_order 个位中的第一个
82        if align_order == 0 {
83            // 不对齐,直接找第一个空闲位
84            if self.bits != 0 {
85                let i = self.bits.trailing_zeros() as usize;
86                self.bits &= !(1 << i);
87                return Some(self.base + i);
88            }
89        } else {
90            // 对齐:只保留 bit 0, 2^align_order, 2*2^align_order, ...
91            let align = 1usize << align_order;
92            // 创建掩码,只保留对齐的位
93            let mut mask = 0usize;
94            for i in (0..usize::BITS as usize).step_by(align) {
95                mask |= 1 << i;
96            }
97            let aligned_bits = self.bits & mask;
98            if aligned_bits != 0 {
99                let i = aligned_bits.trailing_zeros() as usize;
100                self.bits &= !(1 << i);
101                return Some(self.base + i);
102            }
103        }
104        None
105    }
106
107    #[inline]
108    fn put(&mut self, idx: usize) -> Option<usize> {
109        let idx = idx - self.base;
110        debug_assert!(idx < Self::SIZE, "index out of bound");
111        let buddy = idx ^ 1;
112        if self.take(buddy) {
113            Some(idx << 1)
114        } else {
115            self.bits |= 1 << idx;
116            None
117        }
118    }
119}
120
121impl fmt::Debug for UsizeBuddy {
122    #[inline]
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        write!(f, "{:b}", self.bits)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_buddy_line_init() {
134        let mut buddy = UsizeBuddy::EMPTY;
135        buddy.init(3, 10);
136        assert_eq!(buddy.base, 10);
137    }
138
139    #[test]
140    fn test_take_any_basic() {
141        let mut buddy = UsizeBuddy {
142            bits: 0b1010, // 位 1 和 3 是空闲的
143            base: 0,
144        };
145
146        // 应该返回最小的空闲位(1)
147        assert_eq!(BuddyCollection::take_any(&mut buddy, 0), Some(1));
148        // 现在位 3 是空闲的
149        assert_eq!(BuddyCollection::take_any(&mut buddy, 0), Some(3));
150        // 没有更多空闲位
151        assert_eq!(BuddyCollection::take_any(&mut buddy, 0), None);
152    }
153
154    #[test]
155    fn test_take_any_with_align() {
156        let mut buddy = UsizeBuddy {
157            bits: 0b1111, // 位 0,1,2,3 都是空闲的
158            base: 0,
159        };
160
161        // align_order=1 要求索引对齐到 2(即索引必须是 0,2,4...)
162        // 应该返回 0
163        assert_eq!(BuddyCollection::take_any(&mut buddy, 1), Some(0));
164        // 现在位 1,2,3 是空闲的,对齐到 2 的最小是 2
165        assert_eq!(BuddyCollection::take_any(&mut buddy, 1), Some(2));
166        // 没有更多对齐到 2 的空闲位
167        assert_eq!(BuddyCollection::take_any(&mut buddy, 1), None);
168    }
169
170    #[test]
171    fn test_put_basic() {
172        let mut buddy = UsizeBuddy {
173            bits: 0b0000,
174            base: 0,
175        };
176
177        // 放入索引 0
178        assert_eq!(BuddyCollection::put(&mut buddy, 0), None);
179        assert_eq!(buddy.bits, 0b0001);
180
181        // 放入索引 2
182        assert_eq!(BuddyCollection::put(&mut buddy, 2), None);
183        assert_eq!(buddy.bits, 0b0101);
184    }
185
186    #[test]
187    fn test_put_merge_buddy() {
188        let mut buddy = UsizeBuddy {
189            bits: 0b0001, // 本地索引 0 是空闲的
190            base: 0,
191        };
192
193        // 放入全局索引 1(本地索引 1),伙伴本地索引 0 存在,触发合并
194        // 返回父节点的本地索引(idx << 1 格式,调用者需要 idx >> 1 得到实际父节点)
195        assert_eq!(BuddyCollection::put(&mut buddy, 1), Some(2));
196        assert_eq!(buddy.bits, 0b0000);
197    }
198
199    #[test]
200    fn test_put_merge_buddy_base_offset() {
201        // 测试 base 不为 0 的情况
202        let mut buddy = UsizeBuddy {
203            bits: 0b0000, // 初始为空
204            base: 10,
205        };
206
207        // 放入全局索引 10(本地索引 0),伙伴不存在
208        assert_eq!(BuddyCollection::put(&mut buddy, 10), None);
209        assert_eq!(buddy.bits, 0b0001);
210
211        // 放入全局索引 11(本地索引 1),伙伴存在,触发合并
212        assert_eq!(BuddyCollection::put(&mut buddy, 11), Some(2));
213        assert_eq!(buddy.bits, 0b0000);
214    }
215
216    #[test]
217    fn test_take_by_index() {
218        let mut buddy = UsizeBuddy {
219            bits: 0b1010,
220            base: 0,
221        };
222
223        // 提取索引 3
224        assert!(buddy.take(3));
225        assert_eq!(buddy.bits, 0b0010);
226
227        // 再次提取索引 3(应该失败)
228        assert!(!buddy.take(3));
229
230        // 提取索引 1
231        assert!(buddy.take(1));
232        assert_eq!(buddy.bits, 0b0000);
233    }
234
235    #[test]
236    fn test_oligarchy_take_any_single() {
237        let mut buddy = UsizeBuddy {
238            bits: 0b1010,
239            base: 0,
240        };
241
242        // count=1 应该使用 BuddyCollection 的逻辑
243        assert_eq!(OligarchyCollection::take_any(&mut buddy, 0, 1), Some(1));
244        assert_eq!(OligarchyCollection::take_any(&mut buddy, 0, 1), Some(3));
245        assert_eq!(OligarchyCollection::take_any(&mut buddy, 0, 1), None);
246    }
247
248    #[test]
249    fn test_oligarchy_take_any_multiple() {
250        let mut buddy = UsizeBuddy {
251            bits: 0b0111, // 位 0,1,2 是空闲的
252            base: 0,
253        };
254
255        // 取 2 个连续的位
256        assert_eq!(OligarchyCollection::take_any(&mut buddy, 0, 2), Some(0));
257        // 位 0,1 被取走,剩下位 2
258        assert_eq!(buddy.bits, 0b0100);
259
260        // 再尝试取 2 个连续的位(失败)
261        assert_eq!(OligarchyCollection::take_any(&mut buddy, 0, 2), None);
262    }
263
264    #[test]
265    fn test_oligarchy_take_any_count_zero() {
266        let mut buddy = UsizeBuddy {
267            bits: 0b1111,
268            base: 0,
269        };
270
271        // count=0 应该返回 None
272        assert_eq!(OligarchyCollection::take_any(&mut buddy, 0, 0), None);
273        assert_eq!(buddy.bits, 0b1111); // 位图不变
274    }
275
276    #[test]
277    fn test_oligarchy_take_any_with_align() {
278        let mut buddy = UsizeBuddy {
279            bits: 0b111111, // 位 0-5 是空闲的
280            base: 0,
281        };
282
283        // 取 2 个连续的位,align_order=1(对齐到 2)
284        // 可能的起始位置是 0,2,4
285        // 0-1 都空闲,所以可以取
286        assert_eq!(OligarchyCollection::take_any(&mut buddy, 1, 2), Some(0));
287        // 位 0,1 被取走
288        assert_eq!(buddy.bits, 0b111100);
289
290        // 再取 2 个连续的位,对齐到 2
291        // 可能的起始位置是 2,4
292        // 都可以取
293        assert_eq!(OligarchyCollection::take_any(&mut buddy, 1, 2), Some(2));
294        assert_eq!(buddy.bits, 0b110000);
295    }
296
297    #[test]
298    fn test_empty() {
299        let buddy = UsizeBuddy::EMPTY;
300        assert_eq!(buddy.bits, 0);
301        assert_eq!(buddy.base, 0);
302    }
303}