Skip to main content

rustbac_core/encoding/
primitives.rs

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}