blob: ca30c28b0940caf2406cdcd236cfb6f684b812d8 [file] [log] [blame]
Laurence Lundbladed4cd7232020-07-03 19:30:48 -07001/*==============================================================================
2 example.c -- Example code for QCBOR
3
4 Copyright (c) 2020, Laurence Lundblade. All rights reserved.
5
6 SPDX-License-Identifier: BSD-3-Clause
7
8 See BSD-3-Clause license in README.md
9
10 Created on 6/30/2020
11=============================================================================*/
12
13
14#include <stdio.h>
15#include "example.h"
16#include "qcbor/qcbor_encode.h"
17#include "qcbor/qcbor_decode.h"
18
19#define MAX_CYLINDERS 16
20
21typedef struct
22{
23 UsefulBufC Manufacturer;
Laurence Lundblade06c83042020-07-03 23:04:53 -070024 int64_t uNumCylinders;
25 int64_t uDisplacement;
26 int64_t uHorsePower;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070027 double uDesignedCompresion;
28 struct {
29 double uMeasuredCompression;
30 } cylinders[MAX_CYLINDERS];
31 bool bTurboCharged;
32} Engine;
33
34
35void EngineInit(Engine *pE)
36{
37 pE->uNumCylinders = 6;
38 pE->bTurboCharged = false;
39 pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche");
40 pE->uDisplacement = 3296;
41 pE->uHorsePower = 210;
42 pE->uDesignedCompresion = 9.1;
43 pE->cylinders[0].uMeasuredCompression = 9.0;
44 pE->cylinders[1].uMeasuredCompression = 9.2;
45 pE->cylinders[2].uMeasuredCompression = 8.9;
46 pE->cylinders[3].uMeasuredCompression = 8.9;
47 pE->cylinders[4].uMeasuredCompression = 9.1;
48 pE->cylinders[5].uMeasuredCompression = 9.0;
49}
50
51
52UsefulBufC EncodeEngine(const Engine *pEngine, UsefulBuf Buffer)
53{
Laurence Lundblade06c83042020-07-03 23:04:53 -070054 /* Initialize th encoder with the buffer big enough to hold the expected output.
55 If it is too small, QCBOREncode_Finish() will return an error. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070056 QCBOREncodeContext EncodeCtx;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070057 QCBOREncode_Init(&EncodeCtx, Buffer);
Laurence Lundblade06c83042020-07-03 23:04:53 -070058
59 /* Proceed output all the items, letting the internal error
60 tracking do its work. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070061 QCBOREncode_OpenMap(&EncodeCtx);
62 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -070063 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
64 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
65 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070066 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->uDesignedCompresion);
67 QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -070068 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070069 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
70 }
71 QCBOREncode_CloseArray(&EncodeCtx);
72 QCBOREncode_AddBoolToMap(&EncodeCtx, "turbo", pEngine->bTurboCharged);
73 QCBOREncode_CloseMap(&EncodeCtx);
74
Laurence Lundblade06c83042020-07-03 23:04:53 -070075 /* Get the pointer and length of the encoded output. If there was
76 anny error it will be returned here. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070077 UsefulBufC EncodedCBOR;
78 QCBORError uErr;
79 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
80 if(uErr != QCBOR_SUCCESS) {
81 return NULLUsefulBufC;
82 } else {
83 return EncodedCBOR;
84 }
85}
86
87
88UsefulBufC EncodeEngineIndefinteLen(const Engine *pEngine, UsefulBuf Buffer)
89{
90 QCBOREncodeContext EncodeCtx;
91
92 QCBOREncode_Init(&EncodeCtx, Buffer);
93 QCBOREncode_OpenMapIndefiniteLength(&EncodeCtx);
94 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -070095 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
96 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070097 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->uDesignedCompresion);
Laurence Lundblade06c83042020-07-03 23:04:53 -070098 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070099 QCBOREncode_OpenArrayIndefiniteLengthInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700100 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700101 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
102 }
103 QCBOREncode_CloseArrayIndefiniteLength(&EncodeCtx);
104 QCBOREncode_AddBoolToMap(&EncodeCtx, "turbo", pEngine->bTurboCharged);
105 QCBOREncode_CloseMapIndefiniteLength(&EncodeCtx);
106
107 UsefulBufC EncodedCBOR;
108 QCBORError uErr;
109 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
110 if(uErr != QCBOR_SUCCESS) {
111 return NULLUsefulBufC;
112 } else {
113 return EncodedCBOR;
114 }
115}
116
117
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700118
Laurence Lundblade06c83042020-07-03 23:04:53 -0700119typedef enum {
120 EngineSuccess,
121 CBORNotWellFormed,
122 TooManyCylinders,
123 EngineProtocolerror,
124 WrongNumberOfCylinders
125} EngineDecodeErrors;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700126
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700127
Laurence Lundblade06c83042020-07-03 23:04:53 -0700128EngineDecodeErrors ConvertError(QCBORError uErr)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700129{
Laurence Lundblade06c83042020-07-03 23:04:53 -0700130 EngineDecodeErrors uReturn;
131
132 switch(uErr)
133 {
134 case QCBOR_SUCCESS:
135 uReturn = EngineSuccess;
136 break;
137
138 case QCBOR_ERR_HIT_END:
139 uReturn = CBORNotWellFormed;
140 break;
141
142 default:
143 uReturn = EngineProtocolerror;
144 break;
145 }
146
147 return uReturn;
148}
149
150
151/*
152 Decode using the advanced decode features. This pulls in more
153 code from the QCBOR library, but is much simpler and
154 roughly mirrors the encoding implementation.
155 */
156EngineDecodeErrors DecodeEngine(UsefulBufC EncodedEngine, Engine *pE)
157{
158 QCBORError uErr;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700159 QCBORDecodeContext DecodeCtx;
160
161 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
162 QCBORDecode_EnterMap(&DecodeCtx);
163 QCBORDecode_GetTextInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
Laurence Lundblade06c83042020-07-03 23:04:53 -0700164 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
165 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700166 QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->uDesignedCompresion));
167 QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "turbo", &(pE->bTurboCharged));
168
Laurence Lundblade06c83042020-07-03 23:04:53 -0700169 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700170
171 /* Must check error before referencing pE->uNumCylinders to be sure it
172 is valid. If any of the above errored, it won't be valid. */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700173 uErr = QCBORDecode_GetError(&DecodeCtx);
174 if(uErr != QCBOR_SUCCESS) {
175 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700176 }
177
178 if(pE->uNumCylinders > MAX_CYLINDERS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700179 return TooManyCylinders;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700180 }
181
182 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700183 int64_t i = 0;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700184 while(1) {
185 QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
186 i++;
187 if(i >= pE->uNumCylinders ) {
188 break;
189 }
190 }
191 QCBORDecode_ExitArray(&DecodeCtx);
192 QCBORDecode_ExitMap(&DecodeCtx);
193
Laurence Lundblade06c83042020-07-03 23:04:53 -0700194 /* Catch the remainder of errors here */
195 uErr = QCBORDecode_Finish(&DecodeCtx);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700196
Laurence Lundblade06c83042020-07-03 23:04:53 -0700197Done:
198 return ConvertError(uErr);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700199}
200
Laurence Lundblade06c83042020-07-03 23:04:53 -0700201
202
203
204/*
205
206 - Match
207 - Error
208 - No match
209
210 */
211
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700212QCBORError CheckLabelAndType(const char *szLabel, uint8_t uQCBORType, QCBORItem *pItem)
213{
214 if(pItem->uLabelType != QCBOR_TYPE_TEXT_STRING) {
215 return QCBOR_ERR_NOT_FOUND;
216 }
217
218 UsefulBufC Label = UsefulBuf_FromSZ(szLabel);
219
Laurence Lundblade06c83042020-07-03 23:04:53 -0700220 if(UsefulBuf_Compare(Label, pItem->label.string)) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700221 return QCBOR_ERR_NOT_FOUND;
222 }
223
Laurence Lundblade06c83042020-07-03 23:04:53 -0700224 if(pItem->uDataType != uQCBORType && uQCBORType != QCBOR_TYPE_ANY) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700225 return QCBOR_ERR_UNEXPECTED_TYPE;
226 }
227
228 return QCBOR_SUCCESS;
229}
230
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700231
Laurence Lundblade06c83042020-07-03 23:04:53 -0700232EngineDecodeErrors DecodeCylinders(QCBORDecodeContext *pDecodeCtx,
233 Engine *pE,
234 const QCBORItem *pItem)
235{
236 int i = 0;
237 QCBORItem Item;
238
239 /* Loop getting all the items in the array */
240 do {
241 QCBORError uErr;
242
243 uErr = QCBORDecode_GetNext(pDecodeCtx, &Item);
244 if(uErr != QCBOR_SUCCESS) {
245 return CBORNotWellFormed;
246 }
247 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
248 return CBORNotWellFormed;
249 }
250
251 if(i < MAX_CYLINDERS) {
252 pE->cylinders[i].uMeasuredCompression = Item.val.dfnum;
253 i++;
254 }
255
256 } while (Item.uNextNestLevel == pItem->uNextNestLevel);
257
258 if(i != pE->uNumCylinders) {
259 return WrongNumberOfCylinders;
260 } else {
261 return EngineSuccess;
262 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700263}
264
Laurence Lundblade06c83042020-07-03 23:04:53 -0700265
266
267EngineDecodeErrors DecodeEngineBasic(UsefulBufC EncodedEngine, Engine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700268{
269 QCBORDecodeContext DecodeCtx;
270
271 QCBORDecode_Init(&DecodeCtx, EncodedEngine, 0);// TODO: fill in mode;
272
273 QCBORItem Item;
274 QCBORError uErr;
Laurence Lundblade06c83042020-07-03 23:04:53 -0700275 EngineDecodeErrors uReturn;
276
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700277
278 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
279 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700280 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700281 goto Done;
282 }
283 if(Item.uDataType != QCBOR_TYPE_MAP) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700284 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700285 goto Done;
286 }
287
288 while(1) {
289 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
290 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700291 if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
292 break; /* Non-error exit from the loop */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700293 } else {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700294 uReturn = CBORNotWellFormed;
295 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700296 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700297 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700298
Laurence Lundblade06c83042020-07-03 23:04:53 -0700299 uErr = CheckLabelAndType("Manufacturer", QCBOR_TYPE_TEXT_STRING, &Item);
300 if(uErr == QCBOR_SUCCESS) {
301 pE->Manufacturer = Item.val.string;
302 continue;
303 } else if(uErr != QCBOR_ERR_NOT_FOUND){
304 /* Maunfacturer field missing or badly formed */
305 return EngineProtocolerror;
306 } /* continue on and try for another match */
307
308
309
310 uErr = CheckLabelAndType("NumCylinders", QCBOR_TYPE_INT64, &Item);
311 if(uErr == QCBOR_SUCCESS) {
312 if(Item.val.int64 > MAX_CYLINDERS) {
313 return TooManyCylinders;
314 } else {
315 pE->uNumCylinders = (uint8_t)Item.val.int64;
316 continue;
317 }
318 } else if(uErr != QCBOR_ERR_NOT_FOUND){
319 /* Maunfacturer field missing or badly formed */
320 return EngineProtocolerror;
321 }
322
323 uErr = CheckLabelAndType("Cylinders", QCBOR_TYPE_ARRAY, &Item);
324 if(uErr == QCBOR_SUCCESS) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700325 DecodeCylinders(&DecodeCtx, pE, &Item);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700326 continue;
327 } else if(uErr != QCBOR_ERR_NOT_FOUND){
328 return EngineProtocolerror;
329 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700330
Laurence Lundblade06c83042020-07-03 23:04:53 -0700331 uErr = CheckLabelAndType("Displacement", QCBOR_TYPE_INT64, &Item);
332 if(uErr == QCBOR_SUCCESS) {
333 pE->uDisplacement = Item.val.int64;
334 continue;
335 } else if(uErr != QCBOR_ERR_NOT_FOUND){
336 return EngineProtocolerror;
337 }
338
339 uErr = CheckLabelAndType("Horsepower", QCBOR_TYPE_INT64, &Item);
340 if(uErr == QCBOR_SUCCESS) {
341 pE->uHorsePower = Item.val.int64;
342 continue;
343 } else if(uErr != QCBOR_ERR_NOT_FOUND){
344 return EngineProtocolerror;
345 }
346
347 uErr = CheckLabelAndType("DesignedCompression", QCBOR_TYPE_DOUBLE, &Item);
348 if(uErr == QCBOR_SUCCESS) {
349 pE->uDisplacement = Item.val.int64;
350 continue;
351 } else if(uErr != QCBOR_ERR_NOT_FOUND){
352 return EngineProtocolerror;
353 }
354
355 uErr = CheckLabelAndType("turbo", QCBOR_TYPE_ANY, &Item);
356 if(uErr == QCBOR_SUCCESS) {
357 if(Item.uDataType == QCBOR_TYPE_TRUE) {
358 pE->bTurboCharged = true;
359 } else if(Item.uDataType == QCBOR_TYPE_FALSE) {
360 pE->bTurboCharged = false;
361 } else {
362 return EngineProtocolerror;
363 }
364 continue;
365 } else if(uErr != QCBOR_ERR_NOT_FOUND){
366 return EngineProtocolerror;
367 }
368
369
370 /* Some label data item that is not known
371 (could just ignore extras data items) */
372 return EngineProtocolerror;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700373 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700374 uReturn = EngineSuccess;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700375
376
377Done:
Laurence Lundblade06c83042020-07-03 23:04:53 -0700378 return uReturn;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700379}
380
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700381
382
383
384
385void RunQCborExample()
386{
387 Engine E, DecodedEngine;
388 MakeUsefulBufOnStack( EngineBuffer, 300);
389 UsefulBufC EncodedEngine;
390
391 MakeUsefulBufOnStack( InDefEngineBuffer, 300);
392 UsefulBufC InDefEncodedEngine;
393
394 EngineInit(&E);
395
396 EncodedEngine = EncodeEngine(&E, EngineBuffer);
397
398 printf("Engine Encoded in %zu bytes\n", EncodedEngine.len);
399
Laurence Lundblade06c83042020-07-03 23:04:53 -0700400 int x = (int)DecodeEngine(EncodedEngine, &DecodedEngine);
401 printf("Engine Decode Result: %d\n", x);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700402
403
404 InDefEncodedEngine = EncodeEngineIndefinteLen(&E, InDefEngineBuffer);
405
406 printf("Indef Engine Encoded in %zu bytes\n", InDefEncodedEngine.len);
407
Laurence Lundblade06c83042020-07-03 23:04:53 -0700408 x = (int)DecodeEngine(InDefEncodedEngine, &DecodedEngine);
409 printf("Indef Engine Decode Result: %d\n", x);
410
411
412 x = (int)DecodeEngineBasic(EncodedEngine, &DecodedEngine);
413 printf("Engine Basic Decode Result: %d\n", x);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700414}