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}