Skip to main content

flederfuchs_amqp/unsigned/
uint.rs

1//! This module provides tools for encoding and decoding `u32` values in specific AMQP-inspired formats.
2//!
3//! # Examples
4//!
5//! ```rust
6//! use flederfuchs_amqp::{ser_uint, de_uint, UIntByteRepr};
7//!
8//! // Encoding examples
9//! let zero = ser_uint(0);
10//! assert_eq!(zero, UIntByteRepr::ZeroOctet([0x43]));
11//!
12//! let small = ser_uint(127);
13//! assert_eq!(small, UIntByteRepr::SingleOctet([0x52, 127]));
14//!
15//! let large = ser_uint(0x01020304);
16//! assert_eq!(large, UIntByteRepr::FourOctets([0x70, 0x01, 0x02, 0x03, 0x04]));
17//!
18//! // Decoding examples
19//! let bytes = [0x43];
20//! assert_eq!(de_uint(&bytes), Some((0, &[][..])));
21//!
22//! let bytes = [0x52, 0x7F];
23//! assert_eq!(de_uint(&bytes), Some((127, &[][..])));
24//!
25//! let bytes = [0x70, 0x01, 0x02, 0x03, 0x04];
26//! assert_eq!(de_uint(&bytes), Some((0x01020304, &[][..])));
27//! ```
28
29use crate::conditional::if_marker;
30use crate::{de_fixed_width_empty, de_fixed_width_four, de_fixed_width_one};
31
32type UInt = u32;
33
34///
35/// Represents the different byte encodings of a 32-bit unsigned signed.
36///
37/// This enum provides three variants based on the size of the signed's encoding:
38/// - `ZeroOctet`: Represents an signed value of `0`, encoded as a single byte `[0x43]`.
39/// - `SingleOctet`: Represents integers that can fit into a single byte, encoded as two bytes `[0x52, x]`.
40/// - `FourOctets`: Represents integers requiring a full 32-bit representation, encoded as five bytes `[0x70, x1, x2, x3, x4]`.
41///
42/// These formats are designed to handle encoded AMQP integers.
43#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
44pub enum UIntByteRepr {
45    /// Represents the zero-byte encoding of a 32-bit unsigned signed.
46    ///
47    /// This variant is used when the signed value is `0`. It is represented
48    /// as a single byte `[0x43]`.
49    ///
50    /// # Example
51    ///
52    /// ```
53    /// use flederfuchs_amqp::UIntByteRepr;
54    ///
55    /// let encoding = UIntByteRepr::ZeroOctet([0x43]);
56    /// assert_eq!(encoding, UIntByteRepr::ZeroOctet([0x43]));
57    /// ```
58    ZeroOctet([u8; 1]),
59
60    /// Represents the single-octet encoding of a 32-bit unsigned signed.
61    ///
62    /// This variant is used when the signed value fits within a single byte.
63    /// It is represented as two bytes `[0x52, x]`, where `x` is the signed value.
64    ///
65    /// # Example
66    ///
67    /// ```
68    /// use flederfuchs_amqp::UIntByteRepr;
69    ///
70    /// let encoding = UIntByteRepr::SingleOctet([0x52, 0x7F]);
71    /// assert_eq!(encoding, UIntByteRepr::SingleOctet([0x52, 0x7F]));
72    /// ```
73    SingleOctet([u8; 2]),
74
75    /// Represents the four-octet encoding of a 32-bit unsigned signed.
76    ///
77    /// This variant is used when the signed value requires the full 32-bit representation.
78    /// It is represented as five bytes `[0x70, x1, x2, x3, x4]`, where `x1..x4` are the big-endian
79    /// bytes of the signed value.
80    ///
81    /// # Example
82    ///
83    /// ```
84    /// use flederfuchs_amqp::UIntByteRepr;
85    ///
86    /// let encoding = UIntByteRepr::FourOctets([0x70, 0x01, 0x02, 0x03, 0x04]);
87    /// assert_eq!(encoding, UIntByteRepr::FourOctets([0x70, 0x01, 0x02, 0x03, 0x04]));
88    /// ```
89    FourOctets([u8; 5]),
90}
91
92/// Encode a 32-bit unsigned signed into its respective encoding format.
93///
94/// The encoding uses one of the following formats:
95/// 1. `ZeroOctet` for an signed value of `0`. This is represented by a single byte `[0x43]`.
96/// 2. `SingleOctet` for integers that fit into a single byte. This is represented by two bytes `[0x52, x]`,
97///    where `x` is the signed value.
98/// 3. `FourOctets` for integers that require full 32-bit representation. This is represented by five bytes
99///    `[0x70, x1, x2, x3, x4]`, where `x1..x4` are the big-endian bytes of the signed value.
100///
101/// # Examples
102///
103/// ```rust
104/// use flederfuchs_amqp::ser_uint;
105/// use flederfuchs_amqp::UIntByteRepr;
106///
107/// // Encoding the number zero
108/// assert!(matches!(
109///     ser_uint(0),
110///     UIntByteRepr::ZeroOctet([0x43])
111/// ));
112///
113/// // Encoding a single byte signed
114/// assert!(matches!(
115///     ser_uint(127),
116///     UIntByteRepr::SingleOctet([0x52, 127])
117/// ));
118///
119/// // Encoding a full 32-bit signed
120/// assert!(matches!(
121///     ser_uint(0x01020304),
122///     UIntByteRepr::FourOctets([0x70, 0x01, 0x02, 0x03, 0x04])
123/// ));
124/// ```
125///
126#[must_use]
127pub fn ser_uint(number: u32) -> UIntByteRepr {
128    if let Some(value) = ser_uint0(number) {
129        return value;
130    }
131
132    if let Some(value) = ser_smalluint(number) {
133        return value;
134    }
135
136    ser_uint_(number)
137}
138
139const fn ser_uint_(number: u32) -> UIntByteRepr {
140    let split_bytes = number.to_be_bytes();
141    UIntByteRepr::FourOctets([
142        0x70,
143        split_bytes[0],
144        split_bytes[1],
145        split_bytes[2],
146        split_bytes[3],
147    ])
148}
149
150fn ser_smalluint(number: u32) -> Option<UIntByteRepr> {
151    if let Ok(value) = u8::try_from(number) {
152        return Some(UIntByteRepr::SingleOctet([0x52, value]));
153    }
154    None
155}
156
157const fn ser_uint0(number: u32) -> Option<UIntByteRepr> {
158    if number == 0 {
159        return Some(UIntByteRepr::ZeroOctet([0x43]));
160    }
161    None
162}
163
164/// Try and parse a ushort primitive value from a byte slice.
165///
166/// This function attempts to decode a `UShort` (a 16-bit unsigned signed) from a byte slice.
167/// The first byte should match the expected prefix (`0x60`), followed by two bytes representing
168/// the big-endian encoded `UShort`. Any remaining bytes are returned as part of the result.
169///
170/// # Examples
171///
172/// ```
173/// use flederfuchs_amqp::de_ushort;
174///
175/// // Parsing a valid ushort primitive with no extra bytes
176/// let bytes = &[0x60, 0x03, 0xE8];
177/// let result = de_ushort(bytes);
178/// assert_eq!(result, Some((1000, &[] as &[u8])));
179///
180/// // Parsing a valid ushort primitive with additional bytes
181/// let bytes_with_extra = &[0x60, 0x03, 0xE8, 0x00];
182/// let result = de_ushort(bytes_with_extra);
183/// assert_eq!(result, Some((1000, &[0x00_u8][..])));
184///
185/// // Failing to parse due to an incorrect prefix
186/// let invalid_bytes = &[0x50, 0x03, 0xE8];
187/// let result = de_ushort(invalid_bytes);
188/// assert_eq!(result, None);
189///
190/// // Failing to parse due to not enough bytes
191/// let incomplete_bytes = &[0x60, 0x03];
192/// let result = de_ushort(incomplete_bytes);
193/// assert_eq!(result, None);
194/// ```
195#[must_use]
196pub fn de_uint(b: &[u8]) -> Option<(UInt, &[u8])> {
197    Some(b)
198        .and_then(de_uint_uint0)
199        .or_else(|| de_uint_smalluint(b))
200        .or_else(|| de_uint_(b))
201}
202
203fn de_uint_uint0(b: &[u8]) -> Option<(UInt, &[u8])> {
204    Some(b)
205        .and_then(if_marker(0x43_u8))
206        .and_then(de_fixed_width_empty)
207        .map(|((), rest)| (0, rest))
208}
209
210fn de_uint_smalluint(b: &[u8]) -> Option<(UInt, &[u8])> {
211    Some(b)
212        .and_then(if_marker(0x52))
213        .and_then(de_fixed_width_one)
214        .map(|(first, rest)| (UInt::from(first[0]), rest))
215}
216
217fn de_uint_(b: &[u8]) -> Option<(UInt, &[u8])> {
218    Some(b)
219        .and_then(if_marker(0x70))
220        .and_then(de_fixed_width_four)
221        .map(|(data, rest)| {
222            (
223                UInt::from(u32::from_be_bytes([data[0], data[1], data[2], data[3]])),
224                rest,
225            )
226        })
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232
233    #[test]
234    fn test_zero_encoding() {
235        assert_eq!(ser_uint(0), UIntByteRepr::ZeroOctet([0x43]));
236    }
237
238    #[test]
239    fn test_single_octet_encoding() {
240        assert_eq!(ser_uint(127), UIntByteRepr::SingleOctet([0x52, 127]));
241        assert_eq!(ser_uint(1), UIntByteRepr::SingleOctet([0x52, 1]));
242    }
243
244    #[test]
245    fn test_four_octets_encoding() {
246        assert_eq!(
247            ser_uint(0x0102_0304),
248            UIntByteRepr::FourOctets([0x70, 0x01, 0x02, 0x03, 0x04])
249        );
250        assert_eq!(
251            ser_uint(0xFFFF_FFFF),
252            UIntByteRepr::FourOctets([0x70, 0xFF, 0xFF, 0xFF, 0xFF])
253        );
254    }
255
256    #[test]
257    fn test_zero_decoding() {
258        let input = [0x43];
259        assert_eq!(de_uint(&input), Some((0, &[][..])));
260    }
261
262    #[test]
263    fn test_single_octet_decoding() {
264        let input = [0x52, 0x7F];
265        assert_eq!(de_uint(&input), Some((127, &[][..])));
266
267        let input = [0x52, 0x7F, 0x00];
268        assert_eq!(de_uint(&input), Some((127, &[0x00][..])));
269    }
270
271    #[test]
272    fn test_four_octets_decoding() {
273        let input = [0x70, 0x01, 0x02, 0x03, 0x04];
274        assert_eq!(de_uint(&input), Some((0x0102_0304, &[][..])));
275
276        let input = [0x70, 0x01, 0x02, 0x03, 0x04, 0xFF];
277        assert_eq!(de_uint(&input), Some((0x0102_0304, &[0xFF][..])));
278    }
279
280    #[test]
281    fn test_invalid_decoding() {
282        let empty: [u8; 0] = [];
283        assert_eq!(de_uint(&empty), None);
284
285        let invalid_prefix = [0x00];
286        assert_eq!(de_uint(&invalid_prefix), None);
287
288        let incomplete = [0x70, 0x01, 0x02];
289        assert_eq!(de_uint(&incomplete), None);
290    }
291}