lilliput_core/decoder/
int.rs

1use core::num::TryFromIntError;
2
3use num_traits::{Signed, Unsigned};
4
5use crate::{
6    error::{Error, Result},
7    header::{CompactIntHeader, ExtendedIntHeader, IntHeader},
8    marker::Marker,
9    num::FromZigZag,
10    value::{IntValue, SignedIntValue, UnsignedIntValue},
11};
12
13use super::{Decoder, Read};
14
15impl<'de, R> Decoder<R>
16where
17    R: Read<'de>,
18{
19    // MARK: - Value
20
21    /// Decodes a 8-bit signed integer value.
22    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
23    pub fn decode_i8(&mut self) -> Result<i8> {
24        self.decode_signed_int()
25    }
26
27    /// Decodes a 16-bit signed integer value.
28    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
29    pub fn decode_i16(&mut self) -> Result<i16> {
30        self.decode_signed_int()
31    }
32
33    /// Decodes a 32-bit signed integer value.
34    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
35    pub fn decode_i32(&mut self) -> Result<i32> {
36        self.decode_signed_int()
37    }
38
39    /// Decodes a 64-bit signed integer value.
40    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
41    pub fn decode_i64(&mut self) -> Result<i64> {
42        self.decode_signed_int()
43    }
44
45    /// Decodes a 8-bit unsigned integer value.
46    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
47    pub fn decode_u8(&mut self) -> Result<u8> {
48        self.decode_unsigned_int()
49    }
50
51    /// Decodes a 16-bit unsigned integer value.
52    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
53    pub fn decode_u16(&mut self) -> Result<u16> {
54        self.decode_unsigned_int()
55    }
56
57    /// Decodes a 32-bit unsigned integer value.
58    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
59    pub fn decode_u32(&mut self) -> Result<u32> {
60        self.decode_unsigned_int()
61    }
62
63    /// Decodes a 64-bit unsigned integer value.
64    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
65    pub fn decode_u64(&mut self) -> Result<u64> {
66        self.decode_unsigned_int()
67    }
68
69    /// Decodes a signed integer value.
70    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
71    pub fn decode_signed_int<T>(&mut self) -> Result<T>
72    where
73        T: Signed + TryFrom<SignedIntValue, Error = TryFromIntError>,
74    {
75        let pos = self.pos;
76
77        self.decode_signed_int_value()?
78            .try_into()
79            .map_err(|_| Error::number_out_of_range(Some(pos)))
80    }
81
82    /// Decodes a unsigned integer value.
83    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
84    pub fn decode_unsigned_int<T>(&mut self) -> Result<T>
85    where
86        T: Unsigned + TryFrom<UnsignedIntValue, Error = TryFromIntError>,
87    {
88        let pos = self.pos;
89
90        self.decode_unsigned_int_value()?
91            .try_into()
92            .map_err(|_| Error::number_out_of_range(Some(pos)))
93    }
94
95    /// Decodes a signed integer value, as a `SignedIntValue`.
96    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
97    pub fn decode_signed_int_value(&mut self) -> Result<SignedIntValue> {
98        let pos = self.pos;
99
100        self.decode_int_value()?
101            .to_signed()
102            .map_err(|_| Error::number_out_of_range(Some(pos)))
103    }
104
105    /// Decodes a unsigned integer value, as a `UnsignedIntValue`.
106    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
107    pub fn decode_unsigned_int_value(&mut self) -> Result<UnsignedIntValue> {
108        let pos = self.pos;
109
110        self.decode_int_value()?
111            .to_unsigned()
112            .map_err(|_| Error::number_out_of_range(Some(pos)))
113    }
114
115    /// Decodes a integer value, as an `IntValue`.
116    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
117    pub fn decode_int_value(&mut self) -> Result<IntValue> {
118        let header = self.decode_int_header()?;
119        self.decode_int_value_of(header)
120    }
121
122    // MARK: - Header
123
124    /// Decodes a integer value's header.
125    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
126    pub fn decode_int_header(&mut self) -> Result<IntHeader> {
127        let byte = self.pull_byte_expecting(Marker::Int)?;
128
129        if (byte & IntHeader::COMPACT_VARIANT_BIT) != 0b0 {
130            let is_signed = (byte & IntHeader::SIGNEDNESS_BIT) != 0b0;
131            let bits = byte & IntHeader::COMPACT_VALUE_BITS;
132
133            #[cfg(feature = "tracing")]
134            tracing::debug!(
135                byte = crate::binary::fmt_byte(byte),
136                is_compact = true,
137                is_signed = is_signed,
138                bits = bits
139            );
140
141            Ok(IntHeader::Compact(CompactIntHeader { is_signed, bits }))
142        } else {
143            let is_signed = (byte & IntHeader::SIGNEDNESS_BIT) != 0b0;
144            let width = 1 + (byte & IntHeader::EXTENDED_WIDTH_BITS);
145
146            #[cfg(feature = "tracing")]
147            tracing::debug!(
148                byte = crate::binary::fmt_byte(byte),
149                is_compact = false,
150                is_signed = is_signed,
151                width = width
152            );
153
154            Ok(IntHeader::Extended(ExtendedIntHeader { is_signed, width }))
155        }
156    }
157
158    // MARK: - Skip
159
160    /// Skips the integer value for a given `header`.
161    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
162    pub fn skip_int_value_of(&mut self, header: IntHeader) -> Result<()> {
163        let header = match header {
164            IntHeader::Compact(_) => return Ok(()),
165            IntHeader::Extended(header) => header,
166        };
167
168        self.reader.skip(header.width().into())
169    }
170
171    // MARK: - Body
172
173    /// Decodes integer value for a given `header`, as an `IntValue`.
174    #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
175    pub fn decode_int_value_of(&mut self, header: IntHeader) -> Result<IntValue> {
176        let (is_signed, width): (bool, usize) = match header {
177            IntHeader::Compact(CompactIntHeader { is_signed, bits }) => {
178                if is_signed {
179                    let value = i8::from_zig_zag(bits);
180
181                    #[cfg(feature = "tracing")]
182                    tracing::debug!(value = value);
183
184                    return Ok(IntValue::Signed(SignedIntValue::I8(value)));
185                } else {
186                    let value = bits;
187
188                    #[cfg(feature = "tracing")]
189                    tracing::debug!(value = value);
190
191                    return Ok(IntValue::Unsigned(UnsignedIntValue::U8(value)));
192                }
193            }
194            IntHeader::Extended(ExtendedIntHeader { is_signed, width }) => {
195                (is_signed, width as usize)
196            }
197        };
198
199        match width {
200            1..=1 => {
201                const MAX_WIDTH: usize = 1;
202                let mut padded_be_bytes: [u8; MAX_WIDTH] = [0b0; MAX_WIDTH];
203                self.pull_bytes_into(&mut padded_be_bytes[(MAX_WIDTH - width)..])?;
204
205                #[cfg(feature = "tracing")]
206                let bytes = crate::binary::fmt_bytes(&padded_be_bytes[(MAX_WIDTH - width)..]);
207
208                let value = u8::from_be_bytes(padded_be_bytes);
209
210                if is_signed {
211                    let value = i8::from_zig_zag(value);
212
213                    #[cfg(feature = "tracing")]
214                    tracing::debug!(bytes = bytes, value = value);
215
216                    Ok(IntValue::Signed(SignedIntValue::I8(value)))
217                } else {
218                    #[cfg(feature = "tracing")]
219                    tracing::debug!(bytes = bytes, value = value);
220
221                    Ok(IntValue::Unsigned(UnsignedIntValue::U8(value)))
222                }
223            }
224            2..=2 => {
225                const MAX_WIDTH: usize = 2;
226                let mut padded_be_bytes: [u8; MAX_WIDTH] = [0b0; MAX_WIDTH];
227                self.pull_bytes_into(&mut padded_be_bytes[(MAX_WIDTH - width)..])?;
228
229                #[cfg(feature = "tracing")]
230                let bytes = crate::binary::fmt_bytes(&padded_be_bytes[(MAX_WIDTH - width)..]);
231
232                let value = u16::from_be_bytes(padded_be_bytes);
233
234                if is_signed {
235                    let value = i16::from_zig_zag(value);
236
237                    #[cfg(feature = "tracing")]
238                    tracing::debug!(bytes = bytes, value = value);
239
240                    Ok(IntValue::Signed(SignedIntValue::I16(value)))
241                } else {
242                    #[cfg(feature = "tracing")]
243                    tracing::debug!(bytes = bytes, value = value);
244
245                    Ok(IntValue::Unsigned(UnsignedIntValue::U16(value)))
246                }
247            }
248            3..=4 => {
249                const MAX_WIDTH: usize = 4;
250                let mut padded_be_bytes: [u8; MAX_WIDTH] = [0b0; MAX_WIDTH];
251                self.pull_bytes_into(&mut padded_be_bytes[(MAX_WIDTH - width)..])?;
252
253                #[cfg(feature = "tracing")]
254                let bytes = crate::binary::fmt_bytes(&padded_be_bytes[(MAX_WIDTH - width)..]);
255
256                let value = u32::from_be_bytes(padded_be_bytes);
257
258                if is_signed {
259                    let value = i32::from_zig_zag(value);
260
261                    #[cfg(feature = "tracing")]
262                    tracing::debug!(bytes = bytes, value = value);
263
264                    Ok(IntValue::Signed(SignedIntValue::I32(value)))
265                } else {
266                    #[cfg(feature = "tracing")]
267                    tracing::debug!(bytes = bytes, value = value);
268
269                    Ok(IntValue::Unsigned(UnsignedIntValue::U32(value)))
270                }
271            }
272            5..=8 => {
273                const MAX_WIDTH: usize = 8;
274                let mut padded_be_bytes: [u8; MAX_WIDTH] = [0b0; MAX_WIDTH];
275                self.pull_bytes_into(&mut padded_be_bytes[(MAX_WIDTH - width)..])?;
276
277                #[cfg(feature = "tracing")]
278                let bytes = crate::binary::fmt_bytes(&padded_be_bytes[(MAX_WIDTH - width)..]);
279
280                let value = u64::from_be_bytes(padded_be_bytes);
281
282                if is_signed {
283                    let value = i64::from_zig_zag(value);
284
285                    #[cfg(feature = "tracing")]
286                    tracing::debug!(bytes = bytes, value = value);
287
288                    Ok(IntValue::Signed(SignedIntValue::I64(value)))
289                } else {
290                    #[cfg(feature = "tracing")]
291                    tracing::debug!(bytes = bytes, value = value);
292
293                    Ok(IntValue::Unsigned(UnsignedIntValue::U64(value)))
294                }
295            }
296            _ => unreachable!(),
297        }
298    }
299}