musli_descriptive/
integer_encoding.rs

1use musli::Context;
2
3use musli_utils::int::continuation as c;
4use musli_utils::int::zigzag as zig;
5use musli_utils::int::{Signed, Unsigned};
6use musli_utils::{Reader, Writer};
7
8use crate::tag::{Kind, NumberKind, Tag};
9
10#[inline]
11pub(crate) fn encode_typed_unsigned<C, W, T>(
12    cx: &C,
13    writer: W,
14    bits: u8,
15    value: T,
16) -> Result<(), C::Error>
17where
18    C: ?Sized + Context,
19    W: Writer,
20    T: Unsigned,
21{
22    encode_typed(cx, writer, bits, value)
23}
24
25#[inline]
26pub(crate) fn decode_typed_unsigned<'de, C, R, T>(cx: &C, reader: R) -> Result<T, C::Error>
27where
28    C: ?Sized + Context,
29    R: Reader<'de>,
30    T: Unsigned + TryFrom<T::Signed>,
31{
32    let (value, kind): (T, NumberKind) = decode_typed(cx, reader)?;
33
34    match kind {
35        NumberKind::Signed => {
36            let value = zig::decode(value);
37
38            let Ok(value) = T::try_from(value) else {
39                return Err(cx.message(format_args!("Unsigned value outside of signed range")));
40            };
41
42            Ok(value)
43        }
44        NumberKind::Unsigned | NumberKind::Float => Ok(value),
45        kind => Err(cx.message(format_args!(
46            "Expected signed or unsigned number, got {:?}",
47            kind
48        ))),
49    }
50}
51
52#[inline]
53fn encode_typed<C, W, T>(cx: &C, mut writer: W, bits: u8, value: T) -> Result<(), C::Error>
54where
55    C: ?Sized + Context,
56    W: Writer,
57    T: Unsigned,
58{
59    writer.write_byte(cx, Tag::new(Kind::Number, bits).byte())?;
60    c::encode(cx, writer, value)
61}
62
63#[inline]
64fn decode_typed<'de, C, R, T>(cx: &C, mut reader: R) -> Result<(T, NumberKind), C::Error>
65where
66    C: ?Sized + Context,
67    R: Reader<'de>,
68    T: Unsigned,
69{
70    let tag = Tag::from_byte(reader.read_byte(cx)?);
71
72    if tag.kind() != Kind::Number {
73        return Err(cx.message(format_args!("Expected {:?}, got {tag:?}", Kind::Number)));
74    }
75
76    let kind = tag.number_kind();
77    Ok((c::decode(cx, reader)?, kind))
78}
79
80#[inline]
81pub(crate) fn encode_typed_signed<C, W, T>(
82    cx: &C,
83    writer: W,
84    bits: u8,
85    value: T,
86) -> Result<(), C::Error>
87where
88    C: ?Sized + Context,
89    W: Writer,
90    T: Signed,
91{
92    encode_typed(cx, writer, bits, zig::encode(value))
93}
94
95#[inline]
96pub(crate) fn decode_typed_signed<'de, C, R, T>(cx: &C, reader: R) -> Result<T, C::Error>
97where
98    C: ?Sized + Context,
99    R: Reader<'de>,
100    T: Signed + TryFrom<<T as Signed>::Unsigned>,
101{
102    let (value, kind): (T::Unsigned, NumberKind) = decode_typed(cx, reader)?;
103
104    match kind {
105        NumberKind::Signed => Ok(zig::decode(value)),
106        NumberKind::Unsigned => {
107            let Ok(value) = T::try_from(value) else {
108                return Err(cx.message(format_args!("Unsigned value outside of signed range")));
109            };
110
111            Ok(value)
112        }
113        kind => Err(cx.message(format_args!(
114            "Expected signed or unsigned number, got {:?}",
115            kind
116        ))),
117    }
118}