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}