1use bondrewd::Bitfields;
14use core::slice::SliceIndex;
15
16use super::encoded_message::EncodedMessage;
17
18mod encoded_value;
19
20use encoded_value::*;
21
22#[allow(missing_docs)]
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
27#[repr(u16)]
28pub enum CoapOptionName {
29 IfMatch = 1,
30 UriHost = 3,
31 Etag = 4,
32 IfNoneMatch = 5,
33 Observe = 6,
34 UriPort = 7,
35 LocationPath = 8,
36 UriPath = 11,
37 ContentFormat = 12,
38 MaxAge = 14,
39 UriQuery = 15,
40 Accept = 17,
41 LocationQuery = 20,
42 ProxyUri = 35,
43 ProxyScheme = 39,
44 Size1 = 60,
45}
46
47impl TryFrom<u16> for CoapOptionName {
48 type Error = Error;
49
50 fn try_from(val: u16) -> Result<Self, Self::Error> {
51 let option = match val {
52 1 => CoapOptionName::IfMatch,
53 3 => CoapOptionName::UriHost,
54 4 => CoapOptionName::Etag,
55 5 => CoapOptionName::IfNoneMatch,
56 6 => CoapOptionName::Observe,
57 7 => CoapOptionName::UriPort,
58 8 => CoapOptionName::LocationPath,
59 11 => CoapOptionName::UriPath,
60 12 => CoapOptionName::ContentFormat,
61 14 => CoapOptionName::MaxAge,
62 15 => CoapOptionName::UriQuery,
63 17 => CoapOptionName::Accept,
64 20 => CoapOptionName::LocationQuery,
65 35 => CoapOptionName::ProxyUri,
66 39 => CoapOptionName::ProxyScheme,
67 60 => CoapOptionName::Size1,
68 _ => return Err(Error::InvalidOption),
69 };
70
71 Ok(option)
72 }
73}
74
75#[allow(missing_docs)]
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum Error {
79 ParseDelta,
80 ParseLength,
81 OutOfBoundsIndexing,
82 InvalidOption,
83}
84
85#[derive(Bitfields, Debug, PartialEq, Eq)]
87#[bondrewd(enforce_bytes = 1)]
88struct OptionHeader {
89 #[bondrewd(bit_length = 4)]
90 pub delta: u8,
91 #[bondrewd(bit_length = 4)]
92 pub length: u8,
93}
94
95#[derive(Debug, Clone, Eq, PartialEq)]
97pub struct CoapOption<'value> {
98 pub name: CoapOptionName,
100 pub value: &'value [u8],
102}
103
104impl core::cmp::Ord for CoapOption<'_> {
105 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
106 self.name.cmp(&other.name)
107 }
108}
109
110impl core::cmp::PartialOrd for CoapOption<'_> {
111 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
112 Some(self.cmp(other))
113 }
114}
115
116pub fn sort_options_vec<const MAX_OPTION_COUNT: usize>(
118 mut options: heapless::Vec<CoapOption<'_>, MAX_OPTION_COUNT>,
119) -> heapless::Vec<CoapOption<'_>, MAX_OPTION_COUNT> {
120 for i in 0..options.len() {
121 for j in 0..options.len() - 1 - i {
122 if options[j] > options[j + 1] {
123 options.swap(j, j + 1);
124 }
125 }
126 }
127
128 options
129}
130
131pub fn encode_to_buf(buf: &mut [u8], options: &[CoapOption<'_>]) -> Result<usize, super::Error> {
133 let mut index = 0;
134 let mut previous_number = 0;
135
136 for option in options {
137 let delta = option.name as u16 - previous_number;
138 let length = option.value.len();
139
140 let delta = EncodedValue::encode(delta);
141 let length = EncodedValue::encode(length as u16);
142
143 let header = OptionHeader {
144 delta: delta.header_value(),
145 length: length.header_value(),
146 };
147
148 let header = header.into_bytes();
149
150 for x in header {
151 *buf.get_mut(index).ok_or(super::Error::OutOfMemory)? = x;
152 index += 1;
153 }
154
155 index += delta.write_into(&mut buf[index..]);
156 index += length.write_into(&mut buf[index..]);
157
158 for x in option.value {
159 *buf.get_mut(index).ok_or(super::Error::OutOfMemory)? = *x;
160 index += 1;
161 }
162 previous_number = option.name as u16;
163 }
164 Ok(index)
165}
166
167pub struct OptionIterator<'encmsg, 'msgbuf> {
169 message: &'encmsg EncodedMessage<'msgbuf>,
170 pub(crate) next_start_index: usize,
171 previous_number: u16,
172 already_errored: bool,
173}
174
175impl<'encmsg, 'msgbuf> OptionIterator<'encmsg, 'msgbuf> {
176 pub fn new(message: &'encmsg EncodedMessage<'msgbuf>, next_start_index: usize) -> Self {
179 Self {
180 message,
181 next_start_index,
182 previous_number: 0,
183 already_errored: false,
184 }
185 }
186
187 fn access<I>(&self, index: I) -> Result<&'msgbuf <I as SliceIndex<[u8]>>::Output, Error>
192 where
193 I: SliceIndex<[u8]>,
194 {
195 self.message
196 .data
197 .get(index)
198 .ok_or(Error::OutOfBoundsIndexing)
199 }
200
201 fn try_next(&mut self) -> Result<CoapOption<'msgbuf>, Error> {
207 let start_index = self.next_start_index;
208
209 let mut header = [0_u8; OptionHeader::BYTE_SIZE];
210 header.clone_from_slice(self.access(start_index..start_index + OptionHeader::BYTE_SIZE)?);
211 let header = OptionHeader::from_bytes(header);
212
213 let OptionHeader { delta, length } = header;
214
215 let start_index = start_index + OptionHeader::BYTE_SIZE;
216
217 let (delta, length_offset) = if delta <= 12 {
218 (delta as u16, 0)
219 } else if delta == 13 {
220 (*self.access(start_index)? as u16 + 13, 1)
221 } else if delta == 14 {
222 let mut buf = [0_u8; 2];
223 buf.clone_from_slice(self.access(start_index..start_index + 2)?);
224 (u16::from_be_bytes(buf) + 269, 2)
225 } else {
226 return Err(Error::ParseDelta);
228 };
229
230 let start_index = start_index + length_offset;
231 let (length, value_offset) = if length <= 12 {
232 (length as u16, 0)
233 } else if length == 13 {
234 (*self.access(start_index)? as u16 + 13, 1)
235 } else if length == 14 {
236 let mut buf = [0_u8; 2];
237 buf.clone_from_slice(self.access(start_index..start_index + 2)?);
238 (u16::from_be_bytes(buf) + 269, 2)
239 } else {
240 return Err(Error::ParseLength);
242 };
243
244 self.previous_number += delta;
245
246 let start_index = start_index + value_offset;
247
248 let value = self.access(start_index..start_index + length as usize)?;
249
250 self.next_start_index = start_index + length as usize;
253
254 Ok(CoapOption {
255 name: self.previous_number.try_into()?,
256 value,
257 })
258 }
259}
260
261impl<'encmsg, 'msgbuf> Iterator for OptionIterator<'encmsg, 'msgbuf> {
262 type Item = Result<CoapOption<'msgbuf>, Error>;
263
264 fn next(&mut self) -> Option<Self::Item> {
265 let next_byte = match self.message.data.get(self.next_start_index) {
268 Some(b) => *b,
269 None => {
270 self.message.set_payload_offset(self.next_start_index);
271 return None;
272 }
273 };
274 if next_byte == 0xFF {
276 self.message.set_payload_offset(self.next_start_index);
277 return None;
278 }
279
280 match self.try_next() {
284 Ok(o) => Some(Ok(o)),
285 Err(_) if self.already_errored => None,
286 Err(e) => {
287 self.already_errored = true;
288 Some(Err(e))
289 }
290 }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297 use crate::message::encoded_message::EncodedMessage;
298
299 #[test]
300 fn empty() {
301 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0xFF]).unwrap();
302 let mut iter = OptionIterator::new(&msg, 4);
303 assert!(iter.next().is_none());
304 }
305
306 #[test]
307 fn one_option() {
308 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0x41, 0x11]).unwrap();
309 let mut iter = OptionIterator::new(&msg, 4);
310 let opt = iter.next().unwrap().expect("one option expected");
311 assert_eq!(opt.name, CoapOptionName::Etag);
312 assert_eq!(opt.value.len(), 1);
313 assert_eq!(opt.value[0], 0x11);
314 assert!(iter.next().is_none());
315
316 let msg: EncodedMessage<'_> =
317 EncodedMessage::try_new(&[0, 0, 0, 0, 0x41, 0x11, 0xFF, 0x12, 0x34]).unwrap();
318 let mut iter = OptionIterator::new(&msg, 4);
319 let opt = iter.next().unwrap().expect("one option expected");
320 assert_eq!(opt.name, CoapOptionName::Etag);
321 assert_eq!(opt.value.len(), 1);
322 assert_eq!(opt.value[0], 0x11);
323 assert!(iter.next().is_none());
324 }
325
326 #[test]
327 fn two_options() {
328 let msg: EncodedMessage<'_> =
329 EncodedMessage::try_new(&[0, 0, 0, 0, 0x41, 0x11, 0x12, 0x11, 0x22]).unwrap();
330 let mut iter = OptionIterator::new(&msg, 4);
331 let opt = iter.next().unwrap().expect("one option expected");
332 assert_eq!(opt.name, CoapOptionName::Etag);
333 assert_eq!(opt.value.len(), 1);
334 assert_eq!(opt.value[0], 0x11);
335 let opt = iter.next().unwrap().expect("another option expected");
336 assert_eq!(opt.name, CoapOptionName::IfNoneMatch);
337 assert_eq!(opt.value.len(), 2);
338 assert_eq!(&opt.value[..], &[0x11, 0x22]);
339 assert!(iter.next().is_none());
340 }
341
342 #[test]
343 fn extended_delta_and_length() {
344 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0xD0, 0x04]).unwrap();
345 let mut iter = OptionIterator::new(&msg, 4);
346 let opt = iter.next().unwrap().expect("one option expected");
347 assert_eq!(opt.name, CoapOptionName::Accept);
348
349 let msg: EncodedMessage<'_> =
350 EncodedMessage::try_new(&[0, 0, 0, 0, 0xE0, 0x00, 0x04]).unwrap();
351 let mut iter = OptionIterator::new(&msg, 4);
352 let opt = iter.next().unwrap();
353 assert!(matches!(opt, Err(Error::InvalidOption)));
354
355 let mut data = [0; 30];
356 data[0..2].copy_from_slice(&[0xBD, 0x04]);
357 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&data).unwrap();
358 let mut iter = OptionIterator::new(&msg, 0);
359 let opt = iter.next().unwrap().expect("one option expected");
360 assert_eq!(opt.value.len(), 17);
361
362 let mut data = [0; 300];
363 data[0..3].copy_from_slice(&[0xBE, 0x00, 0x04]);
364 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&data).unwrap();
365 let mut iter = OptionIterator::new(&msg, 0);
366 let opt = iter.next().unwrap().expect("one option expected");
367 assert_eq!(opt.value.len(), 273);
368
369 let mut data = [0; 30];
370 data[0..3].copy_from_slice(&[0xDD, 0x2F, 0x04]);
371 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&data).unwrap();
372 let mut iter = OptionIterator::new(&msg, 0);
373 let opt = iter.next().unwrap().expect("one option expected");
374 assert!(matches!(opt.name, CoapOptionName::Size1));
375 assert_eq!(opt.value.len(), 17);
376 }
377
378 #[test]
379 fn missing_bytes() {
380 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0x41]).unwrap();
382 let mut iter = OptionIterator::new(&msg, 4);
383 assert!(iter.next().unwrap().is_err());
384 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0xD0]).unwrap();
387 let mut iter = OptionIterator::new(&msg, 4);
388 assert!(iter.next().unwrap().is_err());
389 }
390
391 #[test]
392 fn wrong_delta_or_length() {
393 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0xF1]).unwrap();
394 let mut iter = OptionIterator::new(&msg, 4);
395 assert!(iter.next().unwrap().is_err());
396
397 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0x4F]).unwrap();
398 let mut iter = OptionIterator::new(&msg, 4);
399 assert!(iter.next().unwrap().is_err());
400 }
401
402 #[test]
403 fn next_after_err_is_none() {
404 let msg: EncodedMessage<'_> = EncodedMessage::try_new(&[0, 0, 0, 0, 0xF1]).unwrap();
405 let mut iter = OptionIterator::new(&msg, 4);
406 assert!(iter.next().unwrap().is_err());
407 assert!(iter.next().is_none());
408 }
409
410 #[test]
411 fn sort_options() {
412 use CoapOptionName::*;
413 sorted_options(&[IfMatch, UriHost, Etag, IfNoneMatch]).unwrap();
421 sorted_options(&[IfMatch, IfMatch, IfMatch, IfMatch]).unwrap();
422 sorted_options(&[IfNoneMatch, Etag, UriHost, IfMatch]).unwrap();
423 sorted_options(&[IfMatch, UriHost, Etag, Etag]).unwrap();
424 sorted_options(&[IfMatch, UriHost, IfNoneMatch, Etag]).unwrap();
425 sorted_options(&[UriHost, IfMatch, IfNoneMatch, Etag]).unwrap();
426 }
427 fn sorted_options(option_names: &[CoapOptionName]) -> Result<(), &'static str> {
428 let mut options = heapless::Vec::new();
429 let values = b"abcdefghijklmnopqrstuvw";
431 for (i, name) in option_names.iter().enumerate() {
432 options
433 .push(CoapOption {
434 name: *name,
435 value: &values[i..i + 1],
436 })
437 .unwrap();
438 }
439 let options = sort_options_vec(options);
440 is_sorted(options)
441 }
442 fn is_sorted(options: heapless::Vec<CoapOption<'_>, 32>) -> Result<(), &'static str> {
443 for items in options.windows(2) {
444 if items[0] > items[1] {
445 return Err("not sorted");
446 }
447 if items[0].cmp(&items[1]) == std::cmp::Ordering::Equal
448 && items[0].value[0] >= items[1].value[0]
449 {
450 return Err("not stable");
451 }
452 }
453 Ok(())
454 }
455}