1use crate::encoding::{
2 primitives::{decode_signed, decode_unsigned, encode_signed, encode_unsigned},
3 reader::Reader,
4 tag::{AppTag, Tag},
5 writer::Writer,
6};
7use crate::types::{BitString, DataValue, Date, ObjectId, Time};
8use crate::{DecodeError, EncodeError};
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12#[cfg(feature = "alloc")]
13use alloc::vec::Vec;
14
15fn u32_len(len: usize) -> Result<u32, EncodeError> {
16 u32::try_from(len).map_err(|_| EncodeError::ValueOutOfRange)
17}
18
19pub fn encode_application_data_value(
20 w: &mut Writer<'_>,
21 value: &DataValue<'_>,
22) -> Result<(), EncodeError> {
23 match value {
24 DataValue::Null => Tag::Application {
25 tag: AppTag::Null,
26 len: 0,
27 }
28 .encode(w),
29 DataValue::Boolean(v) => Tag::Application {
30 tag: AppTag::Boolean,
31 len: if *v { 1 } else { 0 },
32 }
33 .encode(w),
34 DataValue::Unsigned(v) => encode_app_unsigned_like(w, AppTag::UnsignedInt, *v),
35 DataValue::Signed(v) => encode_app_signed_like(w, AppTag::SignedInt, *v),
36 DataValue::Real(v) => {
37 Tag::Application {
38 tag: AppTag::Real,
39 len: 4,
40 }
41 .encode(w)?;
42 w.write_all(&v.to_bits().to_be_bytes())
43 }
44 DataValue::Double(v) => {
45 Tag::Application {
46 tag: AppTag::Double,
47 len: 8,
48 }
49 .encode(w)?;
50 w.write_all(&v.to_bits().to_be_bytes())
51 }
52 DataValue::OctetString(v) => {
53 Tag::Application {
54 tag: AppTag::OctetString,
55 len: u32_len(v.len())?,
56 }
57 .encode(w)?;
58 w.write_all(v)
59 }
60 DataValue::CharacterString(v) => {
61 let bytes = v.as_bytes();
62 Tag::Application {
63 tag: AppTag::CharacterString,
64 len: u32_len(bytes.len().saturating_add(1))?,
65 }
66 .encode(w)?;
67 w.write_u8(0)?;
69 w.write_all(bytes)
70 }
71 DataValue::BitString(v) => {
72 if v.unused_bits > 7 {
73 return Err(EncodeError::ValueOutOfRange);
74 }
75 Tag::Application {
76 tag: AppTag::BitString,
77 len: u32_len(v.data.len().saturating_add(1))?,
78 }
79 .encode(w)?;
80 w.write_u8(v.unused_bits)?;
81 w.write_all(v.data)
82 }
83 DataValue::Enumerated(v) => encode_app_unsigned_like(w, AppTag::Enumerated, *v),
84 DataValue::Date(v) => {
85 Tag::Application {
86 tag: AppTag::Date,
87 len: 4,
88 }
89 .encode(w)?;
90 w.write_all(&[v.year_since_1900, v.month, v.day, v.weekday])
91 }
92 DataValue::Time(v) => {
93 Tag::Application {
94 tag: AppTag::Time,
95 len: 4,
96 }
97 .encode(w)?;
98 w.write_all(&[v.hour, v.minute, v.second, v.hundredths])
99 }
100 DataValue::ObjectId(v) => {
101 Tag::Application {
102 tag: AppTag::ObjectId,
103 len: 4,
104 }
105 .encode(w)?;
106 w.write_all(&v.raw().to_be_bytes())
107 }
108 #[cfg(feature = "alloc")]
109 DataValue::Constructed { tag_num, values } => {
110 Tag::Opening { tag_num: *tag_num }.encode(w)?;
111 for child in values {
112 encode_application_data_value(w, child)?;
113 }
114 Tag::Closing { tag_num: *tag_num }.encode(w)
115 }
116 }
117}
118
119pub fn decode_application_data_value<'a>(r: &mut Reader<'a>) -> Result<DataValue<'a>, DecodeError> {
120 let tag = Tag::decode(r)?;
121 decode_application_data_value_from_tag(r, tag)
122}
123
124pub fn decode_application_data_value_from_tag<'a>(
125 r: &mut Reader<'a>,
126 tag: Tag,
127) -> Result<DataValue<'a>, DecodeError> {
128 match tag {
129 Tag::Application {
130 tag: AppTag::Null, ..
131 } => Ok(DataValue::Null),
132 Tag::Application {
133 tag: AppTag::Boolean,
134 len,
135 } => Ok(DataValue::Boolean(len != 0)),
136 Tag::Application {
137 tag: AppTag::UnsignedInt,
138 len,
139 } => Ok(DataValue::Unsigned(decode_unsigned(r, len as usize)?)),
140 Tag::Application {
141 tag: AppTag::SignedInt,
142 len,
143 } => Ok(DataValue::Signed(decode_signed(r, len as usize)?)),
144 Tag::Application {
145 tag: AppTag::Real,
146 len: 4,
147 } => {
148 let b = r.read_exact(4)?;
149 Ok(DataValue::Real(f32::from_bits(u32::from_be_bytes([
150 b[0], b[1], b[2], b[3],
151 ]))))
152 }
153 Tag::Application {
154 tag: AppTag::Double,
155 len: 8,
156 } => {
157 let b = r.read_exact(8)?;
158 Ok(DataValue::Double(f64::from_bits(u64::from_be_bytes([
159 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
160 ]))))
161 }
162 Tag::Application {
163 tag: AppTag::OctetString,
164 len,
165 } => Ok(DataValue::OctetString(r.read_exact(len as usize)?)),
166 Tag::Application {
167 tag: AppTag::CharacterString,
168 len,
169 } => {
170 if len == 0 {
171 return Err(DecodeError::InvalidLength);
172 }
173 let raw = r.read_exact(len as usize)?;
174 let charset = raw[0];
175 if charset != 0 {
176 return Err(DecodeError::Unsupported);
177 }
178 let s = core::str::from_utf8(&raw[1..]).map_err(|_| DecodeError::InvalidValue)?;
179 Ok(DataValue::CharacterString(s))
180 }
181 Tag::Application {
182 tag: AppTag::BitString,
183 len,
184 } => {
185 if len == 0 {
186 return Err(DecodeError::InvalidLength);
187 }
188 let raw = r.read_exact(len as usize)?;
189 if raw[0] > 7 {
190 return Err(DecodeError::InvalidValue);
191 }
192 Ok(DataValue::BitString(BitString {
193 unused_bits: raw[0],
194 data: &raw[1..],
195 }))
196 }
197 Tag::Application {
198 tag: AppTag::Enumerated,
199 len,
200 } => Ok(DataValue::Enumerated(decode_unsigned(r, len as usize)?)),
201 Tag::Application {
202 tag: AppTag::Date,
203 len: 4,
204 } => {
205 let b = r.read_exact(4)?;
206 Ok(DataValue::Date(Date {
207 year_since_1900: b[0],
208 month: b[1],
209 day: b[2],
210 weekday: b[3],
211 }))
212 }
213 Tag::Application {
214 tag: AppTag::Time,
215 len: 4,
216 } => {
217 let b = r.read_exact(4)?;
218 Ok(DataValue::Time(Time {
219 hour: b[0],
220 minute: b[1],
221 second: b[2],
222 hundredths: b[3],
223 }))
224 }
225 Tag::Application {
226 tag: AppTag::ObjectId,
227 len: 4,
228 } => {
229 let b = r.read_exact(4)?;
230 Ok(DataValue::ObjectId(ObjectId::from_raw(u32::from_be_bytes(
231 [b[0], b[1], b[2], b[3]],
232 ))))
233 }
234 #[cfg(feature = "alloc")]
235 Tag::Opening { tag_num } => {
236 let mut children = Vec::new();
237 loop {
238 let child_tag = Tag::decode(r)?;
239 if child_tag == (Tag::Closing { tag_num }) {
240 break;
241 }
242 children.push(decode_application_data_value_from_tag(r, child_tag)?);
243 }
244 Ok(DataValue::Constructed {
245 tag_num,
246 values: children,
247 })
248 }
249 _ => Err(DecodeError::Unsupported),
250 }
251}
252
253fn encode_app_unsigned_like(
254 w: &mut Writer<'_>,
255 tag: AppTag,
256 value: u32,
257) -> Result<(), EncodeError> {
258 let mut scratch = [0u8; 4];
259 let mut tw = Writer::new(&mut scratch);
260 let len = encode_unsigned(&mut tw, value)? as u32;
261 Tag::Application { tag, len }.encode(w)?;
262 w.write_all(&scratch[..len as usize])
263}
264
265fn encode_app_signed_like(w: &mut Writer<'_>, tag: AppTag, value: i32) -> Result<(), EncodeError> {
266 let mut scratch = [0u8; 4];
267 let mut tw = Writer::new(&mut scratch);
268 let len = encode_signed(&mut tw, value)? as u32;
269 Tag::Application { tag, len }.encode(w)?;
270 w.write_all(&scratch[..len as usize])
271}
272
273#[cfg(test)]
274#[cfg(feature = "alloc")]
275mod tests {
276 use super::{decode_application_data_value, encode_application_data_value};
277 use crate::encoding::{reader::Reader, writer::Writer};
278 use crate::types::{BitString, DataValue, Date, ObjectId, ObjectType, Time};
279
280 #[test]
281 fn value_codec_roundtrip_supported_types() {
282 let values = [
283 DataValue::Null,
284 DataValue::Boolean(true),
285 DataValue::Unsigned(123),
286 DataValue::Signed(-123),
287 DataValue::Real(12.5),
288 DataValue::Double(42.25),
289 DataValue::OctetString(&[1, 2, 3]),
290 DataValue::CharacterString("hello"),
291 DataValue::BitString(BitString::new(1, &[0b1010_0000])),
292 DataValue::Enumerated(9),
293 DataValue::Date(Date {
294 year_since_1900: 124,
295 month: 2,
296 day: 3,
297 weekday: 6,
298 }),
299 DataValue::Time(Time {
300 hour: 1,
301 minute: 2,
302 second: 3,
303 hundredths: 4,
304 }),
305 DataValue::ObjectId(ObjectId::new(ObjectType::Device, 1)),
306 ];
307
308 for v in values {
309 let mut buf = [0u8; 64];
310 let mut w = Writer::new(&mut buf);
311 encode_application_data_value(&mut w, &v).unwrap();
312 let mut r = Reader::new(w.as_written());
313 let got = decode_application_data_value(&mut r).unwrap();
314 assert_eq!(got, v);
315 }
316 }
317
318 #[test]
319 fn value_codec_roundtrip_constructed() {
320 use alloc::vec;
321
322 let value = DataValue::Constructed {
323 tag_num: 2,
324 values: vec![
325 DataValue::Unsigned(42),
326 DataValue::CharacterString("test"),
327 DataValue::Constructed {
328 tag_num: 0,
329 values: vec![DataValue::Boolean(true), DataValue::Real(3.14)],
330 },
331 ],
332 };
333
334 let mut buf = [0u8; 128];
335 let mut w = Writer::new(&mut buf);
336 encode_application_data_value(&mut w, &value).unwrap();
337 let mut r = Reader::new(w.as_written());
338 let got = decode_application_data_value(&mut r).unwrap();
339 assert_eq!(got, value);
340 }
341}