-
Notifications
You must be signed in to change notification settings - Fork 47
/
example.c
384 lines (334 loc) · 12.3 KB
/
example.c
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
372
373
374
375
376
377
378
379
380
381
382
383
384
/* =========================================================================
example.c -- Example code for QCBOR
Copyright (c) 2020-2021, Laurence Lundblade. All rights reserved.
Copyright (c) 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 6/30/2020
========================================================================== */
#include <stdio.h>
#include "example.h"
#include "qcbor/qcbor_encode.h"
#include "qcbor/qcbor_decode.h"
#include "qcbor/qcbor_spiffy_decode.h"
/**
* This is a simple example of encoding and decoding some CBOR from
* and to a C structure.
*
* This also includes a comparison between the original structure
* and the one decoded from the CBOR to confirm correctness.
*/
#define MAX_CYLINDERS 16
/**
* The data structure representing a car engine that is encoded and
* decoded in this example.
*/
typedef struct
{
UsefulBufC Manufacturer;
int64_t uDisplacement;
int64_t uHorsePower;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
double dDesignedCompresion;
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
int64_t uNumCylinders;
bool bTurboCharged;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
struct {
double dMeasuredCompression;
} cylinders[MAX_CYLINDERS];
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
} CarEngine;
/**
* @brief Initialize the Engine data structure with values to encode.
*
* @param[out] pE The Engine structure to fill in
*/
void EngineInit(CarEngine *pE)
{
pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche");
pE->uDisplacement = 3296;
pE->uHorsePower = 210;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
pE->dDesignedCompresion = 9.1;
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
pE->uNumCylinders = 6;
pE->bTurboCharged = false;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
pE->cylinders[0].dMeasuredCompression = 9.0;
pE->cylinders[1].dMeasuredCompression = 9.2;
pE->cylinders[2].dMeasuredCompression = 8.9;
pE->cylinders[3].dMeasuredCompression = 8.9;
pE->cylinders[4].dMeasuredCompression = 9.1;
pE->cylinders[5].dMeasuredCompression = 9.0;
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
}
/**
* @brief Compare two Engine structure for equality.
*
* @param[in] pE1 First Engine to compare.
* @param[in] pE2 Second Engine to compare.
*
* @retval Return @c true if the two Engine data structures are exactly the
* same.
*/
static bool EngineCompare(const CarEngine *pE1, const CarEngine *pE2)
{
if(pE1->uNumCylinders != pE2->uNumCylinders) {
return false;
}
if(pE1->bTurboCharged != pE2->bTurboCharged) {
return false;
}
if(pE1->uDisplacement != pE2->uDisplacement) {
return false;
}
if(pE1->uHorsePower != pE2->uHorsePower) {
return false;
}
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) {
return false;
}
for(int64_t i = 0; i < pE2->uNumCylinders; i++) {
if(pE1->cylinders[i].dMeasuredCompression !=
pE2->cylinders[i].dMeasuredCompression) {
return false;
}
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) {
return false;
}
return true;
}
/**
* @brief Encode an initialized CarEngine data structure in CBOR.
*
* @param[in] pEngine The data structure to encode.
* @param[in] Buffer Pointer and length of buffer to output to.
*
* @return The pointer and length of the encoded CBOR or
* @ref NULLUsefulBufC on error.
*
* This encodes the input structure @c pEngine as a CBOR map of
* label-value pairs. An array of float is one of the items in the
* map.
*
* This uses the UsefulBuf convention of passing in a non-const empty
* buffer to be filled in and returning a filled in const buffer. The
* buffer to write into is given as a pointer and length in a
* UsefulBuf. The buffer returned with the encoded CBOR is a
* UsefulBufC also a pointer and length. In this implementation the
* pointer to the returned data is exactly the same as that of the
* empty buffer. The returned length will be smaller than or equal to
* that of the empty buffer. This gives correct const-ness for the
* buffer passed in and the data returned.
*
* @c Buffer must be big enough to hold the output. If it is not @ref
* NULLUsefulBufC will be returned. @ref NULLUsefulBufC will be
* returned for any other encoding errors.
*
* This can be called with @c Buffer set to @ref SizeCalculateUsefulBuf
* in which case the size of the encoded engine will be calculated,
* but no actual encoded CBOR will be output. The calculated size is
* in @c .len of the returned @ref UsefulBufC.
*/
UsefulBufC EncodeEngine(const CarEngine *pEngine, UsefulBuf Buffer)
{
/* Set up the encoding context with the output buffer */
QCBOREncodeContext EncodeCtx;
QCBOREncode_Init(&EncodeCtx, Buffer);
/* Proceed to output all the items, letting the internal error
* tracking do its work */
QCBOREncode_OpenMap(&EncodeCtx);
QCBOREncode_AddTextToMapSZ(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
QCBOREncode_AddInt64ToMapSZ(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
QCBOREncode_AddInt64ToMapSZ(&EncodeCtx, "Displacement", pEngine->uDisplacement);
QCBOREncode_AddInt64ToMapSZ(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
QCBOREncode_AddDoubleToMapSZ(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBOREncode_OpenArrayInMapSZ(&EncodeCtx, "Cylinders");
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
QCBOREncode_AddDouble(&EncodeCtx,
pEngine->cylinders[i].dMeasuredCompression);
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBOREncode_CloseArray(&EncodeCtx);
QCBOREncode_AddBoolToMapSZ(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
QCBOREncode_CloseMap(&EncodeCtx);
/* Get the pointer and length of the encoded output. If there was
* any encoding error, it will be returned here */
UsefulBufC EncodedCBOR;
QCBORError uErr;
uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
if(uErr != QCBOR_SUCCESS) {
return NULLUsefulBufC;
} else {
return EncodedCBOR;
}
}
/**
* Error results when decoding an Engine data structure.
*/
typedef enum {
EngineSuccess,
CBORNotWellFormed,
TooManyCylinders,
EngineProtocolerror,
WrongNumberOfCylinders
} EngineDecodeErrors;
/**
* Convert @ref QCBORError to @ref EngineDecodeErrors.
*/
static EngineDecodeErrors ConvertError(QCBORError uErr)
{
EngineDecodeErrors uReturn;
switch(uErr)
{
case QCBOR_SUCCESS:
uReturn = EngineSuccess;
break;
case QCBOR_ERR_HIT_END:
uReturn = CBORNotWellFormed;
break;
default:
uReturn = EngineProtocolerror;
break;
}
return uReturn;
}
/**
* @brief Simplest engine decode using spiffy decode features.
*
* @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
* @param[out] pE The structure filled in from the decoding.
*
* @return The decode error or success.
*
* This decodes the CBOR into the engine structure.
*
* As QCBOR automatically supports both definite and indefinite maps
* and arrays, this will decode either.
*
* This uses QCBOR's spiffy decode functions, so the implementation is
* simple and closely parallels the encode implementation in
* EncodeEngineDefiniteLength().
*
* Another way to decode without using spiffy decode functions is to
* use QCBORDecode_GetNext() to traverse the whole tree. This
* requires a more complex implementation, but is faster and will pull
* in less code from the CBOR library. The speed advantage is likely
* of consequence when decoding much much larger CBOR on slow small
* CPUs.
*
* A middle way is to use the spiffy decode
* QCBORDecode_GetItemsInMap(). The implementation has middle
* complexity and uses less CPU.
*/
EngineDecodeErrors DecodeEngineSpiffy(UsefulBufC EncodedEngine, CarEngine *pE)
{
QCBORError uErr;
QCBORDecodeContext DecodeCtx;
/* Let QCBORDecode internal error tracking do its work. */
QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
QCBORDecode_EnterMap(&DecodeCtx, NULL);
QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion));
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged));
QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
/* Check the internal tracked error now before going on to
* reference any of the decoded data, particularly
* pE->uNumCylinders */
uErr = QCBORDecode_GetError(&DecodeCtx);
if(uErr != QCBOR_SUCCESS) {
goto Done;
}
if(pE->uNumCylinders > MAX_CYLINDERS) {
return TooManyCylinders;
}
QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
for(int64_t i = 0; i < pE->uNumCylinders; i++) {
QCBORDecode_GetDouble(&DecodeCtx,
&(pE->cylinders[i].dMeasuredCompression));
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBORDecode_ExitArray(&DecodeCtx);
QCBORDecode_ExitMap(&DecodeCtx);
/* Catch further decoding error here */
uErr = QCBORDecode_Finish(&DecodeCtx);
Done:
return ConvertError(uErr);
}
int32_t RunQCborExample(void)
{
CarEngine InitialEngine;
CarEngine DecodedEngine;
/* For every buffer used by QCBOR a pointer and a length are always
* carried in a UsefulBuf. This is a secure coding and hygene
* practice to help make sure code never runs off the end of a
* buffer.
*
* UsefulBuf structures are passed as a stack parameter to make the
* code prettier. The object code generated isn't much different
* from passing a pointer parameter and a length parameter.
*
* This macro is equivalent to:
* uint8_t __pBufEngineBuffer[300];
* UsefulBuf EngineBuffer = {__pBufEngineBuffer, 300};
*/
UsefulBuf_MAKE_STACK_UB( EngineBuffer, 300);
/* The pointer in UsefulBuf is not const and used for representing
* a buffer to be written to. For UsefulbufC, the pointer is const
* and is used to represent a buffer that has been written to.
*/
UsefulBufC EncodedEngine;
EngineDecodeErrors uErr;
/* Initialize the structure with some values. */
EngineInit(&InitialEngine);
/* Encode the engine structure. */
EncodedEngine = EncodeEngine(&InitialEngine, EngineBuffer);
if(UsefulBuf_IsNULLC(EncodedEngine)) {
printf("Engine encode failed\n");
goto Done;
}
printf("Example: Definite Length Engine Encoded in %zu bytes\n",
EncodedEngine.len);
/* Decode the CBOR */
uErr = DecodeEngineSpiffy(EncodedEngine, &DecodedEngine);
printf("Example: Spiffy Engine Decode Result: %d\n", uErr);
if(uErr) {
goto Done;
}
/* Check the results */
if(!EngineCompare(&InitialEngine, &DecodedEngine)) {
printf("Example: Spiffy Engine Decode comparison fail\n");
}
/* Further example of how to calculate the encoded size, then allocate */
UsefulBufC EncodedEngineSize;
EncodedEngineSize = EncodeEngine(&InitialEngine, SizeCalculateUsefulBuf);
if(UsefulBuf_IsNULLC(EncodedEngine)) {
printf("Engine encode size calculation failed\n");
goto Done;
}
(void)EncodedEngineSize; /* Supress unsed variable warning */
/* Here malloc could be called to allocate a buffer. Then
* EncodeEngine() can be called a second time to actually
* encode. (The actual code is not live here to avoid a
* dependency on malloc()).
* UsefulBuf MallocedBuffer;
* MallocedBuffer.len = EncodedEngineSize.len;
* MallocedBuffer.ptr = malloc(EncodedEngineSize.len);
* EncodedEngine = EncodeEngine(&InitialEngine, MallocedBuffer);
*/
Done:
printf("\n");
return 0;
}