async_coap/message/
codec.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16//! Low-level message codec functions.
17
18use super::option::*;
19use super::*;
20
21/// Calculates the encoded size of a CoAP option.
22pub fn calc_option_size(prev_key: OptionNumber, key: OptionNumber, mut value_len: usize) -> usize {
23    if value_len >= 269 {
24        value_len += 2;
25    } else if value_len >= 13 {
26        value_len += 1;
27    }
28
29    let option_delta = (key - prev_key) as u16;
30
31    if option_delta >= 269 {
32        value_len += 3;
33    } else if option_delta >= 13 {
34        value_len += 2;
35    } else {
36        value_len += 1;
37    }
38
39    return value_len;
40}
41
42/// Decodes one option from a `core::slice::Iter`, which can be obtained from a byte slice.
43/// The iterator is then advanced to the next option.
44///
45/// Will return `Ok(None)` if it either encounters the end-of-options marker (0xFF) or if the
46/// given iterator has been fully consumed.
47pub fn decode_option<'a>(
48    iter: &mut core::slice::Iter<'a, u8>,
49    last_option: OptionNumber,
50) -> Result<Option<(OptionNumber, &'a [u8])>, Error> {
51    // TODO: Improve performance.
52    macro_rules! try_next {
53        ($iter:expr, $none:expr) => {
54            match ($iter).next() {
55                Some(x) => *x,
56                None => return $none,
57            }
58        };
59    }
60
61    let header: u8 = try_next!(iter, Ok(None));
62
63    if header == 0xFF {
64        // End of options marker.
65        return Ok(None);
66    }
67
68    let key_delta: u16 = match header >> 4 {
69        13 => 13u16 + try_next!(iter, Err(Error::ParseFailure)) as u16,
70        14 => {
71            let msb = try_next!(iter, Err(Error::ParseFailure)) as u16;
72            (269u16 + try_next!(iter, Err(Error::ParseFailure)) as u16 + (msb << 8)) as u16
73        }
74        15 => return Err(Error::ParseFailure),
75        key @ _ => key as u16,
76    };
77
78    let len = match header & 0xF {
79        13 => (13 + try_next!(iter, Err(Error::ParseFailure))) as usize,
80        14 => {
81            let msb = try_next!(iter, Err(Error::ParseFailure)) as u16;
82            (269u16 + try_next!(iter, Err(Error::ParseFailure)) as u16 + (msb << 8)) as usize
83        }
84        15 => return Err(Error::ParseFailure),
85        len @ _ => len as usize,
86    };
87
88    if last_option > core::u16::MAX - key_delta {
89        // Don't let the key wrap.
90        return Err(Error::ParseFailure);
91    }
92
93    if len == 0 {
94        return Ok(Some((last_option + key_delta, &[])));
95    }
96
97    let value: &'a [u8] = &iter.as_slice()[..len];
98
99    iter.nth(len - 1);
100
101    Ok(Some((last_option + key_delta, value)))
102}
103
104/// Encodes all parts of an option into the given buffer *except* the value. All other parts,
105/// including the value length, are encoded. This is typically used directly when inserting
106/// options, otherwise `encode_option()` (which writes the value) is typically a better fit.
107pub fn encode_option_without_value(
108    buffer: &mut [u8],
109    prev_key: OptionNumber,
110    key: OptionNumber,
111    value_len: usize,
112) -> Result<usize, Error> {
113    if prev_key > key {
114        return Err(Error::InvalidArgument);
115    }
116
117    let calc_len = calc_option_size(prev_key, key, value_len);
118    if calc_len > buffer.len() {
119        eprintln!("calc_len:{}, blen:{}", calc_len, buffer.len());
120        return Err(Error::OutOfSpace);
121    }
122
123    if value_len > MAX_OPTION_VALUE_SIZE {
124        eprintln!("value_len:{}, max:{}", value_len, MAX_OPTION_VALUE_SIZE);
125        return Err(Error::InvalidArgument);
126    }
127
128    let mut value_offset = 1;
129    let mut option_delta = key - prev_key;
130
131    let buffer_ptr = buffer.as_mut_ptr();
132
133    unsafe {
134        // This is safe because we checked the buffer size constraints in a check above.
135        // This significantly improves performance.
136
137        if option_delta >= 269 {
138            option_delta -= 269;
139            *buffer_ptr.offset(0) = 14 << 4;
140            *buffer_ptr.offset(1) = (option_delta >> 8) as u8;
141            *buffer_ptr.offset(2) = option_delta as u8;
142            value_offset += 2;
143        } else if option_delta >= 13 {
144            *buffer_ptr.offset(0) = 13 << 4;
145            *buffer_ptr.offset(1) = (option_delta - 13) as u8;
146            value_offset += 1;
147        } else {
148            *buffer_ptr.offset(0) = (option_delta << 4) as u8;
149        }
150
151        if value_len >= 269 {
152            *buffer_ptr.offset(0) |= 14;
153            *buffer_ptr.offset(value_offset) = ((value_len - 269) >> 8) as u8;
154            *buffer_ptr.offset(value_offset + 1) = (value_len - 269) as u8;
155            value_offset += 2;
156        } else if value_len >= 13 {
157            *buffer_ptr.offset(0) |= 13;
158            *buffer_ptr.offset(value_offset) = (value_len - 13) as u8;
159            value_offset += 1;
160        } else {
161            *buffer_ptr.offset(0) |= (value_len & 15) as u8;
162        }
163    }
164
165    return Ok(value_offset as usize + value_len);
166}
167
168/// Encodes an option into the given buffer, including the value.
169pub fn encode_option(
170    buffer: &mut [u8],
171    prev_key: OptionNumber,
172    key: OptionNumber,
173    value: &[u8],
174) -> Result<usize, Error> {
175    let option_len = encode_option_without_value(buffer, prev_key, key, value.len())?;
176
177    // The value bytes are always at the end.
178    buffer[option_len - value.len()..option_len].copy_from_slice(value);
179
180    return Ok(option_len);
181}
182
183/// Helper function for implementing option insertion.
184/// Return value is a tuple of several fields:
185///
186/// * `split_index` (`usize`) The index where the new option should be inserted.
187/// * `prev_option_key` (`OptionNumber`) The option number of the option immediately before the split.
188/// * `next_key` (`OptionNumber`) The option number of the option immediately after the split.
189/// * `next_value_len` (`usize`) The length of the value of the option immediately after the split.
190/// * `next_option_size` (`usize`) The length of the entire option immediately after the split.
191///
192fn insert_split_helper(
193    buffer: &[u8],
194    key: OptionNumber,
195) -> (usize, OptionNumber, OptionNumber, usize, usize) {
196    // This is the key for the option immediately prior to
197    // the option we are adding.
198    let mut prev_option_key = OptionNumber(0);
199
200    // This marks at what index we will split the two halves.
201    let mut split_index;
202
203    let mut iter = OptionIterator::new(buffer);
204
205    loop {
206        split_index = iter.as_slice().as_ptr() as usize - buffer.as_ptr() as usize;
207
208        let (next_key, next_value) = iter
209            .next()
210            .expect(&format!(
211                "Unexpected end of options (prev: {}, iter: {:?})",
212                prev_option_key, iter
213            ))
214            .expect("Wrote corrupt options");
215
216        if next_key > key {
217            let next_option_size =
218                iter.as_slice().as_ptr() as usize - buffer.as_ptr() as usize - split_index;
219            return (
220                split_index,
221                prev_option_key,
222                next_key,
223                next_value.len(),
224                next_option_size,
225            );
226        }
227
228        prev_option_key = next_key;
229    }
230}
231
232/// Inserts an option into an option list. Very slow unless called sequentially.
233pub fn insert_option(
234    buffer: &mut [u8],
235    mut len: usize,
236    last_option: OptionNumber,
237    key: OptionNumber,
238    value: &[u8],
239) -> Result<(usize, OptionNumber), Error> {
240    if value.len() > MAX_OPTION_VALUE_SIZE {
241        return Err(Error::InvalidArgument);
242    }
243
244    if key >= last_option {
245        // This is the easy case: A simple append is adequate.
246        len += encode_option(&mut buffer[len..], last_option, key, value)?;
247        return Ok((len, key));
248    }
249
250    // What follows will only happen if this method is called with a property key
251    // out-of-order. Hopefully this should only happen rarely, as there is a
252    // significant performance penalty for doing so. This approach does have a
253    // bright side though: It doesn't require a heap.
254
255    let (split_index, prev_option_key, next_option_key, next_option_value_len, next_option_size) =
256        insert_split_helper(&buffer[..len], key);
257
258    // This variable is keeping track of the small possible change
259    // in size due to the change of the key delta encoding.
260    let key_delta_size_adj =
261        next_option_size - calc_option_size(key, next_option_key, next_option_value_len);
262
263    // The size of the option we are going to insert.
264    let new_option_size = calc_option_size(prev_option_key, key, value.len());
265
266    // Calculate the total change in size.
267    let adj_size = new_option_size - key_delta_size_adj;
268
269    // Do a space check before we start trying to move buffers around.
270    if len + adj_size > buffer.len() {
271        println!(
272            "len:{} + adj_size:{} > blen:{}",
273            len,
274            adj_size,
275            buffer.len()
276        );
277        return Err(Error::OutOfSpace);
278    }
279
280    let src = split_index..len;
281    let dest = split_index + adj_size;
282
283    // Move the options above the split.
284    buffer.copy_within(src, dest);
285    len += adj_size;
286
287    // Encode our new option.
288    // This should not fail---if it does then something
289    // has gone terribly wrong and we should panic.
290    encode_option(
291        &mut buffer[split_index..split_index + new_option_size],
292        prev_option_key,
293        key,
294        value,
295    )
296    .expect("Internal inconsistency inserting option");
297
298    if key != prev_option_key {
299        // Partially Re-encode the next option, since the previous option
300        // key value has changed. Since the value part hasn't changed and
301        // remains at the end of the option, we don't need it here.
302        // This should not fail---if it does then something
303        // has gone terribly wrong and we should panic.
304        encode_option_without_value(
305            &mut buffer[split_index + new_option_size..],
306            key,
307            next_option_key,
308            next_option_value_len,
309        )
310        .expect("Internal inconsistency inserting option");
311    }
312
313    return Ok((len, last_option));
314}