Skip to main content

bzip3_rs/
block_size.rs

1//! Range-checked block size for the bzip3 port.
2
3use crate::error::Error;
4use crate::error::Result;
5
6/// Block size in bytes, checked against the valid range.
7///
8/// Sizes run 65 KiB through 511 MiB. The default is 16 MiB.
9/// Level 1 selects `MIN`, level 14 selects `MAX`; levels 2-13 are
10/// powers of two from 128 KiB to 256 MiB.
11///
12/// # Examples
13///
14/// ```
15/// use bzip3_rs::BlockSize;
16///
17/// # fn main() -> Result<(), bzip3_rs::error::Error> {
18/// let size = BlockSize::kib(128)?;
19/// assert_eq!(size.get(), 131_072);
20/// assert!(BlockSize::MIN < size && size < BlockSize::MAX);
21/// assert_eq!(BlockSize::default(), BlockSize::DEFAULT);
22/// # Ok(())
23/// # }
24/// ```
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26#[repr(transparent)]
27pub struct BlockSize(u32);
28
29impl BlockSize {
30    /// Smallest valid size: 65 KiB.
31    pub const MIN: Self = Self(65 * 1024);
32    /// Largest valid size: 511 MiB.
33    pub const MAX: Self = Self(511 * 1024 * 1024);
34    /// Default size: 16 MiB.
35    pub const DEFAULT: Self = Self(16 * 1024 * 1024);
36
37    /// Build from a byte count.
38    ///
39    /// # Errors
40    ///
41    /// Returns [`Error::InvalidBlockSize`] when `size` is outside `MIN..=MAX`.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use bzip3_rs::BlockSize;
47    ///
48    /// assert!(BlockSize::new(1024 * 1024).is_ok());
49    /// assert!(BlockSize::new(1024).is_err()); // Below `MIN`.
50    /// assert!(BlockSize::new(u32::MAX).is_err()); // Above `MAX`.
51    /// ```
52    #[inline]
53    pub const fn new(size: u32) -> Result<Self> {
54        if size < Self::MIN.0 || size > Self::MAX.0 {
55            return Err(Error::InvalidBlockSize);
56        }
57        Ok(Self(size))
58    }
59
60    /// Build from a kibibyte count.
61    ///
62    /// # Errors
63    ///
64    /// Returns [`Error::InvalidBlockSize`] when the size is outside `MIN..=MAX`
65    /// or the multiplication overflows.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use bzip3_rs::BlockSize;
71    ///
72    /// # fn main() -> Result<(), bzip3_rs::error::Error> {
73    /// assert_eq!(BlockSize::kib(65)?, BlockSize::MIN);
74    /// assert!(BlockSize::kib(64).is_err());
75    /// # Ok(())
76    /// # }
77    /// ```
78    #[inline]
79    pub const fn kib(kibibytes: u32) -> Result<Self> {
80        match kibibytes.checked_mul(1024) {
81            Some(size) => Self::new(size),
82            None => Err(Error::InvalidBlockSize),
83        }
84    }
85
86    /// Build from a mebibyte count.
87    ///
88    /// # Errors
89    ///
90    /// Returns [`Error::InvalidBlockSize`] when the size is outside `MIN..=MAX`
91    /// or the multiplication overflows.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use bzip3_rs::BlockSize;
97    ///
98    /// # fn main() -> Result<(), bzip3_rs::error::Error> {
99    /// assert_eq!(BlockSize::mib(16)?, BlockSize::DEFAULT);
100    /// assert!(BlockSize::mib(512).is_err());
101    /// # Ok(())
102    /// # }
103    /// ```
104    #[inline]
105    pub const fn mib(mebibytes: u32) -> Result<Self> {
106        match mebibytes.checked_mul(1024 * 1024) {
107            Some(size) => Self::new(size),
108            None => Err(Error::InvalidBlockSize),
109        }
110    }
111
112    /// Build from a compression level 1-14.
113    ///
114    /// Level 1 selects [`Self::MIN`] (65 KiB). Levels 2-13 are powers
115    /// of two: 128 KiB, 256 KiB, 512 KiB, 1 MiB, 2 MiB, 4 MiB, 8 MiB,
116    /// 16 MiB, 32 MiB, 64 MiB, 128 MiB, 256 MiB.
117    /// Level 14 selects [`Self::MAX`] (511 MiB).
118    /// Level 9 matches [`Self::DEFAULT`].
119    ///
120    /// # Errors
121    ///
122    /// Returns [`Error::InvalidBlockSize`] when `level` is outside 1-14.
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// use bzip3_rs::BlockSize;
128    ///
129    /// # fn main() -> Result<(), bzip3_rs::error::Error> {
130    /// assert_eq!(BlockSize::from_level(1)?, BlockSize::MIN);
131    /// assert_eq!(BlockSize::from_level(9)?, BlockSize::DEFAULT);
132    /// assert_eq!(BlockSize::from_level(14)?, BlockSize::MAX);
133    /// assert!(BlockSize::from_level(0).is_err());
134    /// # Ok(())
135    /// # }
136    /// ```
137    pub const fn from_level(level: u8) -> Result<Self> {
138        match level {
139            1 => Self::kib(65),
140            2 => Self::kib(128),
141            3 => Self::kib(256),
142            4 => Self::kib(512),
143            5 => Self::mib(1),
144            6 => Self::mib(2),
145            7 => Self::mib(4),
146            8 => Self::mib(8),
147            9 => Self::mib(16),
148            10 => Self::mib(32),
149            11 => Self::mib(64),
150            12 => Self::mib(128),
151            13 => Self::mib(256),
152            14 => Self::mib(511),
153            _ => Err(Error::InvalidBlockSize),
154        }
155    }
156
157    /// Return the size in bytes.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use bzip3_rs::BlockSize;
163    ///
164    /// assert_eq!(BlockSize::MIN.get(), 65 * 1024);
165    /// ```
166    #[must_use]
167    #[inline]
168    pub const fn get(self) -> u32 {
169        self.0
170    }
171
172    /// Return the size in bytes as `usize`.
173    ///
174    /// On 16-bit targets the value truncates like `as`.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// use bzip3_rs::BlockSize;
180    ///
181    /// let buffer = vec![0_u8; BlockSize::MIN.as_usize()];
182    /// assert_eq!(buffer.len(), 65 * 1024);
183    /// ```
184    #[must_use]
185    #[inline]
186    pub const fn as_usize(self) -> usize {
187        self.0 as usize
188    }
189}
190
191impl Default for BlockSize {
192    #[inline]
193    fn default() -> Self {
194        Self::DEFAULT
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::BlockSize;
201    use crate::error::Error;
202
203    #[test]
204    fn limits_match_valid_range() {
205        assert_eq!(BlockSize::MIN.get(), 65 * 1024);
206        assert_eq!(BlockSize::MAX.get(), 511 * 1024 * 1024);
207        assert_eq!(BlockSize::DEFAULT.get(), 16 * 1024 * 1024);
208    }
209
210    #[test]
211    fn default_is_16_mib() {
212        assert_eq!(BlockSize::default(), BlockSize::DEFAULT);
213    }
214
215    #[test]
216    fn new_accepts_boundaries() {
217        assert_eq!(BlockSize::new(65 * 1024).unwrap().get(), 65 * 1024);
218        assert_eq!(
219            BlockSize::new(511 * 1024 * 1024).unwrap().get(),
220            511 * 1024 * 1024
221        );
222        assert_eq!(
223            BlockSize::new(16 * 1024 * 1024).unwrap(),
224            BlockSize::DEFAULT
225        );
226    }
227
228    #[test]
229    fn new_rejects_outside_range() {
230        for size in [0, 1, 65 * 1024 - 1, 511 * 1024 * 1024 + 1, u32::MAX] {
231            assert!(
232                matches!(BlockSize::new(size), Err(Error::InvalidBlockSize)),
233                "{size}"
234            );
235        }
236    }
237
238    #[test]
239    fn kib_ctor_scales_and_checks() {
240        assert_eq!(BlockSize::kib(65).unwrap().get(), 65 * 1024);
241        assert_eq!(BlockSize::kib(16 * 1024).unwrap(), BlockSize::DEFAULT);
242        assert!(matches!(BlockSize::kib(64), Err(Error::InvalidBlockSize)));
243        assert!(matches!(BlockSize::kib(0), Err(Error::InvalidBlockSize)));
244        assert!(matches!(
245            BlockSize::kib(u32::MAX),
246            Err(Error::InvalidBlockSize)
247        ));
248    }
249
250    #[test]
251    fn mib_ctor_scales_and_checks() {
252        assert_eq!(BlockSize::mib(16).unwrap(), BlockSize::DEFAULT);
253        assert_eq!(BlockSize::mib(1).unwrap().get(), 1024 * 1024);
254        assert_eq!(BlockSize::mib(511).unwrap(), BlockSize::MAX);
255        assert!(matches!(BlockSize::mib(0), Err(Error::InvalidBlockSize)));
256        assert!(matches!(BlockSize::mib(512), Err(Error::InvalidBlockSize)));
257        assert!(matches!(
258            BlockSize::mib(u32::MAX),
259            Err(Error::InvalidBlockSize)
260        ));
261    }
262
263    #[test]
264    fn level_maps_powers_of_two() {
265        for (level, bytes) in [
266            (1, 65 * 1024),
267            (2, 128 * 1024),
268            (3, 256 * 1024),
269            (4, 512 * 1024),
270            (5, 1024 * 1024),
271            (6, 2 * 1024 * 1024),
272            (7, 4 * 1024 * 1024),
273            (8, 8 * 1024 * 1024),
274            (9, 16 * 1024 * 1024),
275            (10, 32 * 1024 * 1024),
276            (11, 64 * 1024 * 1024),
277            (12, 128 * 1024 * 1024),
278            (13, 256 * 1024 * 1024),
279            (14, 511 * 1024 * 1024),
280        ] {
281            assert_eq!(
282                BlockSize::from_level(level).unwrap().get(),
283                bytes,
284                "level {level}"
285            );
286        }
287        assert_eq!(BlockSize::from_level(1).unwrap(), BlockSize::MIN);
288        assert_eq!(BlockSize::from_level(14).unwrap(), BlockSize::MAX);
289        assert_eq!(BlockSize::from_level(9).unwrap(), BlockSize::DEFAULT);
290        for level in [0, 15, u8::MAX] {
291            assert!(
292                matches!(BlockSize::from_level(level), Err(Error::InvalidBlockSize)),
293                "level {level}"
294            );
295        }
296    }
297}