blob: 40c21995997fdbd5e6e5406be915e0f7776ebe39 [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
Laurence Lundbladeda319282020-07-06 23:04:58 -070021
22/**
23 The data structure representing a car engine that is encoded and decoded in this examples.
24 */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070025typedef struct
26{
27 UsefulBufC Manufacturer;
Laurence Lundblade1818e632020-07-26 04:14:08 -070028 int64_t uDisplacement;
29 int64_t uHorsePower;
30 double dDesignedCompresion;
31 int64_t uNumCylinders;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070032 struct {
33 double uMeasuredCompression;
Laurence Lundblade1818e632020-07-26 04:14:08 -070034 } cylinders[MAX_CYLINDERS];
35 bool bTurboCharged;
Laurence Lundbladeda319282020-07-06 23:04:58 -070036} CarEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070037
38
Laurence Lundbladeda319282020-07-06 23:04:58 -070039/**
40 Initialize the Engine data structure with some values to encode/decode.
41 */
42void EngineInit(CarEngine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070043{
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070044 pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche");
45 pE->uDisplacement = 3296;
46 pE->uHorsePower = 210;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -070047 pE->dDesignedCompresion = 9.1;
Laurence Lundbladeda319282020-07-06 23:04:58 -070048 pE->uNumCylinders = 6;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070049 pE->cylinders[0].uMeasuredCompression = 9.0;
50 pE->cylinders[1].uMeasuredCompression = 9.2;
51 pE->cylinders[2].uMeasuredCompression = 8.9;
52 pE->cylinders[3].uMeasuredCompression = 8.9;
53 pE->cylinders[4].uMeasuredCompression = 9.1;
54 pE->cylinders[5].uMeasuredCompression = 9.0;
Laurence Lundbladeda319282020-07-06 23:04:58 -070055 pE->bTurboCharged = false;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070056}
57
58
Laurence Lundbladeda319282020-07-06 23:04:58 -070059/**
60 Return @c true if the two Engined data structures are exactly the same.
61 */
62bool EngineCompare(CarEngine *pE1, CarEngine *pE2)
Laurence Lundbladee6bbf552020-07-05 22:57:57 -070063{
64 if(pE1->uNumCylinders != pE2->uNumCylinders) {
65 return false;
66 }
67 if(pE1->bTurboCharged != pE2->bTurboCharged) {
68 return false;
69 }
70 if(pE1->uDisplacement != pE2->uDisplacement) {
71 return false;
72 }
73 if(pE1->uHorsePower != pE2->uHorsePower) {
74 return false;
75 }
76 if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) {
77 return false;
78 }
79 for(int64_t i = 0; i < pE2->uNumCylinders; i++) {
80 if(pE1->cylinders[i].uMeasuredCompression !=
81 pE2->cylinders[i].uMeasuredCompression) {
82 return false;
83 }
84 }
85
86 if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) {
87 return false;
88 }
89
90 return true;
91}
92
93
Laurence Lundbladeda319282020-07-06 23:04:58 -070094/**
95 @brief Encode an initialized Engine data structure in CBOR.
96
97 @param[in] pEngine The data structure to encode.
98 @param[in] Buffer Pointer and length of buffer to output to.
99
100 @return The pointer and length of the encoded CBOR or @ref NULLUsefulBufC on error.
101
102 @c Buffer must be big enough to hold the output. If it is not @ref NULLUsefulBufC
103 will be returned. @ref @ref NULLUsefulBufC will be returned for any other encoding
104 errors.
105
106 This encoding will use definite CBOR lengths.
107 */
108UsefulBufC EncodeEngine(const CarEngine *pEngine, UsefulBuf Buffer)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700109{
Laurence Lundblade06c83042020-07-03 23:04:53 -0700110 /* Initialize th encoder with the buffer big enough to hold the expected output.
111 If it is too small, QCBOREncode_Finish() will return an error. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700112 QCBOREncodeContext EncodeCtx;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700113 QCBOREncode_Init(&EncodeCtx, Buffer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700114
115 /* Proceed output all the items, letting the internal error
116 tracking do its work. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700117 QCBOREncode_OpenMap(&EncodeCtx);
118 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700119 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
120 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
121 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700122 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700123 QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700124 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700125 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
126 }
127 QCBOREncode_CloseArray(&EncodeCtx);
Laurence Lundbladeda319282020-07-06 23:04:58 -0700128 QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700129 QCBOREncode_CloseMap(&EncodeCtx);
130
Laurence Lundblade06c83042020-07-03 23:04:53 -0700131 /* Get the pointer and length of the encoded output. If there was
132 anny error it will be returned here. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700133 UsefulBufC EncodedCBOR;
134 QCBORError uErr;
135 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
136 if(uErr != QCBOR_SUCCESS) {
137 return NULLUsefulBufC;
138 } else {
139 return EncodedCBOR;
140 }
141}
142
143
Laurence Lundbladeda319282020-07-06 23:04:58 -0700144/**
145 @brief Encode an initialized Engine data structure in CBOR using indefinite lengths..
146
147 @param[in] pEngine The data structure to encode.
148 @param[in] Buffer Pointer and length of buffer to output to.
149
150 @return The pointer and length of the encoded CBOR or @ref NULLUsefulBufC on error.
151
152 This is virtually the same as EncodeEngine(). The encoded CBOR is slightly different as the
153 map and array use indefinite lengths, rather than definite lengths.
154
155 There is little practical use for this function as definite lengths are generally preferred for
156 CBOR and QCBOR always easily encodes definite lengths. (The advantage of indefinite
157 lengths are that they are simpler to encode, but that doesn't come into effect here).
158 */
159UsefulBufC EncodeEngineIndefinteLen(const CarEngine *pEngine, UsefulBuf Buffer)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700160{
161 QCBOREncodeContext EncodeCtx;
162
163 QCBOREncode_Init(&EncodeCtx, Buffer);
164 QCBOREncode_OpenMapIndefiniteLength(&EncodeCtx);
165 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700166 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
167 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700168 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700169 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700170 QCBOREncode_OpenArrayIndefiniteLengthInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700171 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700172 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
173 }
174 QCBOREncode_CloseArrayIndefiniteLength(&EncodeCtx);
Laurence Lundbladeda319282020-07-06 23:04:58 -0700175 QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700176 QCBOREncode_CloseMapIndefiniteLength(&EncodeCtx);
177
178 UsefulBufC EncodedCBOR;
179 QCBORError uErr;
180 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
181 if(uErr != QCBOR_SUCCESS) {
182 return NULLUsefulBufC;
183 } else {
184 return EncodedCBOR;
185 }
186}
187
188
Laurence Lundbladeda319282020-07-06 23:04:58 -0700189/**
190 Error results when decoding an Engine data structure.
191 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700192typedef enum {
193 EngineSuccess,
194 CBORNotWellFormed,
195 TooManyCylinders,
196 EngineProtocolerror,
197 WrongNumberOfCylinders
198} EngineDecodeErrors;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700199
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700200
Laurence Lundbladeda319282020-07-06 23:04:58 -0700201/**
202 Convert \ref QCBORError to \ref EngineDecodeErrors.
203 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700204EngineDecodeErrors ConvertError(QCBORError uErr)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700205{
Laurence Lundblade06c83042020-07-03 23:04:53 -0700206 EngineDecodeErrors uReturn;
207
208 switch(uErr)
209 {
210 case QCBOR_SUCCESS:
211 uReturn = EngineSuccess;
212 break;
213
214 case QCBOR_ERR_HIT_END:
215 uReturn = CBORNotWellFormed;
216 break;
217
218 default:
219 uReturn = EngineProtocolerror;
220 break;
221 }
222
223 return uReturn;
224}
225
226
Laurence Lundbladeda319282020-07-06 23:04:58 -0700227/**
228 @brief Simplest engine decode using advanced decoe features.
229
230 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
231 @param[out] pE The structure filled in from the decoding.
232
233 @return The decode error or success.
234
235 This verssion of the decoder has the simplest implementation, but
236 pulls in more code from the QCBOR library. This version uses
237 the most CPU because it scanns the all the CBOR each time
238 a data item is decoded. The CPU used for a data structure as small
239 as this is probably insignificant. CPU use for this style of decode is
240 probably only a factor on slow CPUs with big CBOR inputs.
241
242 Code size is yet to be measured, but this is probably the smallest total
243 code size if multiple protocols are being decoded in one application because
244 the complex parsing of a map and array is done be shared code from the
245 CBOR library rather than by individual protocol-specific chunks of code.
246 Similarly, this may be the smallest for complex CBOR with multiple
247 maps that need to be processed..
248
249 See also DecodeEngineAdvancedFaster() and DecodeEngineBasic().
Laurence Lundblade06c83042020-07-03 23:04:53 -0700250 */
Laurence Lundbladeda319282020-07-06 23:04:58 -0700251EngineDecodeErrors DecodeEngineAdvanced(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundblade06c83042020-07-03 23:04:53 -0700252{
253 QCBORError uErr;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700254 QCBORDecodeContext DecodeCtx;
255
256 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
257 QCBORDecode_EnterMap(&DecodeCtx);
258 QCBORDecode_GetTextInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
Laurence Lundblade06c83042020-07-03 23:04:53 -0700259 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
260 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700261 QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion));
Laurence Lundbladeda319282020-07-06 23:04:58 -0700262 QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700263
Laurence Lundblade06c83042020-07-03 23:04:53 -0700264 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700265
266 /* Must check error before referencing pE->uNumCylinders to be sure it
267 is valid. If any of the above errored, it won't be valid. */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700268 uErr = QCBORDecode_GetError(&DecodeCtx);
269 if(uErr != QCBOR_SUCCESS) {
270 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700271 }
272
273 if(pE->uNumCylinders > MAX_CYLINDERS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700274 return TooManyCylinders;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700275 }
276
277 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700278 for(int64_t i = 0; i < pE->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700279 QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700280 }
281 QCBORDecode_ExitArray(&DecodeCtx);
282 QCBORDecode_ExitMap(&DecodeCtx);
283
Laurence Lundblade06c83042020-07-03 23:04:53 -0700284 /* Catch the remainder of errors here */
285 uErr = QCBORDecode_Finish(&DecodeCtx);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700286
Laurence Lundblade06c83042020-07-03 23:04:53 -0700287Done:
288 return ConvertError(uErr);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700289}
290
Laurence Lundblade06c83042020-07-03 23:04:53 -0700291
Laurence Lundbladeda319282020-07-06 23:04:58 -0700292/**
293 @brief Simplest engine decode using advanced decoe features.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700294
Laurence Lundbladeda319282020-07-06 23:04:58 -0700295 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
296 @param[out] pE The structure filled in from the decoding.
297
298 @return The decode error or success.
299
Laurence Lundblade1818e632020-07-26 04:14:08 -0700300 This verssion of the decoder is still fairly simple and uses the
Laurence Lundbladeda319282020-07-06 23:04:58 -0700301 advanced decode features like DecodeEngine(), but is faster
302 and pulls in less library code. It is faster because all the items
303 except the array are pulled out of the map in one pass, rather
304 than multiple passes.
305
306 See also DecodeEngineAdvanced() and DecodeEngineBasic().
307*/
308EngineDecodeErrors DecodeEngineAdvancedFaster(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700309{
310 QCBORError uErr;
311 QCBORDecodeContext DecodeCtx;
312
313 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
314 QCBORDecode_EnterMap(&DecodeCtx);
315
Laurence Lundbladeda319282020-07-06 23:04:58 -0700316 QCBORItem EngineItems[7];
317 EngineItems[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
318 EngineItems[0].label.string = UsefulBuf_FROM_SZ_LITERAL("Manufacturer");
319 EngineItems[0].uDataType = QCBOR_TYPE_TEXT_STRING;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700320
Laurence Lundbladeda319282020-07-06 23:04:58 -0700321 EngineItems[1].uLabelType = QCBOR_TYPE_TEXT_STRING;
322 EngineItems[1].label.string = UsefulBuf_FROM_SZ_LITERAL("Displacement");
323 EngineItems[1].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700324
Laurence Lundbladeda319282020-07-06 23:04:58 -0700325 EngineItems[2].uLabelType = QCBOR_TYPE_TEXT_STRING;
326 EngineItems[2].label.string = UsefulBuf_FROM_SZ_LITERAL("Horsepower");
327 EngineItems[2].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700328
Laurence Lundbladeda319282020-07-06 23:04:58 -0700329 EngineItems[3].uLabelType = QCBOR_TYPE_TEXT_STRING;
330 EngineItems[3].label.string = UsefulBuf_FROM_SZ_LITERAL("DesignedCompression");
331 EngineItems[3].uDataType = QCBOR_TYPE_DOUBLE;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700332
Laurence Lundbladeda319282020-07-06 23:04:58 -0700333 EngineItems[4].uLabelType = QCBOR_TYPE_TEXT_STRING;
334 EngineItems[4].label.string = UsefulBuf_FROM_SZ_LITERAL("Turbo");
335 EngineItems[4].uDataType = QCBOR_TYPE_ANY;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700336
Laurence Lundbladeda319282020-07-06 23:04:58 -0700337 EngineItems[5].uLabelType = QCBOR_TYPE_TEXT_STRING;
338 EngineItems[5].label.string = UsefulBuf_FROM_SZ_LITERAL("NumCylinders");
339 EngineItems[5].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700340
Laurence Lundbladeda319282020-07-06 23:04:58 -0700341 EngineItems[6].uLabelType = QCBOR_TYPE_NONE;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700342
Laurence Lundbladeda319282020-07-06 23:04:58 -0700343 uErr = QCBORDecode_GetItemsInMap(&DecodeCtx, EngineItems);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700344 if(uErr != QCBOR_SUCCESS) {
345 goto Done;
346 }
347
Laurence Lundbladeda319282020-07-06 23:04:58 -0700348 pE->Manufacturer = EngineItems[0].val.string;
349 pE->uDisplacement = EngineItems[1].val.int64;
350 pE->uHorsePower = EngineItems[2].val.int64;
351 pE->dDesignedCompresion = EngineItems[3].val.dfnum;
352 pE->uNumCylinders = EngineItems[5].val.int64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700353
Laurence Lundbladeda319282020-07-06 23:04:58 -0700354 if(EngineItems[4].uDataType == QCBOR_TYPE_TRUE) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700355 pE->bTurboCharged = true;
Laurence Lundbladeda319282020-07-06 23:04:58 -0700356 } else if(EngineItems[4].uDataType == QCBOR_TYPE_FALSE) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700357 pE->bTurboCharged = false;
358 } else {
359 return EngineProtocolerror;
360 }
361
362
363 /* Must check error before referencing pE->uNumCylinders to be sure it
364 is valid. If any of the above errored, it won't be valid. */
365 uErr = QCBORDecode_GetError(&DecodeCtx);
366 if(uErr != QCBOR_SUCCESS) {
367 goto Done;
368 }
369
370 if(pE->uNumCylinders > MAX_CYLINDERS) {
371 return TooManyCylinders;
372 }
373
374 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
375 for(int64_t i = 0; i < pE->uNumCylinders; i++) {
376 QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
377 }
378 QCBORDecode_ExitArray(&DecodeCtx);
379 QCBORDecode_ExitMap(&DecodeCtx);
380
381 /* Catch the remainder of errors here */
382 uErr = QCBORDecode_Finish(&DecodeCtx);
383
384Done:
385 return ConvertError(uErr);
386}
387
388
389
390
Laurence Lundblade06c83042020-07-03 23:04:53 -0700391
Laurence Lundblade1818e632020-07-26 04:14:08 -0700392/**
393 @brief Check the type and lable of an item.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700394
Laurence Lundblade1818e632020-07-26 04:14:08 -0700395 @param[in] szLabel The expected string label.
396 @param[in] uQCBORType The expected type or @c QCBOR_TYPE_ANY
397 @param[in] pItem The item to check.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700398
Laurence Lundblade1818e632020-07-26 04:14:08 -0700399 @retval QCBOR_ERR_NOT_FOUND The label doesn't match.
400 @retval QCBOR_ERR_UNEXPECTED_TYPE The label matches, but the type is not as expected.
401 @retval QCBOR_SUCCESS Both label and type match.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700402 */
Laurence Lundblade1818e632020-07-26 04:14:08 -0700403QCBORError CheckLabelAndType(const char *szLabel, uint8_t uQCBORType, const QCBORItem *pItem)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700404{
405 if(pItem->uLabelType != QCBOR_TYPE_TEXT_STRING) {
406 return QCBOR_ERR_NOT_FOUND;
407 }
408
409 UsefulBufC Label = UsefulBuf_FromSZ(szLabel);
410
Laurence Lundblade06c83042020-07-03 23:04:53 -0700411 if(UsefulBuf_Compare(Label, pItem->label.string)) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700412 return QCBOR_ERR_NOT_FOUND;
413 }
414
Laurence Lundblade06c83042020-07-03 23:04:53 -0700415 if(pItem->uDataType != uQCBORType && uQCBORType != QCBOR_TYPE_ANY) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700416 return QCBOR_ERR_UNEXPECTED_TYPE;
417 }
418
419 return QCBOR_SUCCESS;
420}
421
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700422
Laurence Lundblade1818e632020-07-26 04:14:08 -0700423/**
424 @brief Decode the array of engine cylinders.
425
426 @param[in] pDecodeCtx The decode context from which to get items.
427 @param[out] pE The structure filled in from the decoding.
428 @param[in] pItem The data item that is the start of the array.
429
430 @return Either @ref EngineSuccess or an error.
431
432 This always consumes the whole array. If it has the wrong number of
433 items in it, an error is returned.
434 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700435EngineDecodeErrors DecodeCylinders(QCBORDecodeContext *pDecodeCtx,
Laurence Lundbladeda319282020-07-06 23:04:58 -0700436 CarEngine *pE,
Laurence Lundblade06c83042020-07-03 23:04:53 -0700437 const QCBORItem *pItem)
438{
439 int i = 0;
440 QCBORItem Item;
441
Laurence Lundblade1818e632020-07-26 04:14:08 -0700442 /* Loop getting all the items in the array. This uses
443 nesting level to detect the end so it works for both
444 definite and indefinite length arrays. */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700445 do {
446 QCBORError uErr;
447
448 uErr = QCBORDecode_GetNext(pDecodeCtx, &Item);
449 if(uErr != QCBOR_SUCCESS) {
450 return CBORNotWellFormed;
451 }
452 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
453 return CBORNotWellFormed;
454 }
455
456 if(i < MAX_CYLINDERS) {
457 pE->cylinders[i].uMeasuredCompression = Item.val.dfnum;
458 i++;
459 }
460
461 } while (Item.uNextNestLevel == pItem->uNextNestLevel);
462
463 if(i != pE->uNumCylinders) {
464 return WrongNumberOfCylinders;
465 } else {
466 return EngineSuccess;
467 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700468}
469
Laurence Lundblade06c83042020-07-03 23:04:53 -0700470
Laurence Lundbladeda319282020-07-06 23:04:58 -0700471/**
Laurence Lundblade1818e632020-07-26 04:14:08 -0700472 @brief Engine decode without advanced decode features.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700473
Laurence Lundblade1818e632020-07-26 04:14:08 -0700474 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
475 @param[out] pE The structure filled in from the decoding.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700476
Laurence Lundblade1818e632020-07-26 04:14:08 -0700477 @return The decode error or success.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700478
Laurence Lundblade1818e632020-07-26 04:14:08 -0700479 This version of the deocde is the most complex, but uses
Laurence Lundbladeda319282020-07-06 23:04:58 -0700480 significantly less code from the QCBOR library. It is also
481 the most CPU-efficient since it does only one pass
482 through the CBOR.
483
Laurence Lundblade1818e632020-07-26 04:14:08 -0700484 Code size is yet to be measured, but this is probably the smallest total
Laurence Lundbladeda319282020-07-06 23:04:58 -0700485 code size of all three, if just one CBOR protocol is being decoded. If
486 multiple protocols are being decoded the other options.
487
488 See also DecodeEngineAdvanced() and DecodeEngineAdvancedFaster().
489*/
490EngineDecodeErrors DecodeEngineBasic(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700491{
492 QCBORDecodeContext DecodeCtx;
493
494 QCBORDecode_Init(&DecodeCtx, EncodedEngine, 0);// TODO: fill in mode;
495
496 QCBORItem Item;
497 QCBORError uErr;
Laurence Lundblade06c83042020-07-03 23:04:53 -0700498 EngineDecodeErrors uReturn;
499
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700500
501 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
502 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700503 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700504 goto Done;
505 }
506 if(Item.uDataType != QCBOR_TYPE_MAP) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700507 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700508 goto Done;
509 }
510
511 while(1) {
512 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
513 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700514 if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
515 break; /* Non-error exit from the loop */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700516 } else {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700517 uReturn = CBORNotWellFormed;
518 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700519 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700520 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700521
Laurence Lundblade06c83042020-07-03 23:04:53 -0700522 uErr = CheckLabelAndType("Manufacturer", QCBOR_TYPE_TEXT_STRING, &Item);
523 if(uErr == QCBOR_SUCCESS) {
524 pE->Manufacturer = Item.val.string;
525 continue;
526 } else if(uErr != QCBOR_ERR_NOT_FOUND){
527 /* Maunfacturer field missing or badly formed */
528 return EngineProtocolerror;
529 } /* continue on and try for another match */
530
Laurence Lundblade06c83042020-07-03 23:04:53 -0700531 uErr = CheckLabelAndType("NumCylinders", QCBOR_TYPE_INT64, &Item);
532 if(uErr == QCBOR_SUCCESS) {
533 if(Item.val.int64 > MAX_CYLINDERS) {
534 return TooManyCylinders;
535 } else {
536 pE->uNumCylinders = (uint8_t)Item.val.int64;
537 continue;
538 }
539 } else if(uErr != QCBOR_ERR_NOT_FOUND){
Laurence Lundblade1818e632020-07-26 04:14:08 -0700540 /* NumCylinders field missing or badly formed */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700541 return EngineProtocolerror;
Laurence Lundblade1818e632020-07-26 04:14:08 -0700542 } /* continue on and try for another match */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700543
544 uErr = CheckLabelAndType("Cylinders", QCBOR_TYPE_ARRAY, &Item);
545 if(uErr == QCBOR_SUCCESS) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700546 DecodeCylinders(&DecodeCtx, pE, &Item);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700547 continue;
548 } else if(uErr != QCBOR_ERR_NOT_FOUND){
549 return EngineProtocolerror;
550 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700551
Laurence Lundblade06c83042020-07-03 23:04:53 -0700552 uErr = CheckLabelAndType("Displacement", QCBOR_TYPE_INT64, &Item);
553 if(uErr == QCBOR_SUCCESS) {
554 pE->uDisplacement = Item.val.int64;
555 continue;
556 } else if(uErr != QCBOR_ERR_NOT_FOUND){
557 return EngineProtocolerror;
558 }
559
560 uErr = CheckLabelAndType("Horsepower", QCBOR_TYPE_INT64, &Item);
561 if(uErr == QCBOR_SUCCESS) {
562 pE->uHorsePower = Item.val.int64;
563 continue;
564 } else if(uErr != QCBOR_ERR_NOT_FOUND){
565 return EngineProtocolerror;
566 }
567
568 uErr = CheckLabelAndType("DesignedCompression", QCBOR_TYPE_DOUBLE, &Item);
569 if(uErr == QCBOR_SUCCESS) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700570 pE->dDesignedCompresion = Item.val.dfnum;
Laurence Lundblade06c83042020-07-03 23:04:53 -0700571 continue;
572 } else if(uErr != QCBOR_ERR_NOT_FOUND){
573 return EngineProtocolerror;
574 }
575
Laurence Lundbladeda319282020-07-06 23:04:58 -0700576 uErr = CheckLabelAndType("Turbo", QCBOR_TYPE_ANY, &Item);
Laurence Lundblade1818e632020-07-26 04:14:08 -0700577 if(uErr == QCBOR_SUCCESS) {
578 if(Item.uDataType == QCBOR_TYPE_TRUE) {
579 pE->bTurboCharged = true;
580 } else if(Item.uDataType == QCBOR_TYPE_FALSE) {
581 pE->bTurboCharged = false;
582 } else {
583 return EngineProtocolerror;
584 }
585 continue;
586 } else if(uErr != QCBOR_ERR_NOT_FOUND){
587 return EngineProtocolerror;
588 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700589
590 /* Some label data item that is not known
591 (could just ignore extras data items) */
592 return EngineProtocolerror;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700593 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700594 uReturn = EngineSuccess;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700595
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700596Done:
Laurence Lundblade06c83042020-07-03 23:04:53 -0700597 return uReturn;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700598}
599
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700600
601
602
603
Laurence Lundblade1818e632020-07-26 04:14:08 -0700604int32_t RunQCborExample()
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700605{
Laurence Lundbladeda319282020-07-06 23:04:58 -0700606 CarEngine E, DecodedEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700607 MakeUsefulBufOnStack( EngineBuffer, 300);
608 UsefulBufC EncodedEngine;
609
610 MakeUsefulBufOnStack( InDefEngineBuffer, 300);
611 UsefulBufC InDefEncodedEngine;
612
Laurence Lundblade1818e632020-07-26 04:14:08 -0700613 // TODO: error codes and other clean up
614
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700615 EngineInit(&E);
616
617 EncodedEngine = EncodeEngine(&E, EngineBuffer);
618
619 printf("Engine Encoded in %zu bytes\n", EncodedEngine.len);
620
Laurence Lundbladeda319282020-07-06 23:04:58 -0700621 int x = (int)DecodeEngineAdvanced(EncodedEngine, &DecodedEngine);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700622 printf("Engine Decode Result: %d\n", x);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700623
624
625 InDefEncodedEngine = EncodeEngineIndefinteLen(&E, InDefEngineBuffer);
626
627 printf("Indef Engine Encoded in %zu bytes\n", InDefEncodedEngine.len);
628
Laurence Lundbladeda319282020-07-06 23:04:58 -0700629 x = (int)DecodeEngineAdvanced(InDefEncodedEngine, &DecodedEngine);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700630 printf("Indef Engine Decode Result: %d\n", x);
631
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700632 if(!EngineCompare(&E, &DecodedEngine)) {
633 printf("decode comparison fail\n");
634 }
635
Laurence Lundblade06c83042020-07-03 23:04:53 -0700636
637 x = (int)DecodeEngineBasic(EncodedEngine, &DecodedEngine);
638 printf("Engine Basic Decode Result: %d\n", x);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700639
640 if(!EngineCompare(&E, &DecodedEngine)) {
641 printf("decode comparison fail\n");
642 }
643
644
645 x = (int)DecodeEngineBasic(InDefEncodedEngine, &DecodedEngine);
646 printf("Indef Engine Basic Decode Result: %d\n", x);
647
648 if(!EngineCompare(&E, &DecodedEngine)) {
649 printf("indef decode comparison fail\n");
650 }
651
Laurence Lundbladeda319282020-07-06 23:04:58 -0700652 x = (int)DecodeEngineAdvancedFaster(EncodedEngine, &DecodedEngine);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700653 printf("Efficient Engine Basic Decode Result: %d\n", x);
654
655 if(!EngineCompare(&E, &DecodedEngine)) {
656 printf("effcieit decode comparison fail\n");
657 }
Laurence Lundblade1818e632020-07-26 04:14:08 -0700658
659 return 0;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700660}