summaryrefslogtreecommitdiff
path: root/README.md
blob: 09046b2a0fa79f0edf908c1b6b568a4e840e7446 (plain)
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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# libecbor

CBOR library with no dependencies, small memory footprint and code size, developed for both desktop and embedded applications.

## Compiling

Compiling the library:

```
cmake .
make
```

This will generate the following files:
* `lib/libecbor.so` - dynamic linking version
* `lib/libecbor.a` - static linking version
* `include/ecbor.h` - header file for library
* `ecbor-describe` - describe tool, loads CBOR contents from file and displays them

## Testing

Unit tests can be run by performing:

```
cd test/
./runtests.sh
```

## Installation

Installing can be performed with:

```
cmake . -DCMAKE_INSTALL_PREFIX=<install_prefix>
make
make install
```

If `<install_prefix>` is omitted, then the default install path is used (usually `/usr/local`).

## Guide

### Memory usage

`libecbor` guarantees that no memory other than the buffers provided by the caller is used; it has no dependency on any memory manager or standard library, other than the standard type definitions. While this can be sometimes cumbersome, it is a desired trait for memory-constrained devices.

Moreover, both encoding and decoding have an *absolute upper bound* on stack usage, regardless of the depth or size of the CBOR object. Actual numbers depend on the compiler and flags.

### Error handling

Most `libecbor` API calls return an error code. It is a good practice to check it consistently, especially when building for embedded targets where debugging may be more difficult.

Usually you would look for a return code of `ECBOR_OK`. The full error list can be found in the header file `include/ecbor.h` under the `ecbor_error_t` enum.

### Encoder

Each encoding operation must use an encode context (`ecbor_encode_context_t`), usually defined on the stack:

```c
ecbor_encode_context_t context;
```

The encoder must be initialized either in *normal* encoding mode

```c
uint8_t buffer[BUFFER_SIZE];
ecbor_error_t rc = ecbor_initialize_encode(&context, buffer, BUFFER_SIZE);
```

or in *streamed* mode

```c
uint8_t buffer[BUFFER_SIZE];
ecbor_error_t rc = ecbor_initialize_encode_streamed(&context, buffer, BUFFER_SIZE);
```

While in *normal* mode the user can encode a whole item tree at once (including children), it lacks support for encoding indefinite strings, maps and arrays, since it is assumed the item is pre-built and the size is already known.

If support for indefinite items is mandatory then the *streamed* mode can be used, but the user must encode the children manually. Please be attentive that the desired CBOR structure is followed in this case.

`buffer` is simply an output buffer of size `BUFFER_SIZE` where the encoder will place the resulting CBOR object.

Once the context has been initialized, items can be encoded with

```c
ecbor_error_t rc = ecbor_encode (&context, &item);
```

where `item` is of type `ecbor_item_t`. If the item is too large for the context output buffer, then `ECBOR_ERR_INVALID_END_OF_BUFFER` will be returned.

Simple items can be built by using one of the following APIs:

```c
ecbor_item_t item = ecbor_int (uint_value);
ecbor_item_t item = ecbor_uint (int_value);

ecbor_item_t item = ecbor_fp32 (float_value);
ecbor_item_t item = ecbor_fp64 (double_value);

ecbor_item_t item = ecbor_bool (bool_or_int_value);

ecbor_item_t item = ecbor_bstr (bin_buffer_ptr, BIN_BUFFER_SIZE);
ecbor_item_t item = ecbor_str (c_str_ptr, strlen(c_str_ptr));
ecbor_item_t item = ecbor_tag (&child_item, tag_int_value);

ecbor_item_t item = ecbor_null ();
ecbor_item_t item = ecbor_undefined ();
```

Arrays can be created with

```c
ecbor_error_t rc = ecbor_array (&arr, array_items, item_count);
```

where `arr` is the output `ecbor_item_t`, `array_items` is a `ecbor_item_t*` pointing to the children and `item_count` is the size of `array_items`.

Maps can be created with

```c
ecbor_error_t rc = ecbor_map (&map, keys, values, keyval_count);
```

where `map` is the output `ecbor_item_t`, `keys` and `values` are `ecbor_item_t*`, and `keyval_count` is the size of `keys` and `values`. Note that the *i*-th key-value pair is composed of `keys[i]` and `values[i]`.

If using *streamed* encoding mode, then only the placeholder (token) items must be built:

```c
ecbor_item_t item = ecbor_array_token (item_count);
ecbor_item_t item = ecbor_indefinite_array_token ();

ecbor_item_t item = ecbor_map_token (keyval_count);
ecbor_item_t item = ecbor_indefinite_map_token ();

ecbor_item_t item = ecbor_stop_code ();
```

but the user must subsequently encode the correct amount of child items (for definite-size arrays and maps) or the stop code (for indefinite arrays and maps).

Once all the items have been encoded, the length of the output buffer can be obtained either by calling 
```c
size_t sz;
ecbor_error_t rc = ecbor_get_encoded_buffer_size(&context, &sz);
```

or by using the `ECBOR_GET_ENCODED_BUFFER_SIZE` macro.

### Decoder

Just like encoding, the decoding operation must use a decode context (`ecbor_decode_context_t`), usually defined on the stack:

```c
ecbor_decode_context_t context;
```

The decoder must be initialized either in *normal* decoding mode

```c
ecbor_error_t rc = ecbor_initialize_decode (&context, buffer, buffer_size);
```

in *streamed* mode

```c
ecbor_error_t rc = ecbor_initialize_decode_streamed (&context, buffer, buffer_size);
```

or in *tree* mode

```c
ecbor_item_t item_buffer[MAX_ITEMS];
ecbor_error_t rc = ecbor_initialize_decode_streamed (&context, buffer, buffer_size, item_buffer, MAX_ITEMS);
```

where `buffer` is the input `uint8_t*` buffer of size `buffer_size`, and `item_buffer` is an output item buffer.

In *streamed* mode, any decoding API call will parse and retrieve *one* item from the buffer, without children, in the order specified in the buffer. This is a *dumb* but fast mode where absolutely no semantic checking is performed. Linking parents to children is left to the user, hence this mode is recommended only for very simple scenarios.

In *normal* mode, any decoding API call will parse one *whole* item, including it's children, but will *not* store the parsed children. Children can later be retrieved with special API calls, but each of them will trigger a re-parsing of part of the subtree. This mode only uses one instance of *ecbor_item_t*, and has no limit on the size of the parsed CBOR, but may be heavy on CPU usage since no parsing results are cached.

In *tree* mode, any decoding API call will parse one *whole* item, including its children, and all parsing results are cached in `item_buffer`. User must make sure this buffer is sufficiently large to store all items. Subsequent API calls to retrieve children will simply explore the parsed tree.

**IMPORTANT:** String items will *not* copy the payload from the original CBOR buffer. Moreover, in *normal* mode children are being re-parsed from the CBOR buffer as well. Do *not* destroy this buffer until *all* manipulation on the CBOR items has been finished.

In *normal* and *streamed* mode, one item can be decoded with

```c
ecbor_item_t item;
ecbor_error_t rc = ecbor_decode (&context, &item);
```

while in *tree* mode, the root item can be decoded with

```c
ecbor_item_t *root = NULL;
ecbor_error_t rc = ecbor_decode_tree (&context, &root);
```

After the call, `root` will point to the root item from the `item_buffer`.

For item manipulation there are two alternative APIs that can be used.

### Decoder - strict API

To retrieve the type of an item:

```c
ecbor_type_t type = ecbor_get_type (&item);
```

To retrieve the values of numerics and booleans:

```c
uint8_t val;
ecbor_error_t rc = ecbor_get_uint8 (item, &val);

uint16_t val;
ecbor_error_t rc = ecbor_get_uint16 (item, &val);

uint32_t val;
ecbor_error_t rc = ecbor_get_uint32 (item, &val);

uint64_t val;
ecbor_error_t rc = ecbor_get_uint64 (item, &val);


int8_t val;
ecbor_error_t rc = ecbor_get_int8 (item, &val);

int16_t val;
ecbor_error_t rc = ecbor_get_int16 (item, &val);

int32_t val;
ecbor_error_t rc = ecbor_get_int32 (item, &val);

int64_t val;
ecbor_error_t rc = ecbor_get_int64 (item, &val);


float val;
ecbor_error_t rc = ecbor_get_fp32 (item, &val);

double val;
ecbor_error_t rc = ecbor_get_fp64 (item, &val);


uint8_t val;
ecbor_error_t rc = ecbor_get_bool (item, &val);
```

To retrieve the *tag value* of a tag item:

```c
uint64_t tag_val;
ecbor_error_t rc = ecbor_get_tag_value (&tag_item, &tag_val);
```

To retrieve the length of a binary string, string, array or map:

```c
size_t length = 0;
ecbor_error_t rc = ecbor_get_length (&item, &length);
```

To retrieve a child of a tag, array or map:

```c
ecbor_item_t arr_item;
ecbor_error_t rc = ecbor_get_array_item (&array, index, &arr_item);

ecbor_item_t *arr_item_ptr = NULL;
ecbor_error_t rc = ecbor_get_array_item_ptr (&array, index, &arr_item_ptr);


ecbor_item_t key, val;
ecbor_error_t rc = ecbor_get_map_item (&map, index, &key, &val);

ecbor_item_t *key_ptr = NULL, *val_ptr = NULL;
ecbor_error_t rc = ecbor_get_map_item_ptr (&map, index, &key_ptr, &val_ptr);


ecbor_item_t item;
ecbor_error_t rc = ecbor_get_tag_item (&tag, &item);

ecbor_item_t *item_ptr = NULL;
ecbor_error_t rc = ecbor_get_tag_item_ptr (&tag, &item_ptr);
```

**IMPORTANT:** Note that the `*_ptr` versions of the APIs work only in *tree* mode.

To retrieve the payload of definite strings and binary strings:

```c
const char *c_str = NULL;
ecbor_error_t rc = ecbor_get_str (&str_item, &c_str);

const uint8_t *buffer = NULL;
ecbor_error_t rc = ecbor_get_bstr (&bstr_item, &buffer);
```

For *indefinite* strings and binary strings, each chunk must be parsed individually (a chunk is a child item of the same type, but with *definite* length):

```c
size_t chunk_count;
ecbor_error_t rc = ecbor_get_str_chunk_count (&str_item, &chunk_count);

ecbor_item_t chunk;
ecbor_error_t rc = ecbor_get_str_chunk (&str_item, index, &chunk);


size_t chunk_count;
ecbor_error_t rc = ecbor_get_bstr_chunk_count (&bstr_item, &chunk_count);

ecbor_item_t chunk;
ecbor_error_t rc = ecbor_get_bstr_chunk (&bstr_item, index, &chunk);
```

### Decoder - fast API

The following macros help to identify the type of the item:

```c
ecbor_item_t item;

ecbor_type_t type = ECBOR_GET_TYPE(&item)

ECBOR_IS_INDEFINITE(&item)
ECBOR_IS_DEFINITE(&item)

ECBOR_IS_NINT(&item)
ECBOR_IS_UINT(&item)
ECBOR_IS_INTEGER(&item)
ECBOR_IS_BSTR(&item)
ECBOR_IS_STR(&item)
ECBOR_IS_ARRAY(&item)
ECBOR_IS_MAP(&item)
ECBOR_IS_TAG(&item)
ECBOR_IS_FP32(&item)
ECBOR_IS_FLOAT(&item)
ECBOR_IS_FP64(&item)
ECBOR_IS_DOUBLE(&item)
ECBOR_IS_BOOL(&item)
ECBOR_IS_NULL(&item)
ECBOR_IS_UNDEFINED(&item)
```

In order to retrieve the value:

```c
ecbor_item_t item;

int64_t val = ECBOR_GET_INT(&item)
uint64_t val = ECBOR_GET_UINT(&item)

const char *c_str = ECBOR_GET_STRING(&item)

float val = ECBOR_GET_FP32(&item)
doube val = ECBOR_GET_FP64(&item)

uint8_t bool_value = ECBOR_GET_BOOL(&item)

uint64_t tag_value = ECBOR_GET_TAG_VALUE(&item)
ecbor_item_t tag_item = ECBOR_GET_TAG_ITEM(&item)
```

And finally, in order to retrieve the length of arrays, maps, strings:

```c
ecbor_item_t item;
size_t len = ECBOR_GET_LENGTH(&item)
```