1#![no_std]
2
3pub mod errors;
136
137use crate::errors::{ByteUnitParseError, NumBytesParseError};
138
139macro_rules! const_try {
141 ($expr:expr) => {
142 match $expr {
143 Ok(value) => value,
144 Err(error) => return Err(error),
145 }
146 };
147}
148
149macro_rules! define_types {
151 ($($name:ident, $type:literal;)*) => {
152 $(
153 #[doc = concat!("Represents a number of _", $type, "_.")]
154 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
155 pub struct $name {
156 #[doc = concat!("Raw number of ", $type, ".")]
157 pub raw: u64,
158 }
159
160 impl $name {
161 #[doc = concat!(
162 "Construct [`", stringify!($name),
163 "`] from the provided raw number of ", $type, "."
164 )]
165 pub const fn new(raw: u64) -> Self {
166 Self { raw }
167 }
168 }
169
170 impl core::ops::Add for $name {
171 type Output = $name;
172
173 fn add(self, rhs: Self) -> Self::Output {
174 self + rhs.raw
175 }
176 }
177
178 impl core::ops::Add<u64> for $name {
179 type Output = $name;
180
181 fn add(self, rhs: u64) -> Self::Output {
182 Self::new(self.raw.checked_add(rhs).expect(concat!("overflow adding ", $type)))
183 }
184 }
185
186 impl core::ops::AddAssign for $name {
187 fn add_assign(&mut self, rhs: Self) {
188 *self = *self + rhs;
189 }
190 }
191
192 impl core::ops::AddAssign<u64> for $name {
193 fn add_assign(&mut self, rhs: u64) {
194 *self = *self + rhs;
195 }
196 }
197
198 impl core::ops::Sub for $name {
199 type Output = $name;
200
201 fn sub(self, rhs: Self) -> Self::Output {
202 self - rhs.raw
203 }
204 }
205
206 impl core::ops::Sub<u64> for $name {
207 type Output = $name;
208
209 fn sub(self, rhs: u64) -> Self::Output {
210 Self::new(self.raw.checked_sub(rhs).expect(concat!("underflow subtracting ", $type)))
211 }
212 }
213
214 impl core::ops::SubAssign for $name {
215 fn sub_assign(&mut self, rhs: Self) {
216 *self = *self - rhs;
217 }
218 }
219
220 impl core::ops::SubAssign<u64> for $name {
221 fn sub_assign(&mut self, rhs: u64) {
222 *self = *self - rhs;
223 }
224 }
225
226 impl core::ops::Mul for $name {
227 type Output = $name;
228
229 fn mul(self, rhs: Self) -> Self::Output {
230 self * rhs.raw
231 }
232 }
233
234 impl core::ops::Mul<u64> for $name {
235 type Output = $name;
236
237 fn mul(self, rhs: u64) -> Self::Output {
238 Self::new(self.raw.checked_mul(rhs).expect(concat!("overflow multiplying ", $type)))
239 }
240 }
241
242 impl core::ops::MulAssign for $name {
243 fn mul_assign(&mut self, rhs: Self) {
244 *self = *self * rhs;
245 }
246 }
247
248 impl core::ops::MulAssign<u64> for $name {
249 fn mul_assign(&mut self, rhs: u64) {
250 *self = *self * rhs;
251 }
252 }
253
254 impl core::ops::Div for $name {
255 type Output = $name;
256
257 fn div(self, rhs: Self) -> Self::Output {
258 self / rhs.raw
259 }
260 }
261
262 impl core::ops::Div<u64> for $name {
263 type Output = $name;
264
265 fn div(self, rhs: u64) -> Self::Output {
266 Self::new(self.raw.checked_div(rhs).expect("division by zero"))
267 }
268 }
269
270 impl core::ops::DivAssign for $name {
271 fn div_assign(&mut self, rhs: Self) {
272 *self = *self / rhs;
273 }
274 }
275
276 impl core::ops::DivAssign<u64> for $name {
277 fn div_assign(&mut self, rhs: u64) {
278 *self = *self / rhs;
279 }
280 }
281
282 impl PartialEq<u64> for $name {
283 fn eq(&self, other: &u64) -> bool {
284 self.raw == *other
285 }
286 }
287
288 impl PartialEq<$name> for u64 {
289 fn eq(&self, other: &$name) -> bool {
290 *self == other.raw
291 }
292 }
293
294 impl PartialOrd<u64> for $name {
295 fn partial_cmp(&self, other: &u64) -> Option<core::cmp::Ordering> {
296 self.raw.partial_cmp(other)
297 }
298 }
299
300 impl PartialOrd<$name> for u64 {
301 fn partial_cmp(&self, other: &$name) -> Option<core::cmp::Ordering> {
302 self.partial_cmp(&other.raw)
303 }
304 }
305 )*
306 };
307}
308
309define_types! {
310 NumBits, "bits";
311 NumBytes, "bytes";
312 NumBlocks, "blocks";
313}
314
315impl NumBits {
316 pub const fn to_bytes_floor(self) -> NumBytes {
318 NumBytes::new(self.raw / 8)
319 }
320
321 pub const fn to_bytes_ceil(self) -> NumBytes {
323 NumBytes::new(self.raw.div_ceil(8))
324 }
325}
326
327#[cfg(feature = "serde")]
328impl serde::Serialize for NumBits {
329 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
330 where
331 S: serde::Serializer,
332 {
333 serializer.serialize_u64(self.raw)
334 }
335}
336
337#[cfg(feature = "serde")]
338impl<'de> serde::Deserialize<'de> for NumBits {
339 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
340 where
341 D: serde::Deserializer<'de>,
342 {
343 Ok(Self::new(u64::deserialize(deserializer)?))
344 }
345}
346
347impl core::fmt::Display for NumBits {
348 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
349 write!(f, "{}bits", self.raw)
350 }
351}
352
353macro_rules! impl_bit_byte_op {
355 ($($trait:ident, $func:ident, $trait_assign:ident, $func_assign:ident;)*) => {
356 $(
357 impl core::ops::$trait<NumBytes> for NumBits {
358 type Output = NumBits;
359
360 fn $func(self, rhs: NumBytes) -> Self::Output {
361 self.$func(rhs.to_bits())
362 }
363 }
364
365 impl core::ops::$trait<NumBits> for NumBytes {
366 type Output = NumBits;
367
368 fn $func(self, rhs: NumBits) -> Self::Output {
369 self.to_bits().$func(rhs)
370 }
371 }
372
373 impl core::ops::$trait_assign<NumBytes> for NumBits {
374 fn $func_assign(&mut self, rhs: NumBytes) {
375 self.$func_assign(rhs.to_bits());
376 }
377 }
378 )*
379 };
380}
381
382impl_bit_byte_op! {
383 Add, add, AddAssign, add_assign;
384 Sub, sub, SubAssign, sub_assign;
385}
386
387impl From<NumBytes> for NumBits {
388 fn from(value: NumBytes) -> Self {
389 value.to_bits()
390 }
391}
392
393impl PartialEq<NumBits> for NumBytes {
394 fn eq(&self, other: &NumBits) -> bool {
395 self.to_bits() == *other
396 }
397}
398
399impl PartialEq<NumBytes> for NumBits {
400 fn eq(&self, other: &NumBytes) -> bool {
401 *self == other.to_bits()
402 }
403}
404
405impl PartialOrd<NumBits> for NumBytes {
406 fn partial_cmp(&self, other: &NumBits) -> Option<core::cmp::Ordering> {
407 self.to_bits().partial_cmp(other)
408 }
409}
410
411impl PartialOrd<NumBytes> for NumBits {
412 fn partial_cmp(&self, other: &NumBytes) -> Option<core::cmp::Ordering> {
413 self.partial_cmp(&other.to_bits())
414 }
415}
416
417impl NumBytes {
418 pub const fn from_usize(n: usize) -> Self {
420 if usize::BITS > u64::BITS && n > (u64::MAX as usize) {
421 panic!("unable to convert `usize` to `NumBytes`, number exceeds 64 bits")
422 } else {
423 Self::new(n as u64)
425 }
426 }
427
428 pub const fn to_bits(self) -> NumBits {
430 NumBits::new(
431 self.raw
432 .checked_mul(8)
433 .expect("overflow converting bytes to bits"),
434 )
435 }
436
437 pub const fn to_blocks_floor(self, block_size: NumBytes) -> NumBlocks {
439 NumBlocks::new(
440 self.raw
441 .checked_div(block_size.raw)
442 .expect("division by zero, block size is zero"),
443 )
444 }
445
446 pub const fn to_blocks_ceil(self, block_size: NumBytes) -> NumBlocks {
448 if block_size.raw == 0 {
449 panic!("division by zero, block size is zero")
450 }
451 NumBlocks::new(self.raw.div_ceil(block_size.raw))
452 }
453
454 pub const fn split_fractional(self, unit: ByteUnit) -> (u64, u64) {
456 let whole = self.raw / unit.num_bytes().raw;
457 let fractional = self.raw % unit.num_bytes().raw;
458 (whole, fractional)
459 }
460
461 pub const fn display_unit(self) -> ByteUnit {
463 let mut unit_idx = ByteUnit::UNITS.len() - 1;
464 while unit_idx > 0 {
465 if ByteUnit::UNITS[unit_idx].num_bytes().raw <= self.raw {
466 break;
467 }
468 unit_idx -= 1;
469 }
470 ByteUnit::UNITS[unit_idx]
471 }
472
473 pub const fn parse_str(s: &str) -> Result<NumBytes, NumBytesParseError> {
475 Self::parse_ascii(s.as_bytes())
476 }
477
478 pub const fn parse_ascii(mut buffer: &[u8]) -> Result<NumBytes, NumBytesParseError> {
480 const fn expect_int(
481 buffer: &mut &[u8],
482 truncate: bool,
483 ) -> Result<(u64, u128), NumBytesParseError> {
484 let mut value = 0u64;
485 let mut base = 1;
486 while let Some((head, tail)) = buffer.split_first() {
487 match *head {
488 b'0'..=b'9' => {
489 match value.checked_mul(10) {
490 Some(shifted) => {
491 let Some(new_value) = shifted.checked_add((*head - b'0') as u64)
492 else {
493 if !truncate {
494 return Err(NumBytesParseError::Overflow);
495 } else {
496 continue;
497 }
498 };
499 value = new_value;
500 if value != 0 {
501 base *= 10;
502 }
503 }
504 None if !truncate => {
505 return Err(NumBytesParseError::Overflow);
506 }
507 _ => { }
508 };
509 }
510 b'_' => { }
511 _ => break,
512 }
513 *buffer = tail;
514 }
515 if base > 1 {
516 Ok((value, base))
517 } else {
518 Err(NumBytesParseError::Format)
520 }
521 }
522 let (whole, _) = const_try!(expect_int(&mut buffer, false));
523 let mut fractional = (0, 1);
524 if let Some((b'.', tail)) = buffer.split_first() {
525 buffer = tail;
526 fractional = const_try!(expect_int(&mut buffer, true));
527 }
528 while let Some((b' ', tail)) = buffer.split_first() {
529 buffer = tail;
530 }
531 let unit = if buffer.is_empty() {
532 ByteUnit::Byte
533 } else {
534 match ByteUnit::parse_ascii(buffer) {
535 Ok(unit) => unit,
536 Err(_) => return Err(NumBytesParseError::Format),
537 }
538 };
539 let Some(value) = whole.checked_mul(unit.num_bytes().raw) else {
540 return Err(NumBytesParseError::Overflow);
541 };
542 let (mut fractional_value, mut fractional_base) = fractional;
543 if fractional_value != 0 && !matches!(unit, ByteUnit::Byte) {
544 let unit_divisor = unit.base10_fractional_divisor() as u128;
545 while fractional_base >= unit_divisor {
547 fractional_value /= 10;
548 fractional_base /= 10;
549 }
550 let fractional_value = fractional_value as u128 * unit_divisor / fractional_base;
554 let unit_value = unit.num_bytes().raw as u128;
555 let unit_divisor = unit_divisor as u128;
556 let fractional_part = fractional_value * unit_value / unit_divisor;
557 if let Some(value) = value.checked_add(fractional_part as u64) {
558 return Ok(NumBytes::new(value));
559 } else {
560 return Err(NumBytesParseError::Overflow);
561 }
562 }
563 Ok(NumBytes::new(value))
564 }
565}
566
567impl core::fmt::Display for NumBytes {
568 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
569 use core::fmt::Write;
570 let unit = self.display_unit();
571 let (whole, fractional) = self.split_fractional(unit);
572 write!(f, "{}", whole)?;
573 let precision = f
575 .precision()
576 .map(|p| p as u32)
577 .unwrap_or(u32::MAX)
578 .min(unit.base10_fractional_digits());
579 let mut fractional_base = 10u64.pow(precision);
580 let mut fractional_value = ((fractional as u128) * (fractional_base as u128)
584 / (unit.num_bytes().raw as u128)) as u64;
585 if let Some(_) = f.precision() {
586 f.write_char('.')?;
587 write!(f, "{fractional_value:0p$}", p = precision as usize)?;
588 } else if fractional_value != 0 {
589 f.write_char('.')?;
590 fractional_base /= 10;
591 while fractional_base > 0 && fractional_value > 0 {
592 let digit = fractional_value / fractional_base;
593 write!(f, "{digit}")?;
594 fractional_value %= fractional_base;
595 fractional_base /= 10;
596 }
597 }
598 f.write_str(unit.as_str())
599 }
600}
601
602impl core::str::FromStr for NumBytes {
603 type Err = NumBytesParseError;
604
605 fn from_str(s: &str) -> Result<Self, Self::Err> {
606 NumBytes::parse_str(s)
607 }
608}
609
610#[cfg(feature = "serde")]
611impl serde::Serialize for NumBytes {
612 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
613 where
614 S: serde::Serializer,
615 {
616 serializer.serialize_u64(self.raw)
617 }
618}
619
620#[cfg(feature = "serde")]
621impl<'de> serde::Deserialize<'de> for NumBytes {
622 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
623 where
624 D: serde::Deserializer<'de>,
625 {
626 struct Visitor;
627
628 impl<'de> serde::de::Visitor<'de> for Visitor {
629 type Value = NumBytes;
630
631 fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
632 f.write_str("byte size")
633 }
634
635 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
636 where
637 E: serde::de::Error,
638 {
639 NumBytes::parse_str(v)
640 .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(v), &"byte size"))
641 }
642
643 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
644 where
645 E: serde::de::Error,
646 {
647 Ok(NumBytes::new(v))
648 }
649 }
650
651 deserializer.deserialize_u64(Visitor)
652 }
653}
654
655impl NumBlocks {
656 pub const fn to_bytes(self, block_size: NumBytes) -> NumBytes {
658 NumBytes::new(
659 self.raw
660 .checked_mul(block_size.raw)
661 .expect("overflow converting blocks to bytes"),
662 )
663 }
664}
665
666impl core::fmt::Display for NumBlocks {
667 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
668 write!(f, "{}blocks", self.raw)
669 }
670}
671
672macro_rules! define_units {
674 ($($name:ident, $name_lower:ident, $suffix:literal, $suffix_lower:literal, $value:expr;)*) => {
675 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
677 pub enum ByteUnit {
678 Byte,
680 $(
681 #[doc = concat!("1 ", $suffix, " is `", stringify!($value), "` bytes.")]
682 $name,
683 )*
684 }
685
686 impl ByteUnit {
687 pub const UNITS: &[Self] = &[Self::Byte, $(Self::$name),*];
689
690 pub const fn as_str(self) -> &'static str {
692 match self {
693 Self::Byte => "B",
694 $(
695 Self::$name => $suffix,
696 )*
697 }
698 }
699
700 pub const fn num_bytes(self) -> NumBytes {
702 NumBytes::new(match self {
703 Self::Byte => 1,
704 $(
705 Self::$name => $value,
706 )*
707 })
708 }
709
710 const fn parse_ascii_lowercase(ascii: &[u8]) -> Result<Self, ByteUnitParseError> {
712 #![expect(non_upper_case_globals)]
713 $(
714 const $name: &[u8] = $suffix_lower.as_bytes();
715 )*
716 match ascii {
717 b"b" => Ok(Self::Byte),
718 b"k" => Ok(Self::Kibibyte),
719 b"m" => Ok(Self::Mebibyte),
720 b"g" => Ok(Self::Gibibyte),
721 b"t" => Ok(Self::Tebibyte),
722 b"p" => Ok(Self::Pebibyte),
723 b"e" => Ok(Self::Exbibyte),
724 $(
725 $name => Ok(Self::$name),
726 )*
727 _ => Err(ByteUnitParseError)
728 }
729 }
730
731 const fn base10_fractional_digits(self) -> u32 {
736 match self {
737 Self::Byte => 0,
738 $(
739 Self::$name => (Self::$name.num_bytes().raw * 10 - 1).ilog10(),
740 )*
741 }
742 }
743
744 const fn base10_fractional_divisor(self) -> u64 {
746 10u64.pow(self.base10_fractional_digits())
747 }
748 }
749
750 impl NumBytes {
751 pub const fn bytes(n: u64) -> Self {
753 Self::new(n)
754 }
755
756 $(
757 #[doc = concat!("Construct [`NumBytes`] from the given number `n` of ", stringify!($name), "s.")]
758 pub const fn $name_lower(n: u64) -> NumBytes {
759 NumBytes::new(n * $value)
760 }
761 )*
762 }
763 };
764}
765
766define_units! {
767 Kilobyte, kilobytes, "kB", "kb", 10u64.pow(3);
768 Kibibyte, kibibytes, "KiB", "kib", 1 << 10;
769 Megabyte, megabytes, "MB", "mb", 10u64.pow(6);
770 Mebibyte, mebibytes, "MiB", "mib", 1 << 20;
771 Gigabyte, gigabytes, "GB", "gb", 10u64.pow(9);
772 Gibibyte, gibibytes, "GiB", "gib", 1 << 30;
773 Terabyte, terabytes, "TB", "tb", 10u64.pow(12);
774 Tebibyte, tebibytes, "TiB", "tib", 1 << 40;
775 Petabyte, petabytes, "PB", "pb", 10u64.pow(15);
776 Pebibyte, pebibytes, "PiB", "pib", 1 << 50;
777 Exabyte, exabytes, "EB", "eb", 10u64.pow(18);
778 Exbibyte, exbibytes, "EiB", "eib", 1 << 60;
779}
780
781impl ByteUnit {
782 pub const fn parse_ascii(ascii: &[u8]) -> Result<Self, ByteUnitParseError> {
784 match ascii {
786 [b1] => Self::parse_ascii_lowercase(&[b1.to_ascii_lowercase()]),
787 [b1, b2] => {
788 Self::parse_ascii_lowercase(&[b1.to_ascii_lowercase(), b2.to_ascii_lowercase()])
789 }
790 [b1, b2, b3] => Self::parse_ascii_lowercase(&[
791 b1.to_ascii_lowercase(),
792 b2.to_ascii_lowercase(),
793 b3.to_ascii_lowercase(),
794 ]),
795 _ => Err(ByteUnitParseError),
796 }
797 }
798
799 pub const fn parse_str(string: &str) -> Result<Self, ByteUnitParseError> {
801 Self::parse_ascii(string.as_bytes())
802 }
803}
804
805impl core::str::FromStr for ByteUnit {
806 type Err = ByteUnitParseError;
807
808 fn from_str(s: &str) -> Result<Self, Self::Err> {
809 Self::parse_str(s)
810 }
811}
812
813impl From<ByteUnit> for NumBytes {
814 fn from(value: ByteUnit) -> Self {
815 value.num_bytes()
816 }
817}
818
819pub trait ByteLen {
821 fn byte_len(&self) -> NumBytes;
823}
824
825impl ByteLen for str {
826 fn byte_len(&self) -> NumBytes {
827 NumBytes::from_usize(self.len())
828 }
829}
830
831impl ByteLen for [u8] {
832 fn byte_len(&self) -> NumBytes {
833 NumBytes::from_usize(self.len())
834 }
835}
836
837#[cfg(test)]
838mod tests {
839 use super::*;
840
841 #[test]
842 pub fn test_byte_unit_order() {
843 let mut unit_iter = ByteUnit::UNITS.iter().peekable();
844 while let Some(this) = unit_iter.next() {
845 if let Some(next) = unit_iter.peek() {
846 assert!(this.num_bytes() < next.num_bytes())
847 }
848 }
849 }
850
851 #[test]
852 pub fn test_byte_display_unit() {
853 assert_eq!(NumBytes::new(0).display_unit(), ByteUnit::Byte);
854 for unit in ByteUnit::UNITS {
855 assert_eq!(unit.num_bytes().display_unit(), *unit);
856 }
857 }
858
859 #[test]
860 pub fn test_base10_fractional_digits() {
861 assert_eq!(ByteUnit::Byte.base10_fractional_digits(), 0);
862 assert_eq!(ByteUnit::Kilobyte.base10_fractional_digits(), 3);
863 assert_eq!(ByteUnit::Kibibyte.base10_fractional_digits(), 4);
864 assert_eq!(ByteUnit::Exabyte.base10_fractional_digits(), 18);
865 assert_eq!(ByteUnit::Exbibyte.base10_fractional_digits(), 19);
866 }
867}