Skip to main content

tuwunel_core/utils/
bytes.rs

1//! Byte-size parsing, display, and integer encoding helpers.
2//!
3//! The module handles human-readable sizes alongside fixed-width big-endian
4//! counters. Its size deserializers integrate human-readable values with Serde.
5
6use bytesize::ByteSize;
7use serde::{Deserialize, Deserializer, de};
8
9use crate::{Result, at, err};
10
11/// Accepts an integer byte count or a string with SI/IEC suffix (e.g. "24 MiB")
12/// and returns a `usize`.
13pub fn deserialize_bytesize_usize<'de, D>(de: D) -> Result<usize, D::Error>
14where
15	D: Deserializer<'de>,
16{
17	ByteSize::deserialize(de)
18		.map(at!(0))
19		.map(usize::try_from)?
20		.map_err(de::Error::custom)
21}
22
23/// Accepts an integer byte count or a string with SI/IEC suffix (e.g. "32 MiB")
24/// and returns a `u64`.
25pub fn deserialize_bytesize_u64<'de, D>(de: D) -> Result<u64, D::Error>
26where
27	D: Deserializer<'de>,
28{
29	ByteSize::deserialize(de).map(at!(0))
30}
31
32/// Parse a human-writable size string w/ si-unit suffix into integer
33#[inline]
34pub fn from_str(str: &str) -> Result<usize> {
35	let bytes: ByteSize = str
36		.parse()
37		.map_err(|e| err!(Arithmetic("Failed to parse byte size: {e}")))?;
38
39	let bytes: usize = bytes
40		.as_u64()
41		.try_into()
42		.map_err(|e| err!(Arithmetic("Failed to convert u64 to usize: {e}")))?;
43
44	Ok(bytes)
45}
46
47/// Output a human-readable size string w/ iec-unit suffix
48#[inline]
49#[must_use]
50pub fn pretty(bytes: usize) -> String {
51	let bytes: u64 = bytes
52		.try_into()
53		.expect("failed to convert usize to u64");
54
55	ByteSize::b(bytes).display().iec().to_string()
56}
57
58/// Increments an optional big-endian counter with wrapping arithmetic.
59///
60/// Missing or malformed input is treated as zero. The returned array contains
61/// the incremented value in big-endian byte order.
62#[inline]
63#[must_use]
64pub fn increment(old: Option<&[u8]>) -> [u8; 8] {
65	old.map_or(0_u64, |bytes| u64_from_bytes(bytes).unwrap_or(0))
66		.wrapping_add(1)
67		.to_be_bytes()
68}
69
70/// Parses 8 big-endian bytes into an u64; panic on invalid argument
71#[inline]
72#[must_use]
73pub fn u64_from_u8(bytes: &[u8]) -> u64 {
74	u64_from_bytes(bytes).expect("must slice at least 8 bytes")
75}
76
77/// Parses the big-endian bytes into an u64.
78#[inline]
79pub fn u64_from_bytes(bytes: &[u8]) -> Result<u64> { Ok(u64::from_be_bytes(bytes.try_into()?)) }