musli_wire/
wire_int.rs

1use musli::Context;
2use musli_utils::int::continuation as c;
3use musli_utils::int::zigzag as zig;
4use musli_utils::int::{Signed, Unsigned, UnsignedOps};
5use musli_utils::{Options, Reader, Writer};
6
7use crate::tag::{Kind, Tag, DATA_MASK};
8
9/// Governs how usize lengths are encoded into a [`Writer`].
10#[inline]
11pub(crate) fn encode_length<C, W, const OPT: Options>(
12    cx: &C,
13    mut writer: W,
14    value: usize,
15) -> Result<(), C::Error>
16where
17    C: ?Sized + Context,
18    W: Writer,
19{
20    match musli_utils::options::length::<OPT>() {
21        musli_utils::options::Integer::Variable => {
22            if value.is_smaller_than(DATA_MASK) {
23                writer.write_byte(cx, Tag::new(Kind::Continuation, value.as_byte()).byte())
24            } else {
25                writer.write_byte(cx, Tag::empty(Kind::Continuation).byte())?;
26                c::encode(cx, writer, value)
27            }
28        }
29        _ => {
30            let bo = musli_utils::options::byteorder::<OPT>();
31            let width = musli_utils::options::length_width::<OPT>();
32            let bytes = 1u8 << width as u8;
33            writer.write_byte(cx, Tag::new(Kind::Prefix, bytes).byte())?;
34
35            macro_rules! fixed {
36                ($ty:ty) => {{
37                    let Ok(value) = <$ty>::try_from(value) else {
38                        return Err(cx.message("Numerical value out of bounds for usize"));
39                    };
40
41                    value.write_bytes(cx, writer, bo)
42                }};
43            }
44
45            musli_utils::width_arm!(width, fixed)
46        }
47    }
48}
49
50/// Governs how usize lengths are decoded from a [`Reader`].
51#[inline]
52pub(crate) fn decode_length<'de, C, R, const OPT: Options>(
53    cx: &C,
54    mut reader: R,
55) -> Result<usize, C::Error>
56where
57    C: ?Sized + Context,
58    R: Reader<'de>,
59{
60    match musli_utils::options::length::<OPT>() {
61        musli_utils::options::Integer::Variable => {
62            let tag = Tag::from_byte(reader.read_byte(cx)?);
63
64            if tag.kind() != Kind::Continuation {
65                return Err(cx.message("Expected continuation"));
66            }
67
68            if let Some(data) = tag.data() {
69                Ok(usize::from_byte(data))
70            } else {
71                c::decode(cx, reader)
72            }
73        }
74        _ => {
75            let bo = musli_utils::options::byteorder::<OPT>();
76            let width = musli_utils::options::length_width::<OPT>();
77
78            let bytes = 1u8 << width as u8;
79            let tag = Tag::from_byte(reader.read_byte(cx)?);
80
81            if tag != Tag::new(Kind::Prefix, bytes) {
82                return Err(cx.message(format_args!(
83                    "Expected fixed {} bytes prefix tag, but got {tag:?}",
84                    bytes
85                )));
86            }
87
88            macro_rules! fixed {
89                ($ty:ty) => {{
90                    let Ok(value) = usize::try_from(<$ty>::read_bytes(cx, reader, bo)?) else {
91                        return Err(cx.message("Value type out of bounds for usize"));
92                    };
93
94                    Ok(value)
95                }};
96            }
97
98            musli_utils::width_arm!(width, fixed)
99        }
100    }
101}
102
103/// Governs how unsigned integers are encoded into a [`Writer`].
104#[inline]
105pub(crate) fn encode_unsigned<C, W, T, const OPT: Options>(
106    cx: &C,
107    mut writer: W,
108    value: T,
109) -> Result<(), C::Error>
110where
111    C: ?Sized + Context,
112    W: Writer,
113    T: UnsignedOps,
114{
115    match musli_utils::options::integer::<OPT>() {
116        musli_utils::options::Integer::Variable => {
117            if value.is_smaller_than(DATA_MASK) {
118                writer.write_byte(cx, Tag::new(Kind::Continuation, value.as_byte()).byte())
119            } else {
120                writer.write_byte(cx, Tag::empty(Kind::Continuation).byte())?;
121                c::encode(cx, writer, value)
122            }
123        }
124        _ => {
125            let bo = musli_utils::options::byteorder::<OPT>();
126            writer.write_byte(cx, Tag::new(Kind::Prefix, T::BYTES).byte())?;
127            value.write_bytes(cx, writer, bo)
128        }
129    }
130}
131
132/// Governs how unsigned integers are decoded from a [`Reader`].
133#[inline]
134pub(crate) fn decode_unsigned<'de, C, R, T, const OPT: Options>(
135    cx: &C,
136    mut reader: R,
137) -> Result<T, C::Error>
138where
139    C: ?Sized + Context,
140    R: Reader<'de>,
141    T: UnsignedOps,
142{
143    match musli_utils::options::integer::<OPT>() {
144        musli_utils::options::Integer::Variable => {
145            let tag = Tag::from_byte(reader.read_byte(cx)?);
146
147            if tag.kind() != Kind::Continuation {
148                return Err(cx.message("Expected continuation"));
149            }
150
151            if let Some(data) = tag.data() {
152                Ok(T::from_byte(data))
153            } else {
154                c::decode(cx, reader)
155            }
156        }
157        _ => {
158            let bo = musli_utils::options::byteorder::<OPT>();
159
160            if Tag::from_byte(reader.read_byte(cx)?) != Tag::new(Kind::Prefix, T::BYTES) {
161                return Err(cx.message("Expected fixed integer"));
162            }
163
164            T::read_bytes(cx, reader, bo)
165        }
166    }
167}
168
169/// Governs how signed integers are encoded into a [`Writer`].
170#[inline]
171pub(crate) fn encode_signed<C, W, T, const OPT: Options>(
172    cx: &C,
173    writer: W,
174    value: T,
175) -> Result<(), C::Error>
176where
177    C: ?Sized + Context,
178    W: Writer,
179    T: Signed,
180    T::Unsigned: UnsignedOps,
181{
182    let value = zig::encode(value);
183    encode_unsigned::<C, W, T::Unsigned, OPT>(cx, writer, value)
184}
185
186/// Governs how signed integers are decoded from a [`Reader`].
187#[inline]
188pub(crate) fn decode_signed<'de, C, R, T, const OPT: Options>(
189    cx: &C,
190    reader: R,
191) -> Result<T, C::Error>
192where
193    C: ?Sized + Context,
194    R: Reader<'de>,
195    T: Signed,
196    T::Unsigned: UnsignedOps,
197{
198    let value = decode_unsigned::<C, R, T::Unsigned, OPT>(cx, reader)?;
199    Ok(zig::decode(value))
200}