1use crate::encoding::{
2 reader::Reader,
3 tag::{AppTag, Tag},
4 writer::Writer,
5};
6use crate::{DecodeError, EncodeError};
7
8pub fn encode_unsigned(w: &mut Writer<'_>, value: u32) -> Result<usize, EncodeError> {
9 let len = if value <= 0xFF {
10 1
11 } else if value <= 0xFFFF {
12 2
13 } else if value <= 0xFF_FFFF {
14 3
15 } else {
16 4
17 };
18
19 for i in (0..len).rev() {
20 let b = ((value >> (i * 8)) & 0xFF) as u8;
21 w.write_u8(b)?;
22 }
23 Ok(len)
24}
25
26pub fn decode_unsigned(r: &mut Reader<'_>, len: usize) -> Result<u32, DecodeError> {
27 if len == 0 || len > 4 {
28 return Err(DecodeError::InvalidLength);
29 }
30 let mut value = 0u32;
31 for _ in 0..len {
32 value = (value << 8) | r.read_u8()? as u32;
33 }
34 Ok(value)
35}
36
37pub fn encode_signed(w: &mut Writer<'_>, value: i32) -> Result<usize, EncodeError> {
38 let value64 = value as i64;
39 let len = if (-128..=127).contains(&value64) {
40 1
41 } else if (-32768..=32767).contains(&value64) {
42 2
43 } else if (-8_388_608..=8_388_607).contains(&value64) {
44 3
45 } else {
46 4
47 };
48
49 let bytes = value.to_be_bytes();
50 w.write_all(&bytes[4 - len..])?;
51 Ok(len)
52}
53
54pub fn decode_signed(r: &mut Reader<'_>, len: usize) -> Result<i32, DecodeError> {
55 if len == 0 || len > 4 {
56 return Err(DecodeError::InvalidLength);
57 }
58
59 let bytes = r.read_exact(len)?;
60 let mut out = [0u8; 4];
61 out[4 - len..].copy_from_slice(bytes);
62 if (bytes[0] & 0x80) != 0 {
63 for b in &mut out[..4 - len] {
64 *b = 0xFF;
65 }
66 }
67 Ok(i32::from_be_bytes(out))
68}
69
70pub fn encode_app_unsigned(w: &mut Writer<'_>, value: u32) -> Result<(), EncodeError> {
71 let mut scratch = [0u8; 4];
72 let mut tw = Writer::new(&mut scratch);
73 let len = encode_unsigned(&mut tw, value)? as u32;
74 Tag::Application {
75 tag: AppTag::UnsignedInt,
76 len,
77 }
78 .encode(w)?;
79 w.write_all(&scratch[..len as usize])
80}
81
82pub fn encode_app_enumerated(w: &mut Writer<'_>, value: u32) -> Result<(), EncodeError> {
83 let mut scratch = [0u8; 4];
84 let mut tw = Writer::new(&mut scratch);
85 let len = encode_unsigned(&mut tw, value)? as u32;
86 Tag::Application {
87 tag: AppTag::Enumerated,
88 len,
89 }
90 .encode(w)?;
91 w.write_all(&scratch[..len as usize])
92}
93
94pub fn encode_app_object_id(w: &mut Writer<'_>, object_id_raw: u32) -> Result<(), EncodeError> {
95 Tag::Application {
96 tag: AppTag::ObjectId,
97 len: 4,
98 }
99 .encode(w)?;
100 w.write_be_u32(object_id_raw)
101}
102
103pub fn encode_app_signed(w: &mut Writer<'_>, value: i32) -> Result<(), EncodeError> {
104 let mut scratch = [0u8; 4];
105 let mut tw = Writer::new(&mut scratch);
106 let len = encode_signed(&mut tw, value)? as u32;
107 Tag::Application {
108 tag: AppTag::SignedInt,
109 len,
110 }
111 .encode(w)?;
112 w.write_all(&scratch[..len as usize])
113}
114
115pub fn decode_app_unsigned(r: &mut Reader<'_>) -> Result<u32, DecodeError> {
116 match Tag::decode(r)? {
117 Tag::Application {
118 tag: AppTag::UnsignedInt,
119 len,
120 } => decode_unsigned(r, len as usize),
121 _ => Err(DecodeError::InvalidTag),
122 }
123}
124
125pub fn decode_app_enumerated(r: &mut Reader<'_>) -> Result<u32, DecodeError> {
126 match Tag::decode(r)? {
127 Tag::Application {
128 tag: AppTag::Enumerated,
129 len,
130 } => decode_unsigned(r, len as usize),
131 _ => Err(DecodeError::InvalidTag),
132 }
133}
134
135pub fn decode_app_signed(r: &mut Reader<'_>) -> Result<i32, DecodeError> {
136 match Tag::decode(r)? {
137 Tag::Application {
138 tag: AppTag::SignedInt,
139 len,
140 } => decode_signed(r, len as usize),
141 _ => Err(DecodeError::InvalidTag),
142 }
143}
144
145pub fn encode_ctx_unsigned(w: &mut Writer<'_>, tag_num: u8, value: u32) -> Result<(), EncodeError> {
146 let mut scratch = [0u8; 4];
147 let mut tw = Writer::new(&mut scratch);
148 let len = encode_unsigned(&mut tw, value)? as u32;
149 Tag::Context { tag_num, len }.encode(w)?;
150 w.write_all(&scratch[..len as usize])
151}
152
153pub fn encode_ctx_object_id(
154 w: &mut Writer<'_>,
155 tag_num: u8,
156 object_id_raw: u32,
157) -> Result<(), EncodeError> {
158 Tag::Context { tag_num, len: 4 }.encode(w)?;
159 w.write_be_u32(object_id_raw)
160}
161
162pub fn encode_ctx_signed(w: &mut Writer<'_>, tag_num: u8, value: i32) -> Result<(), EncodeError> {
163 let mut scratch = [0u8; 4];
164 let mut tw = Writer::new(&mut scratch);
165 let len = encode_signed(&mut tw, value)? as u32;
166 Tag::Context { tag_num, len }.encode(w)?;
167 w.write_all(&scratch[..len as usize])
168}
169
170pub fn encode_ctx_character_string(
171 w: &mut Writer<'_>,
172 tag_num: u8,
173 value: &str,
174) -> Result<(), EncodeError> {
175 let bytes = value.as_bytes();
176 Tag::Context {
177 tag_num,
178 len: (bytes.len() + 1) as u32,
179 }
180 .encode(w)?;
181 w.write_u8(0)?;
182 w.write_all(bytes)
183}
184
185pub fn decode_ctx_character_string<'a>(
186 r: &mut Reader<'a>,
187 len: usize,
188) -> Result<&'a str, DecodeError> {
189 if len == 0 {
190 return Err(DecodeError::InvalidLength);
191 }
192 let raw = r.read_exact(len)?;
193 if raw[0] != 0 {
194 return Err(DecodeError::Unsupported);
195 }
196 core::str::from_utf8(&raw[1..]).map_err(|_| DecodeError::InvalidValue)
197}
198
199pub fn encode_opening_tag(w: &mut Writer<'_>, tag_num: u8) -> Result<(), EncodeError> {
200 Tag::Opening { tag_num }.encode(w)
201}
202
203pub fn encode_closing_tag(w: &mut Writer<'_>, tag_num: u8) -> Result<(), EncodeError> {
204 Tag::Closing { tag_num }.encode(w)
205}
206
207pub fn encode_app_real(w: &mut Writer<'_>, value: f32) -> Result<(), EncodeError> {
208 Tag::Application {
209 tag: AppTag::Real,
210 len: 4,
211 }
212 .encode(w)?;
213 w.write_all(&value.to_bits().to_be_bytes())
214}
215
216pub fn decode_app_real(r: &mut Reader<'_>) -> Result<f32, DecodeError> {
217 match Tag::decode(r)? {
218 Tag::Application {
219 tag: AppTag::Real,
220 len: 4,
221 } => {
222 let bytes = r.read_exact(4)?;
223 Ok(f32::from_bits(u32::from_be_bytes([
224 bytes[0], bytes[1], bytes[2], bytes[3],
225 ])))
226 }
227 _ => Err(DecodeError::InvalidTag),
228 }
229}
230
231#[cfg(test)]
232#[cfg(feature = "alloc")]
233mod tests {
234 use super::{
235 decode_app_unsigned, decode_ctx_character_string, decode_unsigned, encode_app_unsigned,
236 encode_ctx_character_string, encode_unsigned,
237 };
238 use crate::encoding::{reader::Reader, writer::Writer};
239 use alloc::format;
240 use proptest::prelude::*;
241
242 proptest! {
243 #[test]
244 fn unsigned_roundtrip(v in any::<u32>()) {
245 let mut b = [0u8; 8];
246 let mut w = Writer::new(&mut b);
247 let len = encode_unsigned(&mut w, v).unwrap();
248 let mut r = Reader::new(w.as_written());
249 let got = decode_unsigned(&mut r, len).unwrap();
250 prop_assert_eq!(got, v);
251 }
252
253 #[test]
254 fn app_unsigned_roundtrip(v in any::<u32>()) {
255 let mut b = [0u8; 16];
256 let mut w = Writer::new(&mut b);
257 encode_app_unsigned(&mut w, v).unwrap();
258 let mut r = Reader::new(w.as_written());
259 let got = decode_app_unsigned(&mut r).unwrap();
260 prop_assert_eq!(got, v);
261 }
262
263 #[test]
264 fn signed_roundtrip(v in any::<i32>()) {
265 let mut b = [0u8; 8];
266 let mut w = Writer::new(&mut b);
267 let len = super::encode_signed(&mut w, v).unwrap();
268 let mut r = Reader::new(w.as_written());
269 let got = super::decode_signed(&mut r, len).unwrap();
270 prop_assert_eq!(got, v);
271 }
272
273 #[test]
274 fn app_signed_roundtrip(v in any::<i32>()) {
275 let mut b = [0u8; 16];
276 let mut w = Writer::new(&mut b);
277 super::encode_app_signed(&mut w, v).unwrap();
278 let mut r = Reader::new(w.as_written());
279 let got = super::decode_app_signed(&mut r).unwrap();
280 prop_assert_eq!(got, v);
281 }
282 }
283
284 #[test]
285 fn ctx_character_string_roundtrip() {
286 let mut b = [0u8; 32];
287 let mut w = Writer::new(&mut b);
288 encode_ctx_character_string(&mut w, 2, "hello").unwrap();
289 let mut r = Reader::new(w.as_written());
290 match crate::encoding::tag::Tag::decode(&mut r).unwrap() {
291 crate::encoding::tag::Tag::Context { tag_num: 2, len } => {
292 let got = decode_ctx_character_string(&mut r, len as usize).unwrap();
293 assert_eq!(got, "hello");
294 }
295 other => panic!("unexpected tag: {other:?}"),
296 }
297 }
298}