Skip to main content

sark_core/http/
prefixed_int.rs

1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2pub enum PrefixedIntError {
3    NeedMore,
4    Overflow,
5}
6
7/// An HPACK/QPACK integer whose prefix width is fixed by its type.
8///
9/// Every `u64` is representable; construction only records the wire format.
10///
11/// Invalid wire widths expose no safe constructor or codec:
12///
13/// ```compile_fail
14/// use sark_core::http::PrefixedInt;
15///
16/// let _ = PrefixedInt::<0>::new(0);
17/// ```
18#[repr(transparent)]
19#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct PrefixedInt<const BITS: u8>(u64);
21
22#[doc(hidden)]
23pub trait ValidPrefixedIntWidth {
24    const MAX_PREFIX: u64;
25}
26
27macro_rules! valid_widths {
28    ($($bits:literal),+ $(,)?) => {
29        $(
30            impl ValidPrefixedIntWidth for PrefixedInt<$bits> {
31                const MAX_PREFIX: u64 = (1u64 << $bits) - 1;
32            }
33        )+
34    };
35}
36
37valid_widths!(1, 2, 3, 4, 5, 6, 7, 8);
38
39impl<const BITS: u8> PrefixedInt<BITS>
40where
41    Self: ValidPrefixedIntWidth,
42{
43    pub const fn new(value: u64) -> Self {
44        Self(value)
45    }
46
47    pub const fn get(self) -> u64 {
48        self.0
49    }
50
51    pub const fn encoded_len(self) -> usize {
52        if self.0 < Self::MAX_PREFIX {
53            return 1;
54        }
55        let remaining = self.0 - Self::MAX_PREFIX;
56        2 + ((remaining | 1).ilog2() / 7) as usize
57    }
58
59    pub fn encode(self, prefix_byte: u8, out: &mut impl Extend<u8>) {
60        let low_mask = Self::MAX_PREFIX as u8;
61        let high = prefix_byte & !low_mask;
62        if self.0 < Self::MAX_PREFIX {
63            out.extend([high | self.0 as u8]);
64            return;
65        }
66        out.extend([high | low_mask]);
67        let mut remaining = self.0 - Self::MAX_PREFIX;
68        while remaining >= 128 {
69            out.extend([((remaining & 0x7f) as u8) | 0x80]);
70            remaining >>= 7;
71        }
72        out.extend([remaining as u8]);
73    }
74
75    pub fn decode(buf: &[u8]) -> Result<(Self, usize), PrefixedIntError> {
76        let Some(&byte) = buf.first() else {
77            return Err(PrefixedIntError::NeedMore);
78        };
79        let first = (byte & Self::MAX_PREFIX as u8) as u64;
80        if first < Self::MAX_PREFIX {
81            return Ok((Self(first), 1));
82        }
83
84        let mut value = Self::MAX_PREFIX;
85        let mut shift = 0u32;
86        let mut pos = 1usize;
87        loop {
88            let Some(&byte) = buf.get(pos) else {
89                return Err(PrefixedIntError::NeedMore);
90            };
91            pos += 1;
92            let chunk = (byte & 0x7f) as u64;
93            let shifted = chunk.checked_shl(shift).ok_or(PrefixedIntError::Overflow)?;
94            value = value
95                .checked_add(shifted)
96                .ok_or(PrefixedIntError::Overflow)?;
97            if byte & 0x80 == 0 {
98                return Ok((Self(value), pos));
99            }
100            shift = shift.checked_add(7).ok_or(PrefixedIntError::Overflow)?;
101            if shift >= 64 {
102                return Err(PrefixedIntError::Overflow);
103            }
104        }
105    }
106}
107
108impl<const BITS: u8> From<PrefixedInt<BITS>> for u64
109where
110    PrefixedInt<BITS>: ValidPrefixedIntWidth,
111{
112    fn from(value: PrefixedInt<BITS>) -> Self {
113        value.get()
114    }
115}