1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
use crate::error::{Error, Result};
use crate::raw;
use crate::types::CborMajorType;
use core::marker::PhantomData;
use core::ptr;
use core::slice;
/// Zero-copy CBOR item decoded from an input buffer.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CborItem<'a> {
/// Major type.
pub major_type: CborMajorType,
/// Numeric value, length, or item count.
pub value: u64,
/// Borrowed byte/text payload for byte and text strings.
pub data: Option<&'a [u8]>,
}
impl<'a> CborItem<'a> {
fn from_raw(item: raw::WOLFCOSE_CBOR_ITEM) -> Self {
let data = if item.data.is_null() {
None
} else {
// SAFETY: wolfCOSE returns a pointer into the decoder input valid
// for `dataLen`; the lifetime is tied to the decoder input.
Some(unsafe { slice::from_raw_parts(item.data, item.dataLen) })
};
Self {
major_type: CborMajorType::from_raw(item.majorType).unwrap_or(CborMajorType::SIMPLE),
value: item.val,
data,
}
}
}
/// CBOR encoder over a caller-provided output buffer.
pub struct CborEncoder<'a> {
ctx: raw::WOLFCOSE_CBOR_CTX,
_marker: PhantomData<&'a mut [u8]>,
}
impl<'a> CborEncoder<'a> {
/// Create an encoder over `out`.
pub fn new(out: &'a mut [u8]) -> Self {
Self {
ctx: raw::WOLFCOSE_CBOR_CTX {
buf: out.as_mut_ptr(),
cbuf: ptr::null(),
bufSz: out.len(),
idx: 0,
},
_marker: PhantomData,
}
}
/// Number of bytes written.
pub fn len(&self) -> usize {
self.ctx.idx
}
/// Whether no bytes have been written.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Return the bytes written so far.
pub fn as_written(&self) -> &'a [u8] {
// SAFETY: the encoder exclusively owns `buf` for `'a`, and `idx` never
// exceeds `bufSz` when wolfCOSE reports success.
unsafe { slice::from_raw_parts(self.ctx.buf, self.ctx.idx) }
}
/// Encode an unsigned integer.
pub fn encode_u64(&mut self, value: u64) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeUint(&mut self.ctx, value) })
}
/// Encode a signed integer.
pub fn encode_i64(&mut self, value: i64) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeInt(&mut self.ctx, value) })
}
/// Encode a byte string.
pub fn encode_bstr(&mut self, data: &[u8]) -> Result<()> {
let ptr = if data.is_empty() {
ptr::null()
} else {
data.as_ptr()
};
// SAFETY: `ptr` is null for empty input or valid for `data.len()`.
Error::from_code(unsafe { raw::wc_CBOR_EncodeBstr(&mut self.ctx, ptr, data.len()) })
}
/// Encode a UTF-8 text string.
pub fn encode_tstr(&mut self, text: &str) -> Result<()> {
let bytes = text.as_bytes();
let ptr = if bytes.is_empty() {
ptr::null()
} else {
bytes.as_ptr()
};
// SAFETY: `ptr` is null for empty input or valid for `bytes.len()`.
Error::from_code(unsafe { raw::wc_CBOR_EncodeTstr(&mut self.ctx, ptr, bytes.len()) })
}
/// Encode an array header.
pub fn encode_array_start(&mut self, count: usize) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeArrayStart(&mut self.ctx, count) })
}
/// Encode a map header.
pub fn encode_map_start(&mut self, count: usize) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeMapStart(&mut self.ctx, count) })
}
/// Encode a semantic tag.
pub fn encode_tag(&mut self, tag: u64) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeTag(&mut self.ctx, tag) })
}
/// Encode CBOR true.
pub fn encode_true(&mut self) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeTrue(&mut self.ctx) })
}
/// Encode CBOR false.
pub fn encode_false(&mut self) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeFalse(&mut self.ctx) })
}
/// Encode CBOR null.
pub fn encode_null(&mut self) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer.
Error::from_code(unsafe { raw::wc_CBOR_EncodeNull(&mut self.ctx) })
}
/// Encode an IEEE 754 single-precision float.
///
/// Returns [`Error::Unsupported`](crate::Error::Unsupported) unless
/// `wolfcose-sys` was built with the `float` feature.
pub fn encode_f32(&mut self, value: f32) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer. The sys shim
// returns Unsupported when the C float API is not compiled in.
Error::from_code(unsafe { raw::rb_wc_CBOR_EncodeFloat(&mut self.ctx, value) })
}
/// Encode an IEEE 754 double-precision float.
///
/// Returns [`Error::Unsupported`](crate::Error::Unsupported) unless
/// `wolfcose-sys` was built with the `float` feature.
pub fn encode_f64(&mut self, value: f64) -> Result<()> {
// SAFETY: `ctx` points to the live encoder buffer. The sys shim
// returns Unsupported when the C float API is not compiled in.
Error::from_code(unsafe { raw::rb_wc_CBOR_EncodeDouble(&mut self.ctx, value) })
}
}
/// CBOR decoder over an input buffer.
pub struct CborDecoder<'a> {
ctx: raw::WOLFCOSE_CBOR_CTX,
_marker: PhantomData<&'a [u8]>,
}
impl<'a> CborDecoder<'a> {
/// Create a decoder over `input`.
pub fn new(input: &'a [u8]) -> Self {
Self {
ctx: raw::WOLFCOSE_CBOR_CTX {
buf: ptr::null_mut(),
cbuf: input.as_ptr(),
bufSz: input.len(),
idx: 0,
},
_marker: PhantomData,
}
}
/// Current decoder cursor.
pub fn position(&self) -> usize {
self.ctx.idx
}
/// Total input length.
pub fn len(&self) -> usize {
self.ctx.bufSz
}
/// Whether the decoder input is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Whether all input bytes have been consumed.
pub fn is_finished(&self) -> bool {
self.position() == self.len()
}
/// Remaining undecoded byte count.
pub fn remaining(&self) -> usize {
self.len().saturating_sub(self.position())
}
/// Peek at the next CBOR major type.
pub fn peek_type(&self) -> Option<CborMajorType> {
if self.ctx.idx >= self.ctx.bufSz {
None
} else {
// SAFETY: bounds were checked above and `ctx` points to this decoder.
CborMajorType::from_raw(unsafe { raw::wc_CBOR_PeekType_Rust(&self.ctx) })
}
}
/// Peek at the raw initial byte of the next item.
pub fn peek_initial_byte(&self) -> Option<u8> {
if self.ctx.idx >= self.ctx.bufSz {
None
} else {
// SAFETY: bounds were checked above and `cbuf` points to decoder input.
Some(unsafe { *self.ctx.cbuf.add(self.ctx.idx) })
}
}
/// Decode a raw CBOR item head.
pub fn decode_head(&mut self) -> Result<CborItem<'a>> {
let mut item = raw::WOLFCOSE_CBOR_ITEM::default();
// SAFETY: output item and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeHead(&mut self.ctx, &mut item) })?;
Ok(CborItem::from_raw(item))
}
/// Decode CBOR null.
pub fn decode_null(&mut self) -> Result<()> {
let item = self.decode_head()?;
if item.major_type == CborMajorType::SIMPLE
&& item.value == simple_value(raw::WOLFCOSE_CBOR_NULL)
{
Ok(())
} else {
Err(Error::CborType)
}
}
/// Decode a CBOR boolean.
pub fn decode_bool(&mut self) -> Result<bool> {
let item = self.decode_head()?;
if item.major_type != CborMajorType::SIMPLE {
return Err(Error::CborType);
}
match item.value {
value if value == simple_value(raw::WOLFCOSE_CBOR_TRUE) => Ok(true),
value if value == simple_value(raw::WOLFCOSE_CBOR_FALSE) => Ok(false),
_ => Err(Error::CborType),
}
}
/// Decode an unsigned integer.
pub fn decode_u64(&mut self) -> Result<u64> {
let mut value = 0;
// SAFETY: output value and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeUint(&mut self.ctx, &mut value) })?;
Ok(value)
}
/// Decode a signed integer.
pub fn decode_i64(&mut self) -> Result<i64> {
let mut value = 0;
// SAFETY: output value and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeInt(&mut self.ctx, &mut value) })?;
Ok(value)
}
/// Decode a byte string.
pub fn decode_bstr(&mut self) -> Result<&'a [u8]> {
let mut data = ptr::null();
let mut len = 0;
// SAFETY: outputs and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeBstr(&mut self.ctx, &mut data, &mut len) })?;
if data.is_null() {
Ok(&[])
} else {
// SAFETY: wolfCOSE returns a slice into the decoder input.
Ok(unsafe { slice::from_raw_parts(data, len) })
}
}
/// Decode a text string as bytes.
pub fn decode_tstr_bytes(&mut self) -> Result<&'a [u8]> {
let mut data = ptr::null();
let mut len = 0;
// SAFETY: outputs and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeTstr(&mut self.ctx, &mut data, &mut len) })?;
if data.is_null() {
Ok(&[])
} else {
// SAFETY: wolfCOSE returns a slice into the decoder input.
Ok(unsafe { slice::from_raw_parts(data, len) })
}
}
/// Decode an array header.
pub fn decode_array_start(&mut self) -> Result<usize> {
let mut count = 0;
// SAFETY: output count and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeArrayStart(&mut self.ctx, &mut count) })?;
Ok(count)
}
/// Decode a map header.
pub fn decode_map_start(&mut self) -> Result<usize> {
let mut count = 0;
// SAFETY: output count and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeMapStart(&mut self.ctx, &mut count) })?;
Ok(count)
}
/// Decode a semantic tag.
pub fn decode_tag(&mut self) -> Result<u64> {
let mut tag = 0;
// SAFETY: output tag and decoder context are valid.
Error::from_code(unsafe { raw::wc_CBOR_DecodeTag(&mut self.ctx, &mut tag) })?;
Ok(tag)
}
/// Skip the next complete CBOR item.
pub fn skip(&mut self) -> Result<()> {
// SAFETY: decoder context is valid.
Error::from_code(unsafe { raw::wc_CBOR_Skip(&mut self.ctx) })
}
}
fn simple_value(initial_byte: u32) -> u64 {
(initial_byte & 0x1f) as u64
}