bao_tree/
tree.rs

1//! Define a number of newtypes and operations on these newtypes
2//!
3//! Most operations are concerned with node indexes in an in order traversal of a binary tree.
4use std::{
5    fmt,
6    ops::{Add, Div, Mul, Sub},
7};
8
9use range_collections::range_set::RangeSetEntry;
10
11/// A number of blake3 chunks.
12///
13/// This is a newtype for u64.
14/// The blake3 chunk size is 1024 bytes.
15#[repr(transparent)]
16#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct ChunkNum(pub u64);
19
20impl std::fmt::Debug for ChunkNum {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        if f.alternate() {
23            write!(f, "ChunkNum({:#x})", self.0)
24        } else {
25            write!(f, "{}", self.0)
26        }
27    }
28}
29
30impl std::fmt::Display for ChunkNum {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        std::fmt::Debug::fmt(self, f)
33    }
34}
35
36impl RangeSetEntry for ChunkNum {
37    fn min_value() -> Self {
38        ChunkNum(0)
39    }
40
41    fn is_min_value(&self) -> bool {
42        self.0 == 0
43    }
44}
45
46impl Mul<u64> for ChunkNum {
47    type Output = ChunkNum;
48
49    fn mul(self, rhs: u64) -> Self::Output {
50        ChunkNum(self.0 * rhs)
51    }
52}
53
54impl Div<u64> for ChunkNum {
55    type Output = ChunkNum;
56
57    fn div(self, rhs: u64) -> Self::Output {
58        ChunkNum(self.0 / rhs)
59    }
60}
61
62impl Sub<u64> for ChunkNum {
63    type Output = ChunkNum;
64
65    fn sub(self, rhs: u64) -> Self::Output {
66        ChunkNum(self.0 - rhs)
67    }
68}
69
70impl Sub<ChunkNum> for ChunkNum {
71    type Output = ChunkNum;
72
73    fn sub(self, rhs: ChunkNum) -> Self::Output {
74        ChunkNum(self.0 - rhs.0)
75    }
76}
77
78impl Add<u64> for ChunkNum {
79    type Output = ChunkNum;
80
81    fn add(self, rhs: u64) -> Self::Output {
82        ChunkNum(self.0 + rhs)
83    }
84}
85
86impl Add<ChunkNum> for ChunkNum {
87    type Output = ChunkNum;
88
89    fn add(self, rhs: ChunkNum) -> Self::Output {
90        ChunkNum(self.0 + rhs.0)
91    }
92}
93
94impl PartialEq<u64> for ChunkNum {
95    fn eq(&self, other: &u64) -> bool {
96        self.0 == *other
97    }
98}
99
100impl PartialEq<ChunkNum> for u64 {
101    fn eq(&self, other: &ChunkNum) -> bool {
102        *self == other.0
103    }
104}
105
106impl PartialOrd<u64> for ChunkNum {
107    fn partial_cmp(&self, other: &u64) -> Option<std::cmp::Ordering> {
108        self.0.partial_cmp(other)
109    }
110}
111
112impl ChunkNum {
113    /// Convert to usize or panic if it doesn't fit.
114    pub fn to_usize(self) -> usize {
115        usize::try_from(self.0).expect("usize overflow")
116    }
117}
118
119pub(crate) const BLAKE3_CHUNK_SIZE: usize = 1024;
120
121/// A block size.
122///
123/// Block sizes are powers of 2, with the smallest being 1024 bytes.
124/// They are encoded as the power of 2, minus 10, so 1 is 1024 bytes, 2 is 2048 bytes, etc.
125///
126/// Since only powers of 2 are valid, the log2 of the size in bytes / 1024 is given in the
127/// constructor. So a block size of 0 is 1024 bytes, 1 is 2048 bytes, etc.
128///
129/// The actual size in bytes can be computed with [BlockSize::bytes].
130#[repr(transparent)]
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
132pub struct BlockSize(pub(crate) u8);
133
134impl fmt::Display for BlockSize {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        fmt::Debug::fmt(&self, f)
137    }
138}
139
140impl BlockSize {
141    /// Create a block size from the log2 of the size in bytes / 1024
142    ///
143    /// 0 is 1024 bytes, 1 is 2048 bytes, etc.
144    pub const fn from_chunk_log(chunk_log: u8) -> Self {
145        Self(chunk_log)
146    }
147
148    /// Get the log2 of the number of 1 KiB blocks in a chunk.
149    pub const fn chunk_log(self) -> u8 {
150        self.0
151    }
152
153    /// The default block size, 1024 bytes
154    ///
155    /// This means that blocks and blake3 chunks are the same size.
156    pub const ZERO: BlockSize = BlockSize(0);
157
158    /// Number of bytes in a block at this level
159    pub const fn bytes(self) -> usize {
160        BLAKE3_CHUNK_SIZE << self.0
161    }
162
163    /// Compute a block size from bytes
164    pub const fn from_bytes(bytes: u64) -> Option<Self> {
165        if bytes.count_ones() != 1 {
166            // must be a power of 2
167            return None;
168        }
169        if bytes < 1024 {
170            // must be at least 1024 bytes
171            return None;
172        }
173        Some(Self((bytes.trailing_zeros() - 10) as u8))
174    }
175
176    /// Convert to an u32 for comparison with levels
177    pub(crate) const fn to_u32(self) -> u32 {
178        self.0 as u32
179    }
180}
181
182impl ChunkNum {
183    /// Start (inclusive) of the chunk group that this chunk is in
184    pub const fn chunk_group_start(start: ChunkNum, block_size: BlockSize) -> ChunkNum {
185        ChunkNum((start.0 >> block_size.0) << block_size.0)
186    }
187
188    /// End (exclusive) of the chunk group that this chunk the end for
189    pub const fn chunk_group_end(end: ChunkNum, block_size: BlockSize) -> ChunkNum {
190        let mask = (1 << block_size.0) - 1;
191        let part = ((end.0 & mask) != 0) as u64;
192        let whole = end.0 >> block_size.0;
193        ChunkNum((whole + part) << block_size.0)
194    }
195
196    /// number of chunks that this number of bytes covers
197    ///
198    /// E.g. 1024 bytes is 1 chunk, 1025 bytes is 2 chunks
199    pub const fn chunks(size: u64) -> ChunkNum {
200        let mask = (1 << 10) - 1;
201        let part = ((size & mask) != 0) as u64;
202        let whole = size >> 10;
203        ChunkNum(whole + part)
204    }
205
206    /// number of chunks that this number of bytes covers
207    ///
208    /// E.g. 1024 bytes is 1 chunk, 1025 bytes is still 1 chunk
209    pub const fn full_chunks(size: u64) -> ChunkNum {
210        ChunkNum(size >> 10)
211    }
212
213    /// number of bytes that this number of chunks covers
214    pub const fn to_bytes(&self) -> u64 {
215        self.0 << 10
216    }
217}