Skip to main content

oxide_core/heap/
values.rs

1//! The value heap: one `Vec<Value>` allocated in ranges of size classes.
2//!
3//! Every property value, dense element and scope slot lives in a range. The
4//! classes are the powers of two and three times the powers of two (1, 2, 3,
5//! 4, 6, 8, 12, 16, 24, …), and each keeps a free list. A range's length is
6//! always its class size, which lets an object header store it as one `u8`
7//! code: an exponent and one mantissa bit.
8//! A collection's sweep records the ranges it frees in a bitmap of free slots
9//! and rebuilds the free space from its maximal runs: short runs go on the
10//! class lists, long ones become free runs that requests take from in address
11//! order, a long free run at the end is cut off the heap and its capacity
12//! given back. A request whose class list is empty takes from the current
13//! free run, then splits the smallest larger free range. The value heap does not move
14//! ranges; [`ValueHeap::fragmentation`] reports what churn leaves behind.
15
16use super::arena::{grown_capacity, step_bytes};
17use super::gc::Room;
18use crate::value::Value;
19
20/// Class `i` holds [`class_size`]`(i)` slots; index 1 (a second class of one
21/// slot) is unused, so the sizes increase with the index.
22const CLASSES: usize = 64;
23/// The largest class, 2^31 slots.
24const LARGEST: u32 = 62;
25/// Free runs shorter than this go on the class lists after a collection.
26const SHORT_RUN: usize = 64;
27
28/// The slots of class `class`: `2^(class / 2)` for an even class, `3 ×
29/// 2^(class / 2 - 1)` for an odd one.
30#[inline]
31pub const fn class_size(class: u32) -> u32 {
32    ((((2 | (class & 1)) as u64) << (class >> 1)) >> 1) as u32
33}
34
35/// The class of a class size.
36#[inline]
37pub const fn class_of(len: u32) -> u32 {
38    let k = len.trailing_zeros();
39    if len.is_power_of_two() {
40        2 * k
41    } else {
42        2 * k + 3
43    }
44}
45
46/// The size and the class of the smallest class of at least `len` slots
47/// (`len` at least one).
48#[inline]
49fn class_for(len: u32) -> (u32, u32) {
50    let k = u32::BITS - (len - 1).leading_zeros();
51    if k >= 2 && len <= 3 << (k - 2) {
52        (3 << (k - 2), 2 * k - 1)
53    } else {
54        (1 << k, 2 * k)
55    }
56}
57
58/// The largest class of at most `slots` slots (at least one).
59#[inline]
60fn class_at_most(slots: usize) -> u32 {
61    let k = usize::BITS - 1 - slots.leading_zeros();
62    let class = if k >= 1 && slots >= 3 << (k - 1) {
63        2 * k + 1
64    } else {
65        2 * k
66    };
67    class.min(LARGEST)
68}
69
70/// A block of the value heap. `len` is zero or a class size.
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub struct Range {
73    pub start: u32,
74    pub len: u32,
75}
76
77impl Range {
78    pub const EMPTY: Range = Range { start: 0, len: 0 };
79
80    /// The `u8` code stored in object headers: the class's exponent, with
81    /// bit 7 set for three times a power of two (`len = ((2 | m) << e) / 2`);
82    /// `u8::MAX` for an empty range.
83    #[inline]
84    pub const fn log(self) -> u8 {
85        if self.len == 0 {
86            u8::MAX
87        } else {
88            let class = class_of(self.len);
89            ((class >> 1) | ((class & 1) << 7)) as u8
90        }
91    }
92
93    /// [`Range::log`] of a power-of-two range.
94    #[inline]
95    pub const fn exponent(self) -> u8 {
96        if self.len == 0 {
97            u8::MAX
98        } else {
99            self.len.trailing_zeros() as u8
100        }
101    }
102
103    /// [`Range::from_log`] for a code without the mantissa bit (a
104    /// power-of-two range).
105    #[inline]
106    pub const fn from_exponent(start: u32, log: u8) -> Range {
107        if log == u8::MAX {
108            Range::EMPTY
109        } else {
110            Range {
111                start,
112                len: 1 << log,
113            }
114        }
115    }
116
117    #[inline]
118    pub const fn from_log(start: u32, log: u8) -> Range {
119        if log == u8::MAX {
120            Range::EMPTY
121        } else {
122            let len = ((((2 | (log >> 7)) as u64) << (log & 0x7f)) >> 1) as u32;
123            Range { start, len }
124        }
125    }
126}
127
128/// Rounds a requested length to its class size.
129#[inline]
130pub fn class_len(len: u32) -> u32 {
131    if len == 0 { 0 } else { class_for(len).0 }
132}
133
134/// The class above `len`'s.
135pub fn next_class_len(len: u32) -> u32 {
136    let class = class_for(len.max(1)).1;
137    class_size(if class == 0 { 2 } else { class + 1 })
138}
139
140pub struct ValueHeap {
141    values: Vec<Value>,
142    free: [Vec<u32>; CLASSES],
143    /// Bit `k` is set while list `k` may hold a range: set by every push,
144    /// cleared when a split finds the list empty.
145    listed: u64,
146    /// The long free runs `[start, end)` the last collection left, highest
147    /// first, each with the longest length among it and the runs above it.
148    runs: Vec<(u32, u32, u32)>,
149    /// The free run requests take from now.
150    current: (u32, u32),
151    free_slots: usize,
152    /// One bit per slot, set for a free one, during a sweep.
153    bits: Vec<u64>,
154}
155
156impl Default for ValueHeap {
157    fn default() -> Self {
158        Self::new()
159    }
160}
161
162impl ValueHeap {
163    pub fn new() -> Self {
164        ValueHeap {
165            values: Vec::new(),
166            free: std::array::from_fn(|_| Vec::new()),
167            listed: 0,
168            runs: Vec::new(),
169            current: (0, 0),
170            free_slots: 0,
171            bits: Vec::new(),
172        }
173    }
174
175    /// Allocates a range of at least `len` slots, filled with `Value::EMPTY`.
176    /// The returned length is rounded up to the class size. A growth step is
177    /// charged to `room`.
178    #[inline]
179    pub fn alloc(&mut self, len: u32, room: &mut Room) -> Range {
180        if len == 0 {
181            return Range::EMPTY;
182        }
183        let (len, class) = class_for(len);
184        self.alloc_class(len, class, room)
185    }
186
187    /// [`ValueHeap::alloc`] rounded to a power of two: an object's named
188    /// property slots, which the header decodes with one shift.
189    #[inline]
190    pub fn alloc_slots(&mut self, len: u32, room: &mut Room) -> Range {
191        if len == 0 {
192            return Range::EMPTY;
193        }
194        let len = len.next_power_of_two();
195        self.alloc_class(len, 2 * len.trailing_zeros(), room)
196    }
197
198    #[inline]
199    fn alloc_class(&mut self, len: u32, class: u32, room: &mut Room) -> Range {
200        if let Some(start) = self.free[class as usize].pop() {
201            self.free_slots -= len as usize;
202            return Range { start, len };
203        }
204        let (start, end) = self.current;
205        if end - start >= len {
206            self.current.0 += len;
207            self.free_slots -= len as usize;
208            return Range { start, len };
209        }
210        self.alloc_slow(len, class, room)
211    }
212
213    /// A range of class `class` (`len` slots) from the current free run,
214    /// split from the smallest larger free range, or fresh past the end.
215    #[inline(never)]
216    fn alloc_slow(&mut self, len: u32, class: u32, room: &mut Room) -> Range {
217        loop {
218            let (start, end) = self.current;
219            if end - start >= len {
220                self.current.0 += len;
221                self.free_slots -= len as usize;
222                return Range { start, len };
223            }
224            // No run left is long enough: the runs stay for later requests.
225            if !self
226                .runs
227                .last()
228                .is_some_and(|&(_, _, longest)| longest >= len)
229            {
230                break;
231            }
232            // The rest of the run is too short: it goes on the class lists,
233            // and the next run is taken.
234            self.push_run(start as usize, end as usize);
235            match self.runs.pop() {
236                Some((start, end, _)) => self.current = (start, end),
237                None => unreachable!("a run long enough was left"),
238            }
239        }
240        let mut larger = self.listed & !((2u64 << class) - 1);
241        while larger != 0 {
242            let source = larger.trailing_zeros();
243            larger &= larger - 1;
244            let Some(start) = self.free[source as usize].pop() else {
245                self.listed &= !(1 << source);
246                continue;
247            };
248            // `[start, start + len)` is returned and the rest of the block
249            // goes back as the largest ranges that fit.
250            self.push_run(
251                (start + len) as usize,
252                (start + class_size(source)) as usize,
253            );
254            self.free_slots -= len as usize;
255            return Range { start, len };
256        }
257        self.alloc_fresh(len, room)
258    }
259
260    #[inline]
261    fn push(&mut self, start: u32, class: u32) {
262        self.free[class as usize].push(start);
263        self.listed |= 1 << class;
264    }
265
266    /// Lists the free slots `[start, stop)` as the largest ranges that fit,
267    /// from `start`.
268    fn push_run(&mut self, mut start: usize, stop: usize) {
269        while start < stop {
270            let class = class_at_most(stop - start);
271            self.push(start as u32, class);
272            start += class_size(class) as usize;
273        }
274    }
275
276    fn alloc_fresh(&mut self, len: u32, room: &mut Room) -> Range {
277        let start = u32::try_from(self.values.len()).expect("value heap overflow");
278        let end = self.values.len() + len as usize;
279        assert!(end <= u32::MAX as usize, "value heap overflow");
280        if end > self.values.capacity() {
281            let before = self.values.capacity();
282            let target = grown_capacity(before, end, 8);
283            self.values.reserve_exact(target - self.values.len());
284            room.take((self.values.capacity() - before) * 8);
285        }
286        self.values.resize(end, Value::EMPTY);
287        Range { start, len }
288    }
289
290    /// Bytes the backing vector grows by if a range of `len` is allocated now.
291    pub fn growth_bytes(&self, len: u32) -> usize {
292        // A power of two covers both a slot request (`alloc_slots`) and the
293        // class of an element request, so a free range listed at its class
294        // or above serves either.
295        let len = len.next_power_of_two() * u32::from(len != 0);
296        let (start, end) = self.current;
297        if len == 0
298            || end - start >= len
299            || self
300                .runs
301                .last()
302                .is_some_and(|&(_, _, longest)| longest >= len)
303            || self.free[class_of(len) as usize..]
304                .iter()
305                .any(|list| !list.is_empty())
306        {
307            return 0;
308        }
309        let end = self.values.len() + len as usize;
310        let capacity = self.values.capacity();
311        (grown_capacity(capacity, end, 8) - capacity) * 8
312    }
313
314    /// Returns a range of at least `new_len` holding `range`'s values: the same
315    /// range when it is already large enough, otherwise a new one (alloc, copy,
316    /// free).
317    pub fn grow(&mut self, range: Range, new_len: u32, room: &mut Room) -> Range {
318        if new_len <= range.len {
319            return range;
320        }
321        let new = self.alloc(new_len, room);
322        self.moved(range, new)
323    }
324
325    /// [`ValueHeap::grow`] for named property slots, to a power of two.
326    pub fn grow_slots(&mut self, range: Range, new_len: u32, room: &mut Room) -> Range {
327        if new_len <= range.len {
328            return range;
329        }
330        let new = self.alloc_slots(new_len, room);
331        self.moved(range, new)
332    }
333
334    /// Copies `range`'s values into `new` and frees `range`.
335    fn moved(&mut self, range: Range, new: Range) -> Range {
336        let (from, to) = (range.start as usize, new.start as usize);
337        self.values.copy_within(from..from + range.len as usize, to);
338        self.free(range);
339        new
340    }
341
342    /// Returns a range to its class. The slots are poisoned with `EMPTY`.
343    pub fn free(&mut self, range: Range) {
344        if range.len == 0 {
345            return;
346        }
347        debug_assert_eq!(class_len(range.len), range.len);
348        let start = range.start as usize;
349        self.values[start..start + range.len as usize].fill(Value::EMPTY);
350        self.push(range.start, class_of(range.len));
351        self.free_slots += range.len as usize;
352    }
353
354    /// Starts a collection's sweep: the free space moves into the bitmap of
355    /// free slots, where [`ValueHeap::release`] records the ranges the sweep
356    /// frees, and [`ValueHeap::finish_sweep`] rebuilds it from the bitmap.
357    pub fn begin_sweep(&mut self) {
358        let mut bits = std::mem::take(&mut self.bits);
359        bits.clear();
360        bits.resize(self.values.len().div_ceil(64), 0);
361        for (class, list) in self.free.iter_mut().enumerate() {
362            for start in list.drain(..) {
363                let end = start as usize + class_size(class as u32) as usize;
364                set_bits(&mut bits, start as usize, end);
365            }
366        }
367        let (start, end) = std::mem::take(&mut self.current);
368        for (start, end, _) in self.runs.drain(..).chain([(start, end, 0)]) {
369            if start < end {
370                set_bits(&mut bits, start as usize, end as usize);
371            }
372        }
373        self.bits = bits;
374        self.listed = 0;
375        self.free_slots = 0;
376    }
377
378    /// Frees a range during the sweep: poisons it with `EMPTY` and records it
379    /// in the bitmap of free slots.
380    pub fn release(&mut self, range: Range) {
381        if range.len == 0 {
382            return;
383        }
384        let (start, end) = (range.start as usize, (range.start + range.len) as usize);
385        self.values[start..end].fill(Value::EMPTY);
386        set_bits(&mut self.bits, start, end);
387    }
388
389    /// Ends the sweep: rebuilds the free space from the maximal runs of free
390    /// slots. A run shorter than [`SHORT_RUN`] goes on the class lists, cut
391    /// from its start into the largest class ranges that fit (a run of seven
392    /// becomes ranges of six and one); a longer one becomes a free
393    /// run. A run that ends the heap and spans more than a quarter of its
394    /// capacity is cut off instead, and the capacity past one growth step
395    /// goes back to the allocator. Lower ranges and runs are reused first.
396    pub fn finish_sweep(&mut self) {
397        let len = self.values.len();
398        let bits = std::mem::take(&mut self.bits);
399        let mut at = 0;
400        while let Some(start) = next_bit(&bits, at, true, len) {
401            let stop = next_bit(&bits, start, false, len).unwrap_or(len);
402            let capacity = self.values.capacity();
403            if stop == len && (capacity - start) * 4 > capacity {
404                self.values.truncate(start);
405                self.values.shrink_to(start + step_bytes(start * 8, 8) / 8);
406                break;
407            }
408            self.free_slots += stop - start;
409            if stop - start < SHORT_RUN {
410                self.push_run(start, stop);
411            } else {
412                self.runs.push((start as u32, stop as u32, 0));
413            }
414            at = stop;
415        }
416        self.bits = bits;
417        self.runs.reverse();
418        let mut longest = 0;
419        for run in &mut self.runs {
420            longest = longest.max(run.1 - run.0);
421            run.2 = longest;
422        }
423        for list in &mut self.free {
424            list.reverse();
425            if list.capacity() > 2 * list.len() + 16 {
426                list.shrink_to(2 * list.len());
427            }
428        }
429    }
430
431    /// Coalesces the free lists outside a collection.
432    #[cfg(test)]
433    fn coalesce(&mut self) {
434        self.begin_sweep();
435        self.finish_sweep();
436    }
437
438    #[inline]
439    #[track_caller]
440    pub fn get(&self, range: Range, slot: u32) -> Value {
441        debug_assert!(
442            slot < range.len,
443            "slot {slot} outside range of {}",
444            range.len
445        );
446        self.values[(range.start + slot) as usize]
447    }
448
449    #[inline]
450    #[track_caller]
451    pub fn set(&mut self, range: Range, slot: u32, value: Value) {
452        debug_assert!(
453            slot < range.len,
454            "slot {slot} outside range of {}",
455            range.len
456        );
457        self.values[(range.start + slot) as usize] = value;
458    }
459
460    /// The value at `slot` of `range`, or `Value::EMPTY` past its end.
461    #[inline]
462    pub fn get_or_empty(&self, range: Range, slot: u32) -> Value {
463        if slot < range.len {
464            self.get(range, slot)
465        } else {
466            Value::EMPTY
467        }
468    }
469
470    /// An absolute slot; callers check the range in debug builds.
471    #[inline]
472    pub fn get_at(&self, index: u32) -> Value {
473        self.values[index as usize]
474    }
475
476    #[inline]
477    pub fn set_at(&mut self, index: u32, value: Value) {
478        self.values[index as usize] = value;
479    }
480
481    #[inline]
482    pub fn slice(&self, range: Range) -> &[Value] {
483        let start = range.start as usize;
484        &self.values[start..start + range.len as usize]
485    }
486
487    #[inline]
488    pub fn slice_mut(&mut self, range: Range) -> &mut [Value] {
489        let start = range.start as usize;
490        &mut self.values[start..start + range.len as usize]
491    }
492
493    /// Slots handed out, free or in use.
494    pub fn total_slots(&self) -> usize {
495        self.values.len()
496    }
497
498    /// Slots sitting in free lists.
499    pub fn free_slots(&self) -> usize {
500        self.free_slots
501    }
502
503    /// Slots held by live ranges.
504    pub fn used_slots(&self) -> usize {
505        self.values.len() - self.free_slots
506    }
507
508    /// Free-list slots over all handed-out slots.
509    pub fn fragmentation(&self) -> f64 {
510        if self.values.is_empty() {
511            0.0
512        } else {
513            self.free_slots as f64 / self.values.len() as f64
514        }
515    }
516
517    pub fn bytes(&self) -> usize {
518        self.values.capacity() * 8
519            + self
520                .free
521                .iter()
522                .map(|list| list.capacity() * 4)
523                .sum::<usize>()
524            + self.runs.capacity() * 12
525            + self.bits.capacity() * 8
526    }
527
528    /// Every free range of the class lists.
529    pub fn free_blocks(&self) -> impl Iterator<Item = Range> + '_ {
530        self.free.iter().enumerate().flat_map(|(class, starts)| {
531            starts.iter().map(move |&start| Range {
532                start,
533                len: class_size(class as u32),
534            })
535        })
536    }
537
538    /// The free runs, the current one first, for the stress-mode verifier.
539    pub fn free_runs(&self) -> impl Iterator<Item = (u32, u32)> + '_ {
540        let (start, end) = self.current;
541        (start < end)
542            .then_some((start, end))
543            .into_iter()
544            .chain(self.runs.iter().rev().map(|&(start, end, _)| (start, end)))
545    }
546}
547
548/// Sets bits `[from, to)`.
549fn set_bits(bits: &mut [u64], from: usize, to: usize) {
550    let (first, last) = (from / 64, (to - 1) / 64);
551    let low = !0u64 << (from % 64);
552    let high = !0u64 >> (63 - (to - 1) % 64);
553    if first == last {
554        bits[first] |= low & high;
555        return;
556    }
557    bits[first] |= low;
558    bits[first + 1..last].fill(!0);
559    bits[last] |= high;
560}
561
562/// The first bit at or after `from`, below `len`, that is set (`set`) or
563/// clear.
564fn next_bit(bits: &[u64], from: usize, set: bool, len: usize) -> Option<usize> {
565    if from >= len {
566        return None;
567    }
568    let flip = if set { 0 } else { !0 };
569    let mut word = from / 64;
570    let mut current = (bits[word] ^ flip) & (!0u64 << (from % 64));
571    loop {
572        if current != 0 {
573            let index = word * 64 + current.trailing_zeros() as usize;
574            return (index < len).then_some(index);
575        }
576        word += 1;
577        if word == bits.len() {
578            return None;
579        }
580        current = bits[word] ^ flip;
581    }
582}
583
584#[cfg(test)]
585mod tests {
586    use super::*;
587
588    #[test]
589    fn classes_round_up_and_reuse() {
590        let room = &mut Room::unbounded();
591        let mut heap = ValueHeap::new();
592        let a = heap.alloc(5, room);
593        assert_eq!(a, Range { start: 0, len: 6 });
594        assert!(heap.slice(a).iter().all(|v| v.is_empty()));
595        heap.set(a, 2, Value::int32(5));
596        let b = heap.alloc(1, room);
597        assert_eq!(b, Range { start: 6, len: 1 });
598        heap.free(a);
599        assert_eq!(heap.free_slots(), 6);
600        let c = heap.alloc(6, room);
601        assert_eq!(c.start, 0);
602        assert!(heap.get(c, 2).is_empty(), "freed slots are poisoned");
603        assert_eq!(heap.alloc(0, room), Range::EMPTY);
604    }
605
606    #[test]
607    fn grow_copies_and_frees() {
608        let room = &mut Room::unbounded();
609        let mut heap = ValueHeap::new();
610        let a = heap.alloc(2, room);
611        heap.set(a, 0, Value::int32(1));
612        heap.set(a, 1, Value::int32(2));
613        assert_eq!(heap.grow(a, 2, room), a);
614        let b = heap.grow(a, 3, room);
615        assert_eq!(b.len, 3);
616        assert_eq!(heap.get(b, 0), Value::int32(1));
617        assert_eq!(heap.get(b, 1), Value::int32(2));
618        assert!(heap.get(b, 2).is_empty());
619        assert_eq!(heap.free_blocks().collect::<Vec<_>>(), [a]);
620        let large = heap.alloc(1000, room);
621        assert_eq!(large.len, 1024);
622        heap.free(large);
623        assert_eq!(heap.alloc(513, room).start, large.start);
624    }
625
626    #[test]
627    fn a_request_splits_a_larger_range() {
628        let room = &mut Room::unbounded();
629        let mut heap = ValueHeap::new();
630        let big = heap.alloc(16, room);
631        let after = heap.alloc(1, room);
632        heap.free(big);
633        assert_eq!(heap.alloc(2, room), Range { start: 0, len: 2 });
634        // The rest of the block: ranges of 8, 4 and 2.
635        assert_eq!(heap.alloc(2, room), Range { start: 14, len: 2 });
636        assert_eq!(heap.alloc(8, room), Range { start: 2, len: 8 });
637        assert_eq!(heap.alloc(4, room), Range { start: 10, len: 4 });
638        assert_eq!(heap.free_slots(), 0);
639        assert_eq!(heap.alloc(1, room).start, after.start + 1);
640    }
641
642    #[test]
643    fn requests_take_from_free_runs_in_address_order() {
644        let room = &mut Room::unbounded();
645        let mut heap = ValueHeap::new();
646        heap.alloc(1, room);
647        let big = heap.alloc(1024, room);
648        let tail = heap.alloc(1, room);
649        heap.free(big);
650        heap.coalesce();
651        assert_eq!(heap.free_runs().collect::<Vec<_>>(), [(1, 1025)]);
652        assert_eq!(heap.alloc(4, room).start, 1);
653        assert_eq!(heap.alloc(2, room).start, 5);
654        assert_eq!(heap.alloc(512, room).start, 7);
655        // 506 slots are left: too few for 512, which no run fits, so the run
656        // stays for the requests it does fit.
657        assert_eq!(heap.alloc(512, room).start, tail.start + 1);
658        assert_eq!(heap.free_runs().collect::<Vec<_>>(), [(519, 1025)]);
659        assert_eq!(heap.free_slots(), 506);
660        assert_eq!(heap.alloc(256, room).start, 519);
661    }
662
663    #[test]
664    fn coalescing_rebuilds_runs_and_cuts_the_end() {
665        let room = &mut Room::unbounded();
666        let mut heap = ValueHeap::new();
667        let ranges: Vec<Range> = (0..8).map(|_| heap.alloc(2, room)).collect();
668        // Free slots 2..8 (a run of six) and 10..16 (the end).
669        for &index in &[1, 2, 3, 5, 6, 7] {
670            heap.free(ranges[index]);
671        }
672        heap.coalesce();
673        assert_eq!(heap.total_slots(), 10);
674        assert_eq!(heap.free_slots(), 6);
675        assert_eq!(
676            heap.free_blocks().collect::<Vec<_>>(),
677            [Range { start: 2, len: 6 }]
678        );
679        assert_eq!(heap.alloc(4, room).start, 2);
680        assert_eq!(heap.alloc(2, room).start, 6);
681        assert_eq!(heap.alloc(2, room).start, 10);
682    }
683
684    #[test]
685    fn a_short_free_end_stays_listed() {
686        let room = &mut Room::unbounded();
687        let mut heap = ValueHeap::new();
688        let ranges: Vec<Range> = (0..8).map(|_| heap.alloc(2, room)).collect();
689        heap.free(ranges[7]);
690        heap.coalesce();
691        assert_eq!(heap.total_slots(), 16);
692        assert_eq!(heap.free_blocks().collect::<Vec<_>>(), [ranges[7]]);
693    }
694
695    #[test]
696    fn a_long_free_end_gives_back_capacity() {
697        let room = &mut Room::unbounded();
698        let mut heap = ValueHeap::new();
699        let keep = heap.alloc(64, room);
700        let large = heap.alloc(1 << 20, room);
701        heap.free(large);
702        let before = heap.bytes();
703        heap.coalesce();
704        assert_eq!(heap.total_slots(), 64);
705        assert!(heap.bytes() < before / 4);
706        assert_eq!(heap.alloc(64, room).start, keep.start + 64);
707    }
708
709    #[test]
710    fn bit_runs() {
711        let mut bits = vec![0u64; 3];
712        set_bits(&mut bits, 3, 130);
713        assert_eq!(next_bit(&bits, 0, true, 192), Some(3));
714        assert_eq!(next_bit(&bits, 3, false, 192), Some(130));
715        assert_eq!(next_bit(&bits, 130, true, 192), None);
716        set_bits(&mut bits, 150, 151);
717        assert_eq!(next_bit(&bits, 130, true, 192), Some(150));
718        assert_eq!(next_bit(&bits, 150, false, 140), None);
719    }
720
721    #[test]
722    fn classes_are_powers_of_two_and_three_times_them() {
723        let sizes: Vec<u32> = (0..12)
724            .filter(|&class| class != 1)
725            .map(class_size)
726            .collect();
727        assert_eq!(sizes, [1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48]);
728        assert_eq!(class_size(LARGEST), 1 << 31);
729        for len in 1..5000 {
730            let (size, class) = class_for(len);
731            assert_eq!(class_size(class), size);
732            assert_eq!(class_of(size), class);
733            assert!(size >= len);
734            assert!(class <= 2 || class_size(class - 1 - u32::from(class == 2)) < len);
735        }
736        assert_eq!(class_len(10), 12);
737        assert_eq!(next_class_len(1024), 1536);
738        assert_eq!(next_class_len(1536), 2048);
739        assert_eq!(next_class_len(1), 2);
740        assert_eq!(class_at_most(7), class_of(6));
741        assert_eq!(class_at_most(5), class_of(4));
742    }
743
744    #[test]
745    fn range_log_round_trips() {
746        for len in [0, 1, 2, 3, 6, 12, 256, 384, 1 << 20, 3 << 29, 1 << 31] {
747            let range = Range { start: 9, len };
748            let decoded = Range::from_log(9, range.log());
749            assert_eq!(decoded.len, len);
750            assert!(len == 0 || decoded == range);
751        }
752    }
753}