|
11 | 11 | /// 0bXXXXXXX100000000...0 |
12 | 12 | /// ``` |
13 | 13 | /// |
14 | | -/// Node reach after traversal of `LRLRRLLR` branches should be represented as `0b0101100110000...0`. |
15 | | -/// Root is obviously encoded as `0b10000...0`. |
| 14 | +/// The reached node after traversal of `LRLRRLLR` branches (`L` for left, `R` for right) should be |
| 15 | +/// represented as `0b0101100110000...0`. |
| 16 | +/// Root is encoded as `0b10000...0` from an empty bitstring we don't need to traverse any branch |
| 17 | +/// to reach a binary tree's root. |
| 18 | +/// |
| 19 | +/// Here are some examples: |
| 20 | +/// |
| 21 | +/// ```text |
| 22 | +/// (root) -> 0b10000000...0 |
| 23 | +/// L (left) -> 0b01000000...0 |
| 24 | +/// R (right) -> 0b11000000...0 |
| 25 | +/// LL -> 0b00100000...0 |
| 26 | +/// RLR -> 0b10110000...0 |
| 27 | +/// LRL -> 0b01010000...0 |
| 28 | +/// LRRLR -> 0b01101100...0 |
| 29 | +/// ``` |
| 30 | +/// |
| 31 | +/// ## Multi-way tree |
| 32 | +/// |
| 33 | +/// But we don't necessary need to encode a binary tree directly. |
| 34 | +/// We can imagine some node to have `N` number of branches instead of two: right and left. |
| 35 | +/// We encode `0 <= i < N` numbered branches by interpreting `i`'s binary representation as |
| 36 | +/// bitstring for a binary tree traversal. |
| 37 | +/// |
| 38 | +/// For example `N = 3`. Notice how right-most leaf node is unused: |
| 39 | +/// |
| 40 | +/// ```text |
| 41 | +/// root |
| 42 | +/// root / \ |
| 43 | +/// / | \ => . . |
| 44 | +/// 0 1 2 / \ / \ |
| 45 | +/// 0 1 2 - |
| 46 | +/// ``` |
16 | 47 | /// |
17 | 48 | /// ## Order |
18 | 49 | /// |
19 | | -/// Encoding allows to sort nodes in left < parent < right linear order. |
20 | | -/// If you only consider leaves of a tree then those are sorted in order left < right. |
| 50 | +/// Encoding allows to sort nodes in `left < parent < right` linear order. |
| 51 | +/// If you only consider leaves of a tree then those are sorted in order `left < right`. |
21 | 52 | /// |
22 | 53 | /// ## Used in |
23 | 54 | /// |
24 | | -/// Primary purpose of `TreeNodeIndex` is to track order of parallel tasks of functions like `join` |
25 | | -/// or `scope` (see `rustc_middle::sync`). |
26 | | -/// This is done in query cycle handling code to determine **intended** first task for a single- |
27 | | -/// threaded compiler front-end to execute even while multi-threaded. |
| 55 | +/// Primary purpose of `TreeNodeIndex` is to track order of parallel tasks of functions like |
| 56 | +/// `par_join`, `par_slice`, and others (see `rustc_middle::sync`). |
| 57 | +/// This is done in query cycle handling code to determine **intended** first task for a |
| 58 | +/// single-threaded compiler front-end to execute even while multi-threaded. |
28 | 59 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] |
29 | 60 | pub struct TreeNodeIndex(u64); |
30 | 61 |
|
31 | 62 | impl TreeNodeIndex { |
| 63 | + /// Root node of a tree |
32 | 64 | pub const fn root() -> Self { |
33 | 65 | Self(0x80000000_00000000) |
34 | 66 | } |
35 | 67 |
|
36 | | - /// Append tree branch no. `branch_idx` reserving `bits` bits. |
37 | | - fn try_bits_branch(self, branch_idx: u64, bits: u32) -> Option<Self> { |
| 68 | + /// Append branch `i` out of `n` branches total to `TreeNodeIndex`'s traversal representation. |
| 69 | + /// |
| 70 | + /// This method reserves `ceil(log2(n))` bits within `TreeNodeIndex`'s integer encoded |
| 71 | + /// bitstring. |
| 72 | + pub fn branch(self, i: u64, n: u64) -> TreeNodeIndex { |
| 73 | + debug_assert!(i < n, "i = {i} should be less than n = {n}"); |
| 74 | + // `branch_num != 0` per debug assertion above |
| 75 | + let bits = ceil_ilog2(n); |
| 76 | + |
38 | 77 | let trailing_zeros = self.0.trailing_zeros(); |
39 | | - let allocated_shift = trailing_zeros.checked_sub(bits)?; |
| 78 | + let allocated_shift = trailing_zeros.checked_sub(bits).expect( |
| 79 | + "TreeNodeIndex's free bits have been exhausted, make sure recursion is used carefully", |
| 80 | + ); |
40 | 81 | // Using wrapping operations for optimization, as edge cases are unreachable: |
41 | 82 | // - `trailing_zeros < 64` as we are guaranteed at least one bit is set |
42 | 83 | // - `allocated_shift == trailing_zeros - bits <= trailing_zeros < 64` |
43 | | - Some(TreeNodeIndex( |
| 84 | + TreeNodeIndex( |
44 | 85 | self.0 & !u64::wrapping_shl(1, trailing_zeros) |
45 | 86 | | u64::wrapping_shl(1, allocated_shift) |
46 | | - | branch_idx.unbounded_shl(allocated_shift.wrapping_add(1)), |
47 | | - )) |
48 | | - } |
49 | | - |
50 | | - /// Append tree branch no. `branch_idx` reserving `ceil(log2(branch_num))` bits. |
51 | | - pub fn branch(self, branch_idx: u64, branch_num: u64) -> TreeNodeIndex { |
52 | | - debug_assert!( |
53 | | - branch_idx < branch_num, |
54 | | - "branch_idx = {branch_idx} should be less than branch_num = {branch_num}" |
55 | | - ); |
56 | | - // `branch_num != 0` per debug assertion above |
57 | | - let bits = ceil_ilog2(branch_num); |
58 | | - self.try_bits_branch(branch_idx, bits).expect( |
59 | | - "TreeNodeIndex's free bits have been exhausted, make sure recursion is used carefully", |
| 87 | + | i.unbounded_shl(allocated_shift.wrapping_add(1)), |
60 | 88 | ) |
61 | 89 | } |
62 | 90 | } |
|
0 commit comments