toad_msg/msg/opt/
mod.rs

1use core::hash::Hash;
2use core::iter::FromIterator;
3use core::marker::PhantomData;
4use core::ops::{Add, Sub};
5
6#[cfg(feature = "alloc")]
7use std_alloc::vec::Vec;
8use tinyvec::ArrayVec;
9use toad_array::{AppendCopy, Array, Indexed};
10use toad_cursor::Cursor;
11use toad_len::Len;
12use toad_macros::rfc_7252_doc;
13use toad_map::Map;
14
15use crate::from_bytes::*;
16
17/// Option parsing error
18pub mod parse_error;
19pub use parse_error::*;
20
21/// Well-known options
22pub mod known;
23pub use known::*;
24
25use self::no_repeat::{BLOCK1, BLOCK2};
26
27/// An iterator over owned [`Opt`]s
28#[derive(Debug, Clone)]
29pub struct OptIter<M, I>
30  where M: OptionMap
31{
32  iter: I,
33  last_seen_num: OptNumber,
34  repeated: Option<(OptNumber, M::OptValues)>,
35  __p: PhantomData<M>,
36}
37
38/// An iterator over [`OptRef`]s
39#[derive(Debug, Clone)]
40pub struct OptRefIter<'a, M, I>
41  where M: OptionMap
42{
43  iter: I,
44  last_seen_num: OptNumber,
45  repeated: Option<(OptNumber, &'a M::OptValues, usize)>,
46  __p: PhantomData<M>,
47}
48
49impl<M, I> Iterator for OptIter<M, I>
50  where I: Iterator<Item = (OptNumber, M::OptValues)>,
51        M: OptionMap
52{
53  type Item = Opt<M::OptValue>;
54
55  fn next(&mut self) -> Option<Self::Item> {
56    let (num, values) = Option::take(&mut self.repeated).or_else(|| self.iter.next())?;
57
58    match values.len() {
59      | 1 => {
60        let OptNumber(delta) = num - self.last_seen_num;
61        let delta = OptDelta(delta as u16);
62        self.last_seen_num = num;
63
64        Some(Opt { value: values.into_iter().next().unwrap(),
65                   delta })
66      },
67      | _ => {
68        let mut values = values.into_iter();
69        if let Some(value) = values.next() {
70          self.repeated = Some((num, values.collect()));
71
72          let OptNumber(delta) = num - self.last_seen_num;
73          let delta = OptDelta(delta as u16);
74          self.last_seen_num = num;
75
76          Some(Opt { value, delta })
77        } else {
78          self.repeated = None;
79          self.next()
80        }
81      },
82    }
83  }
84}
85
86impl<'a, M, I> Iterator for OptRefIter<'a, M, I>
87  where I: Iterator<Item = (&'a OptNumber, &'a M::OptValues)>,
88        Self: 'a,
89        M: 'a + OptionMap
90{
91  type Item = OptRef<'a, M::OptValue>;
92
93  fn next(&mut self) -> Option<Self::Item> {
94    let (num, values, ix) = self.repeated
95                                .or_else(|| self.iter.next().map(|(a, b)| (*a, b, 0)))?;
96
97    match values.len() {
98      | 1 => {
99        let OptNumber(delta) = num - self.last_seen_num;
100        let delta = OptDelta(delta as u16);
101        self.last_seen_num = num;
102
103        Some(OptRef { value: &values[0],
104                      delta })
105      },
106      | _ => {
107        if let Some(value) = values.get(ix) {
108          self.repeated = Some((num, values, ix + 1));
109
110          let OptNumber(delta) = num - self.last_seen_num;
111          let delta = OptDelta(delta as u16);
112          self.last_seen_num = num;
113
114          Some(OptRef { value, delta })
115        } else {
116          self.repeated = None;
117          self.next()
118        }
119      },
120    }
121  }
122}
123
124/// Generalization of `HashMap<OptNumber, OptValue<Vec<u8>>>`
125pub trait OptionMap
126  where Self: Map<OptNumber, Self::OptValues>
127{
128  /// Byte array for option values
129  type OptValue: Array<Item = u8> + AppendCopy<u8>;
130
131  /// One or more values for a given number.
132  ///
133  /// Note that not all options are repeatable.
134  type OptValues: Array<Item = OptValue<Self::OptValue>>;
135
136  /// Iterate over the map, yielding raw option structures
137  fn opts(self) -> OptIter<Self, Self::IntoIter> {
138    OptIter { iter: self.into_iter(),
139              last_seen_num: OptNumber(0),
140              __p: PhantomData,
141              repeated: None }
142  }
143
144  /// Iterate over the map, yielding raw option structures
145  fn opt_refs(&self) -> OptRefIter<'_, Self, toad_map::Iter<'_, OptNumber, Self::OptValues>> {
146    OptRefIter { iter: self.iter(),
147                 last_seen_num: OptNumber(0),
148                 __p: PhantomData,
149                 repeated: None }
150  }
151}
152
153#[cfg(feature = "alloc")]
154impl OptionMap for std_alloc::collections::BTreeMap<OptNumber, Vec<OptValue<Vec<u8>>>> {
155  type OptValue = Vec<u8>;
156  type OptValues = Vec<OptValue<Vec<u8>>>;
157}
158
159type ArrayVecMap<const N: usize, K, V> = ArrayVec<[(K, V); N]>;
160
161impl<const MAX_OPTS: usize, const MAX_INSTANCES: usize, const MAX_BYTES_PER_INSTANCE: usize>
162  OptionMap
163  for ArrayVecMap<MAX_OPTS,
164                  OptNumber,
165                  ArrayVec<[OptValue<ArrayVec<[u8; MAX_BYTES_PER_INSTANCE]>>; MAX_INSTANCES]>>
166{
167  type OptValue = ArrayVec<[u8; MAX_BYTES_PER_INSTANCE]>;
168  type OptValues = ArrayVec<[OptValue<Self::OptValue>; MAX_INSTANCES]>;
169}
170
171impl<B: AsRef<[u8]>, M: OptionMap> TryConsumeBytes<B> for M {
172  type Error = OptParseError;
173
174  fn try_consume_bytes(bytes: &mut Cursor<B>) -> Result<Self, Self::Error> {
175    let mut map = Self::default();
176
177    let mut last_inserted = OptNumber(0);
178
179    loop {
180      match Opt::try_consume_bytes(bytes) {
181        | Ok(opt) => {
182          if map.is_full() {
183            break Err(Self::Error::TooManyOptions(map.len()));
184          }
185
186          let OptDelta(d) = opt.delta;
187          let num = last_inserted + OptNumber(d as u32);
188
189          let mut values = M::OptValues::default();
190          values.push(opt.value);
191
192          map.insert(num, values).ok();
193          last_inserted = num;
194        },
195        | Err(OptParseError::OptionsExhausted) => break Ok(map),
196        | Err(e) => break Err(e),
197      }
198    }
199  }
200}
201
202pub(crate) fn parse_opt_len_or_delta<A: AsRef<[u8]>>(head: u8,
203                                                     bytes: &mut Cursor<A>,
204                                                     reserved_err: OptParseError)
205                                                     -> Result<u16, OptParseError> {
206  match head {
207    | 13 => {
208      let n = bytes.next().ok_or_else(OptParseError::eof)?;
209      Ok((n as u16) + 13)
210    },
211    | 14 => match bytes.take_exact(2) {
212      | Some(&[a, b]) => Ok(u16::from_be_bytes([a, b]) + 269),
213      | _ => Err(OptParseError::eof()),
214    },
215    | 15 => Err(reserved_err),
216    | _ => Ok(head as u16),
217  }
218}
219
220#[doc = rfc_7252_doc!("5.4")]
221/// <details><summary><b>RFC7252 Section 3.1 Option binary format</b></summary>
222#[doc = concat!("\n#", rfc_7252_doc!("3.1"))]
223/// </details>
224///
225/// # `Opt` struct
226/// Low-level representation of a CoAP Option, closely mirroring the byte layout
227/// of message options.
228///
229/// Notably, this doesn't include the Number (key, e.g. "Content-Format" or "Uri-Path").
230/// To refer to numbers we use implementors of the [`OptionMap`] trait.
231#[derive(Clone, Debug, Default)]
232pub struct Opt<C> {
233  /// See [`OptDelta`]
234  pub delta: OptDelta,
235  /// See [`OptValue`]
236  pub value: OptValue<C>,
237}
238
239impl<C> PartialOrd for Opt<C> where C: Array<Item = u8>
240{
241  fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
242    Some(self.cmp(other))
243  }
244}
245
246impl<C> PartialEq for Opt<C> where C: Array<Item = u8>
247{
248  fn eq(&self, other: &Self) -> bool {
249    self.delta.eq(&other.delta) && self.value.eq(&other.value)
250  }
251}
252
253impl<C> Ord for Opt<C> where C: Array<Item = u8>
254{
255  fn cmp(&self, other: &Self) -> core::cmp::Ordering {
256    self.delta
257        .cmp(&other.delta)
258        .then_with(|| self.value.cmp(&other.value))
259  }
260}
261
262impl<C> Eq for Opt<C> where C: Array<Item = u8> {}
263
264/// A low-cost copyable [`Opt`] that stores a reference to the value
265#[derive(Copy, Clone, Debug)]
266#[allow(missing_docs)]
267pub struct OptRef<'a, C> {
268  pub delta: OptDelta,
269  pub value: &'a OptValue<C>,
270}
271
272impl<'a, C> PartialOrd for OptRef<'a, C> where C: Array<Item = u8>
273{
274  fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
275    Some(self.cmp(other))
276  }
277}
278
279impl<'a, C> PartialEq for OptRef<'a, C> where C: Array<Item = u8>
280{
281  fn eq(&self, other: &Self) -> bool {
282    self.delta.eq(&other.delta) && self.value.eq(other.value)
283  }
284}
285
286impl<'a, C> Ord for OptRef<'a, C> where C: Array<Item = u8>
287{
288  fn cmp(&self, other: &Self) -> core::cmp::Ordering {
289    self.delta
290        .cmp(&other.delta)
291        .then_with(|| self.value.cmp(other.value))
292  }
293}
294
295impl<'a, C> Eq for OptRef<'a, C> where C: Array<Item = u8> {}
296
297impl<'a, C: Array<Item = u8>> Len for OptRef<'a, C> {
298  const CAPACITY: Option<usize> = None;
299
300  fn len(&self) -> usize {
301    let header_size = 1;
302    let delta_size = match self.delta.0 {
303      | n if n >= 269 => 2,
304      | n if n >= 13 => 1,
305      | _ => 0,
306    };
307
308    let value_len_size = match self.value.0.len() {
309      | n if n >= 269 => 2,
310      | n if n >= 13 => 1,
311      | _ => 0,
312    };
313
314    header_size + delta_size + value_len_size + self.value.0.len()
315  }
316
317  fn is_full(&self) -> bool {
318    false
319  }
320}
321
322impl<'a, V> From<&'a Opt<V>> for OptRef<'a, V> {
323  fn from(o: &'a Opt<V>) -> Self {
324    Self { value: &o.value,
325           delta: o.delta }
326  }
327}
328
329impl<C: Array<Item = u8>> Opt<C> {
330  /// Given a collection to [`Extend`] and an Opt, add that Opt's bytes to the collection.
331  pub fn extend_bytes(self, bytes: &mut impl Extend<u8>) {
332    let (del, del_bytes) = crate::to_bytes::opt_len_or_delta(self.delta.0);
333    let (len, len_bytes) = crate::to_bytes::opt_len_or_delta(self.value.0.len() as u16);
334    let del = del << 4;
335
336    let header = del | len;
337
338    bytes.extend(Some(header));
339
340    if let Some(bs) = del_bytes {
341      bytes.extend(bs);
342    }
343
344    if let Some(bs) = len_bytes {
345      bytes.extend(bs);
346    }
347
348    bytes.extend(self.value.0);
349  }
350}
351
352/// The "Option Delta" is the difference between this Option's Number
353/// and the previous Option's number.
354///
355/// This is just used to compute the Option Number, identifying which
356/// Option is being set (e.g. Content-Format has a Number of 12)
357///
358/// # Related
359/// - [RFC7252#section-3.1 Option Format](https://datatracker.ietf.org/doc/html/rfc7252#section-3.1)
360#[derive(Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord, Debug, Default)]
361pub struct OptDelta(pub u16);
362
363#[doc = rfc_7252_doc!("5.4.6")]
364/// <details><summary><b>RFC7252 Section 12.2 Core CoAP Option Numbers</b></summary>
365#[doc = concat!("\n#", rfc_7252_doc!("12.2"))]
366/// </details>
367#[derive(Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord, Debug, Default)]
368pub struct OptNumber(pub u32);
369
370impl Add for OptNumber {
371  type Output = OptNumber;
372
373  fn add(self, rhs: Self) -> Self::Output {
374    Self(self.0 + rhs.0)
375  }
376}
377
378impl Sub for OptNumber {
379  type Output = OptNumber;
380
381  fn sub(self, rhs: Self) -> Self::Output {
382    Self(self.0 - rhs.0)
383  }
384}
385
386#[doc = rfc_7252_doc!("5.4.1")]
387#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
388pub enum OptionMustBeProcessed {
389  /// This option must be processed,
390  /// and a response that ignores it
391  /// will be rejected.
392  ///
393  /// Corresponds to the option being "critical"
394  /// in strict CoAP terms
395  Yes,
396  /// This option does not _need_ to
397  /// be processed,
398  /// and a response that ignores it
399  /// will be processed anyway.
400  ///
401  /// Corresponds to the option being "elective"
402  /// in strict CoAP terms
403  No,
404}
405
406#[doc = rfc_7252_doc!("5.4.2")]
407#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
408pub enum WhenOptionUnsupportedByProxy {
409  /// This option /must be/ processed & understood by proxies
410  /// and may not be forwarded blindly to their destination.
411  ///
412  /// Corresponds to the option being "UnSafe" to forward
413  /// in strict CoAP terms
414  Error,
415  /// This option may not be processed & understood by proxies
416  /// and may be forwarded blindly to their destination.
417  ///
418  /// Corresponds to the option being "SafeToForward"
419  /// in strict CoAP terms
420  Forward,
421}
422
423#[doc = rfc_7252_doc!("5.4.2")]
424#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
425pub enum WhenOptionChanges {
426  /// If this option is [safe to forward](`WhenOptionUnsupportedByProxy::Forward`),
427  /// but unknown to a proxy, it should be included in the proxy's
428  /// cache key for this message.
429  ///
430  /// Corresponds to the option being not "NoCacheKey"
431  /// in strict CoAP terms
432  ResponseChanges,
433  /// If this option is [safe to forward](`WhenOptionUnsupportedByProxy::Forward`),
434  /// but unknown to a proxy, it should not be included in the proxy's
435  /// cache key for this message, and different values for this option
436  /// should yield the cached response.
437  ///
438  /// Corresponds to the option being "NoCacheKey"
439  /// in strict CoAP terms
440  ResponseDoesNotChange,
441}
442
443impl OptNumber {
444  /// Whether or not this option may be ignored by a server
445  pub fn must_be_processed(&self) -> OptionMustBeProcessed {
446    #[allow(clippy::wildcard_in_or_patterns)] // will only ever be 0 or 1
447    match self.0 & 0b1 {
448      | 1 => OptionMustBeProcessed::Yes,
449      | 0 | _ => OptionMustBeProcessed::No,
450    }
451  }
452
453  /// Whether or not this option may be forwarded blindly by
454  /// a proxy that does not support processing it
455  pub fn when_unsupported_by_proxy(&self) -> WhenOptionUnsupportedByProxy {
456    #[allow(clippy::wildcard_in_or_patterns)] // will only ever be 0 or 1
457    match (self.0 & 0b10) >> 1 {
458      | 1 => WhenOptionUnsupportedByProxy::Error,
459      | 0 | _ => WhenOptionUnsupportedByProxy::Forward,
460    }
461  }
462
463  /// Whether or not different values for this option should
464  /// yield proxies' cached response
465  ///
466  /// _(when the proxy does not support processing it and
467  /// the option is safe to forward)_
468  pub fn when_option_changes(&self) -> WhenOptionChanges {
469    match (self.0 & 0b11100) >> 2 {
470      | 0b111 => WhenOptionChanges::ResponseDoesNotChange,
471      | _ => WhenOptionChanges::ResponseChanges,
472    }
473  }
474
475  /// Whether this option should be included in the [`Message::cache_key`]
476  pub fn include_in_cache_key(&self) -> bool {
477    self.when_option_changes() == WhenOptionChanges::ResponseChanges
478    && self != &BLOCK1
479    && self != &BLOCK2
480  }
481}
482
483#[doc = rfc_7252_doc!("3.2")]
484#[derive(Default, Clone, Debug)]
485pub struct OptValue<C>(pub C);
486
487impl<C> PartialOrd for OptValue<C> where C: Array<Item = u8>
488{
489  fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
490    self.0.iter().partial_cmp(other.0.iter())
491  }
492}
493
494impl<C> PartialEq for OptValue<C> where C: Array<Item = u8>
495{
496  fn eq(&self, other: &Self) -> bool {
497    self.0.iter().eq(other.0.iter())
498  }
499}
500
501impl<C> Ord for OptValue<C> where C: Array<Item = u8>
502{
503  fn cmp(&self, other: &Self) -> core::cmp::Ordering {
504    self.0.iter().cmp(other.0.iter())
505  }
506}
507
508impl<C> Eq for OptValue<C> where C: Array<Item = u8> {}
509
510impl<C> Hash for OptValue<C> where C: Array<Item = u8>
511{
512  fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
513    state.write(&self.0)
514  }
515}
516
517impl<C> OptValue<C> where C: Array<Item = u8>
518{
519  /// Convert a reference to a OptValue to a byte slice
520  pub fn as_bytes(&self) -> &[u8] {
521    &self.0
522  }
523}
524
525impl<C> FromIterator<u8> for OptValue<C> where C: FromIterator<u8>
526{
527  fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
528    Self(iter.into_iter().collect::<C>())
529  }
530}
531
532impl<Bytes: AsRef<[u8]>, V: Array<Item = u8> + AppendCopy<u8>> TryConsumeBytes<Bytes> for Opt<V> {
533  type Error = OptParseError;
534
535  fn try_consume_bytes(bytes: &mut Cursor<Bytes>) -> Result<Self, Self::Error> {
536    let byte1 = bytes.next()
537                     .ok_or(OptParseError::OptionsExhausted)
538                     .and_then(|b| {
539                       if b == 0b11111111 {
540                         Err(OptParseError::OptionsExhausted)
541                       } else {
542                         Ok(b)
543                       }
544                     })?;
545
546    // NOTE: Delta **MUST** be consumed before Value. see comment on `opt_len_or_delta` for more info
547    let delta = parse_opt_len_or_delta(byte1 >> 4,
548                                       bytes,
549                                       OptParseError::OptionDeltaReservedValue(15))?;
550    let delta = OptDelta(delta);
551
552    let len = parse_opt_len_or_delta(byte1 & 0b00001111,
553                                     bytes,
554                                     OptParseError::ValueLengthReservedValue(15))?
555              as usize;
556
557    let mut value = V::reserve(len);
558    value.append_copy(bytes.take(len));
559
560    if value.len() < len {
561      return Err(Self::Error::UnexpectedEndOfStream);
562    }
563
564    let value = OptValue(value);
565
566    Ok(Opt { delta, value })
567  }
568}
569
570#[cfg(test)]
571mod tests {
572  use std_alloc::collections::BTreeMap;
573
574  use super::*;
575
576  #[test]
577  fn parse_opt() {
578    let mut opt_bytes = Cursor::new([0b00010001, 0b00000001]);
579    let opt = Opt::try_consume_bytes(&mut opt_bytes).unwrap();
580    assert_eq!(opt,
581               Opt { delta: OptDelta(1),
582                     value: OptValue(vec![1]) });
583
584    let mut opt_bytes = Cursor::new([0b11010001, 0b00000001, 0b00000001]);
585    let opt = Opt::try_consume_bytes(&mut opt_bytes).unwrap();
586    assert_eq!(opt,
587               Opt { delta: OptDelta(14),
588                     value: OptValue(vec![1]) });
589
590    let mut opt_bytes = Cursor::new([0b11100001, 0b00000000, 0b00000001, 0b00000001]);
591    let opt = Opt::try_consume_bytes(&mut opt_bytes).unwrap();
592    assert_eq!(opt,
593               Opt { delta: OptDelta(270),
594                     value: OptValue(vec![1]) });
595
596    let mut opt_bytes = Cursor::new([0b00000001, 0b00000001]);
597    let opt = Opt::try_consume_bytes(&mut opt_bytes).unwrap();
598    assert_eq!(opt,
599               Opt { delta: OptDelta(0),
600                     value: OptValue(vec![1]) });
601
602    let mut opt_bytes = Cursor::new([0b00000001, 0b00000001, 0b00010001, 0b00000011, 0b11111111]);
603    let opt =
604      BTreeMap::<OptNumber, Vec<OptValue<Vec<u8>>>>::try_consume_bytes(&mut opt_bytes).unwrap();
605    assert_eq!(opt,
606               BTreeMap::from([(OptNumber(0), vec![OptValue(vec![1])]),
607                               (OptNumber(1), vec![OptValue(vec![3])])]));
608  }
609
610  #[test]
611  fn opt_number_qualities() {
612    // critical, safe-to-fwd, cache-key
613    let if_match = OptNumber(1);
614
615    // critical, unsafe-to-fwd, cache-key
616    let uri_host = OptNumber(3);
617
618    // elective, safe-to-fwd, cache-key
619    let etag = OptNumber(4);
620
621    // elective, safe-to-fwd, no-cache-key
622    let size1 = OptNumber(60);
623
624    [&if_match, &uri_host].into_iter()
625                          .for_each(|num| {
626                            assert_eq!(num.must_be_processed(), OptionMustBeProcessed::Yes);
627                          });
628
629    [&etag, &size1].into_iter().for_each(|num| {
630                                 assert_eq!(num.must_be_processed(), OptionMustBeProcessed::No);
631                               });
632
633    [&if_match, &etag, &size1].into_iter().for_each(|num| {
634                                            assert_eq!(num.when_unsupported_by_proxy(),
635                                                       WhenOptionUnsupportedByProxy::Forward);
636                                          });
637
638    [&uri_host].into_iter().for_each(|num| {
639                             assert_eq!(num.when_unsupported_by_proxy(),
640                                        WhenOptionUnsupportedByProxy::Error);
641                           });
642
643    [&if_match, &uri_host, &etag].into_iter().for_each(|num| {
644                                               assert_eq!(num.when_option_changes(),
645                                                          WhenOptionChanges::ResponseChanges);
646                                             });
647
648    [&size1].into_iter().for_each(|num| {
649                          assert_eq!(num.when_option_changes(),
650                                     WhenOptionChanges::ResponseDoesNotChange);
651                        });
652  }
653}