blob: 39e6d556e168afba6eabc01aeac01195b413d01e [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"
Laurence Lundblade67257dc2020-07-27 03:33:37 -070018#include "qcbor/qcbor_spiffy_decode.h"
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070019
20#define MAX_CYLINDERS 16
21
Laurence Lundbladeda319282020-07-06 23:04:58 -070022
23/**
24 The data structure representing a car engine that is encoded and decoded in this examples.
25 */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070026typedef struct
27{
28 UsefulBufC Manufacturer;
Laurence Lundblade1818e632020-07-26 04:14:08 -070029 int64_t uDisplacement;
30 int64_t uHorsePower;
31 double dDesignedCompresion;
32 int64_t uNumCylinders;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070033 struct {
34 double uMeasuredCompression;
Laurence Lundblade1818e632020-07-26 04:14:08 -070035 } cylinders[MAX_CYLINDERS];
36 bool bTurboCharged;
Laurence Lundbladeda319282020-07-06 23:04:58 -070037} CarEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070038
39
Laurence Lundbladeda319282020-07-06 23:04:58 -070040/**
41 Initialize the Engine data structure with some values to encode/decode.
42 */
43void EngineInit(CarEngine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070044{
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070045 pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche");
46 pE->uDisplacement = 3296;
47 pE->uHorsePower = 210;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -070048 pE->dDesignedCompresion = 9.1;
Laurence Lundbladeda319282020-07-06 23:04:58 -070049 pE->uNumCylinders = 6;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070050 pE->cylinders[0].uMeasuredCompression = 9.0;
51 pE->cylinders[1].uMeasuredCompression = 9.2;
52 pE->cylinders[2].uMeasuredCompression = 8.9;
53 pE->cylinders[3].uMeasuredCompression = 8.9;
54 pE->cylinders[4].uMeasuredCompression = 9.1;
55 pE->cylinders[5].uMeasuredCompression = 9.0;
Laurence Lundbladeda319282020-07-06 23:04:58 -070056 pE->bTurboCharged = false;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070057}
58
59
Laurence Lundbladeda319282020-07-06 23:04:58 -070060/**
61 Return @c true if the two Engined data structures are exactly the same.
62 */
63bool EngineCompare(CarEngine *pE1, CarEngine *pE2)
Laurence Lundbladee6bbf552020-07-05 22:57:57 -070064{
65 if(pE1->uNumCylinders != pE2->uNumCylinders) {
66 return false;
67 }
68 if(pE1->bTurboCharged != pE2->bTurboCharged) {
69 return false;
70 }
71 if(pE1->uDisplacement != pE2->uDisplacement) {
72 return false;
73 }
74 if(pE1->uHorsePower != pE2->uHorsePower) {
75 return false;
76 }
77 if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) {
78 return false;
79 }
80 for(int64_t i = 0; i < pE2->uNumCylinders; i++) {
81 if(pE1->cylinders[i].uMeasuredCompression !=
82 pE2->cylinders[i].uMeasuredCompression) {
83 return false;
84 }
85 }
86
87 if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) {
88 return false;
89 }
90
91 return true;
92}
93
94
Laurence Lundbladeda319282020-07-06 23:04:58 -070095/**
96 @brief Encode an initialized Engine data structure in CBOR.
97
98 @param[in] pEngine The data structure to encode.
99 @param[in] Buffer Pointer and length of buffer to output to.
100
101 @return The pointer and length of the encoded CBOR or @ref NULLUsefulBufC on error.
102
103 @c Buffer must be big enough to hold the output. If it is not @ref NULLUsefulBufC
104 will be returned. @ref @ref NULLUsefulBufC will be returned for any other encoding
105 errors.
106
107 This encoding will use definite CBOR lengths.
108 */
109UsefulBufC EncodeEngine(const CarEngine *pEngine, UsefulBuf Buffer)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700110{
Laurence Lundblade06c83042020-07-03 23:04:53 -0700111 /* Initialize th encoder with the buffer big enough to hold the expected output.
112 If it is too small, QCBOREncode_Finish() will return an error. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700113 QCBOREncodeContext EncodeCtx;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700114 QCBOREncode_Init(&EncodeCtx, Buffer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700115
116 /* Proceed output all the items, letting the internal error
117 tracking do its work. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700118 QCBOREncode_OpenMap(&EncodeCtx);
119 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700120 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
121 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
122 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700123 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700124 QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700125 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700126 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
127 }
128 QCBOREncode_CloseArray(&EncodeCtx);
Laurence Lundbladeda319282020-07-06 23:04:58 -0700129 QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700130 QCBOREncode_CloseMap(&EncodeCtx);
131
Laurence Lundblade06c83042020-07-03 23:04:53 -0700132 /* Get the pointer and length of the encoded output. If there was
133 anny error it will be returned here. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700134 UsefulBufC EncodedCBOR;
135 QCBORError uErr;
136 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
137 if(uErr != QCBOR_SUCCESS) {
138 return NULLUsefulBufC;
139 } else {
140 return EncodedCBOR;
141 }
142}
143
144
Laurence Lundbladeda319282020-07-06 23:04:58 -0700145/**
146 @brief Encode an initialized Engine data structure in CBOR using indefinite lengths..
147
148 @param[in] pEngine The data structure to encode.
149 @param[in] Buffer Pointer and length of buffer to output to.
150
151 @return The pointer and length of the encoded CBOR or @ref NULLUsefulBufC on error.
152
153 This is virtually the same as EncodeEngine(). The encoded CBOR is slightly different as the
154 map and array use indefinite lengths, rather than definite lengths.
155
156 There is little practical use for this function as definite lengths are generally preferred for
157 CBOR and QCBOR always easily encodes definite lengths. (The advantage of indefinite
158 lengths are that they are simpler to encode, but that doesn't come into effect here).
159 */
160UsefulBufC EncodeEngineIndefinteLen(const CarEngine *pEngine, UsefulBuf Buffer)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700161{
162 QCBOREncodeContext EncodeCtx;
163
164 QCBOREncode_Init(&EncodeCtx, Buffer);
165 QCBOREncode_OpenMapIndefiniteLength(&EncodeCtx);
166 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700167 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
168 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700169 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700170 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700171 QCBOREncode_OpenArrayIndefiniteLengthInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700172 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700173 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
174 }
175 QCBOREncode_CloseArrayIndefiniteLength(&EncodeCtx);
Laurence Lundbladeda319282020-07-06 23:04:58 -0700176 QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700177 QCBOREncode_CloseMapIndefiniteLength(&EncodeCtx);
178
179 UsefulBufC EncodedCBOR;
180 QCBORError uErr;
181 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
182 if(uErr != QCBOR_SUCCESS) {
183 return NULLUsefulBufC;
184 } else {
185 return EncodedCBOR;
186 }
187}
188
189
Laurence Lundbladeda319282020-07-06 23:04:58 -0700190/**
191 Error results when decoding an Engine data structure.
192 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700193typedef enum {
194 EngineSuccess,
195 CBORNotWellFormed,
196 TooManyCylinders,
197 EngineProtocolerror,
198 WrongNumberOfCylinders
199} EngineDecodeErrors;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700200
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700201
Laurence Lundbladeda319282020-07-06 23:04:58 -0700202/**
203 Convert \ref QCBORError to \ref EngineDecodeErrors.
204 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700205EngineDecodeErrors ConvertError(QCBORError uErr)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700206{
Laurence Lundblade06c83042020-07-03 23:04:53 -0700207 EngineDecodeErrors uReturn;
208
209 switch(uErr)
210 {
211 case QCBOR_SUCCESS:
212 uReturn = EngineSuccess;
213 break;
214
215 case QCBOR_ERR_HIT_END:
216 uReturn = CBORNotWellFormed;
217 break;
218
219 default:
220 uReturn = EngineProtocolerror;
221 break;
222 }
223
224 return uReturn;
225}
226
227
Laurence Lundbladeda319282020-07-06 23:04:58 -0700228/**
229 @brief Simplest engine decode using advanced decoe features.
230
231 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
232 @param[out] pE The structure filled in from the decoding.
233
234 @return The decode error or success.
235
236 This verssion of the decoder has the simplest implementation, but
237 pulls in more code from the QCBOR library. This version uses
238 the most CPU because it scanns the all the CBOR each time
239 a data item is decoded. The CPU used for a data structure as small
240 as this is probably insignificant. CPU use for this style of decode is
241 probably only a factor on slow CPUs with big CBOR inputs.
242
243 Code size is yet to be measured, but this is probably the smallest total
244 code size if multiple protocols are being decoded in one application because
245 the complex parsing of a map and array is done be shared code from the
246 CBOR library rather than by individual protocol-specific chunks of code.
247 Similarly, this may be the smallest for complex CBOR with multiple
248 maps that need to be processed..
249
250 See also DecodeEngineAdvancedFaster() and DecodeEngineBasic().
Laurence Lundblade06c83042020-07-03 23:04:53 -0700251 */
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700252EngineDecodeErrors DecodeEngineSpiffy(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundblade06c83042020-07-03 23:04:53 -0700253{
254 QCBORError uErr;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700255 QCBORDecodeContext DecodeCtx;
256
257 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
258 QCBORDecode_EnterMap(&DecodeCtx);
259 QCBORDecode_GetTextInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
Laurence Lundblade06c83042020-07-03 23:04:53 -0700260 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
261 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700262 QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion));
Laurence Lundbladeda319282020-07-06 23:04:58 -0700263 QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700264
Laurence Lundblade06c83042020-07-03 23:04:53 -0700265 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700266
267 /* Must check error before referencing pE->uNumCylinders to be sure it
268 is valid. If any of the above errored, it won't be valid. */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700269 uErr = QCBORDecode_GetError(&DecodeCtx);
270 if(uErr != QCBOR_SUCCESS) {
271 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700272 }
273
274 if(pE->uNumCylinders > MAX_CYLINDERS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700275 return TooManyCylinders;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700276 }
277
278 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700279 for(int64_t i = 0; i < pE->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700280 QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700281 }
282 QCBORDecode_ExitArray(&DecodeCtx);
283 QCBORDecode_ExitMap(&DecodeCtx);
284
Laurence Lundblade06c83042020-07-03 23:04:53 -0700285 /* Catch the remainder of errors here */
286 uErr = QCBORDecode_Finish(&DecodeCtx);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700287
Laurence Lundblade06c83042020-07-03 23:04:53 -0700288Done:
289 return ConvertError(uErr);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700290}
291
Laurence Lundblade06c83042020-07-03 23:04:53 -0700292
Laurence Lundbladeda319282020-07-06 23:04:58 -0700293/**
294 @brief Simplest engine decode using advanced decoe features.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700295
Laurence Lundbladeda319282020-07-06 23:04:58 -0700296 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
297 @param[out] pE The structure filled in from the decoding.
298
299 @return The decode error or success.
300
Laurence Lundblade1818e632020-07-26 04:14:08 -0700301 This verssion of the decoder is still fairly simple and uses the
Laurence Lundbladeda319282020-07-06 23:04:58 -0700302 advanced decode features like DecodeEngine(), but is faster
303 and pulls in less library code. It is faster because all the items
304 except the array are pulled out of the map in one pass, rather
305 than multiple passes.
306
307 See also DecodeEngineAdvanced() and DecodeEngineBasic().
308*/
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700309EngineDecodeErrors DecodeEngineSpiffyFaster(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700310{
311 QCBORError uErr;
312 QCBORDecodeContext DecodeCtx;
313
314 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
315 QCBORDecode_EnterMap(&DecodeCtx);
316
Laurence Lundbladeda319282020-07-06 23:04:58 -0700317 QCBORItem EngineItems[7];
318 EngineItems[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
319 EngineItems[0].label.string = UsefulBuf_FROM_SZ_LITERAL("Manufacturer");
320 EngineItems[0].uDataType = QCBOR_TYPE_TEXT_STRING;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700321
Laurence Lundbladeda319282020-07-06 23:04:58 -0700322 EngineItems[1].uLabelType = QCBOR_TYPE_TEXT_STRING;
323 EngineItems[1].label.string = UsefulBuf_FROM_SZ_LITERAL("Displacement");
324 EngineItems[1].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700325
Laurence Lundbladeda319282020-07-06 23:04:58 -0700326 EngineItems[2].uLabelType = QCBOR_TYPE_TEXT_STRING;
327 EngineItems[2].label.string = UsefulBuf_FROM_SZ_LITERAL("Horsepower");
328 EngineItems[2].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700329
Laurence Lundbladeda319282020-07-06 23:04:58 -0700330 EngineItems[3].uLabelType = QCBOR_TYPE_TEXT_STRING;
331 EngineItems[3].label.string = UsefulBuf_FROM_SZ_LITERAL("DesignedCompression");
332 EngineItems[3].uDataType = QCBOR_TYPE_DOUBLE;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700333
Laurence Lundbladeda319282020-07-06 23:04:58 -0700334 EngineItems[4].uLabelType = QCBOR_TYPE_TEXT_STRING;
335 EngineItems[4].label.string = UsefulBuf_FROM_SZ_LITERAL("Turbo");
336 EngineItems[4].uDataType = QCBOR_TYPE_ANY;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700337
Laurence Lundbladeda319282020-07-06 23:04:58 -0700338 EngineItems[5].uLabelType = QCBOR_TYPE_TEXT_STRING;
339 EngineItems[5].label.string = UsefulBuf_FROM_SZ_LITERAL("NumCylinders");
340 EngineItems[5].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700341
Laurence Lundbladeda319282020-07-06 23:04:58 -0700342 EngineItems[6].uLabelType = QCBOR_TYPE_NONE;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700343
Laurence Lundblade5f53f832020-09-03 12:00:14 -0700344 QCBORDecode_GetItemsInMap(&DecodeCtx, EngineItems);
345 uErr = QCBORDecode_GetError(&DecodeCtx);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700346 if(uErr != QCBOR_SUCCESS) {
347 goto Done;
348 }
349
Laurence Lundbladeda319282020-07-06 23:04:58 -0700350 pE->Manufacturer = EngineItems[0].val.string;
351 pE->uDisplacement = EngineItems[1].val.int64;
352 pE->uHorsePower = EngineItems[2].val.int64;
353 pE->dDesignedCompresion = EngineItems[3].val.dfnum;
354 pE->uNumCylinders = EngineItems[5].val.int64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700355
Laurence Lundbladeda319282020-07-06 23:04:58 -0700356 if(EngineItems[4].uDataType == QCBOR_TYPE_TRUE) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700357 pE->bTurboCharged = true;
Laurence Lundbladeda319282020-07-06 23:04:58 -0700358 } else if(EngineItems[4].uDataType == QCBOR_TYPE_FALSE) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700359 pE->bTurboCharged = false;
360 } else {
361 return EngineProtocolerror;
362 }
363
364
365 /* Must check error before referencing pE->uNumCylinders to be sure it
366 is valid. If any of the above errored, it won't be valid. */
367 uErr = QCBORDecode_GetError(&DecodeCtx);
368 if(uErr != QCBOR_SUCCESS) {
369 goto Done;
370 }
371
372 if(pE->uNumCylinders > MAX_CYLINDERS) {
373 return TooManyCylinders;
374 }
375
376 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
377 for(int64_t i = 0; i < pE->uNumCylinders; i++) {
378 QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
379 }
380 QCBORDecode_ExitArray(&DecodeCtx);
381 QCBORDecode_ExitMap(&DecodeCtx);
382
383 /* Catch the remainder of errors here */
384 uErr = QCBORDecode_Finish(&DecodeCtx);
385
386Done:
387 return ConvertError(uErr);
388}
389
390
391
392
Laurence Lundblade06c83042020-07-03 23:04:53 -0700393
Laurence Lundblade1818e632020-07-26 04:14:08 -0700394/**
395 @brief Check the type and lable of an item.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700396
Laurence Lundblade1818e632020-07-26 04:14:08 -0700397 @param[in] szLabel The expected string label.
398 @param[in] uQCBORType The expected type or @c QCBOR_TYPE_ANY
399 @param[in] pItem The item to check.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700400
Laurence Lundblade1818e632020-07-26 04:14:08 -0700401 @retval QCBOR_ERR_NOT_FOUND The label doesn't match.
402 @retval QCBOR_ERR_UNEXPECTED_TYPE The label matches, but the type is not as expected.
403 @retval QCBOR_SUCCESS Both label and type match.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700404 */
Laurence Lundblade1818e632020-07-26 04:14:08 -0700405QCBORError CheckLabelAndType(const char *szLabel, uint8_t uQCBORType, const QCBORItem *pItem)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700406{
407 if(pItem->uLabelType != QCBOR_TYPE_TEXT_STRING) {
408 return QCBOR_ERR_NOT_FOUND;
409 }
410
411 UsefulBufC Label = UsefulBuf_FromSZ(szLabel);
412
Laurence Lundblade06c83042020-07-03 23:04:53 -0700413 if(UsefulBuf_Compare(Label, pItem->label.string)) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700414 return QCBOR_ERR_NOT_FOUND;
415 }
416
Laurence Lundblade06c83042020-07-03 23:04:53 -0700417 if(pItem->uDataType != uQCBORType && uQCBORType != QCBOR_TYPE_ANY) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700418 return QCBOR_ERR_UNEXPECTED_TYPE;
419 }
420
421 return QCBOR_SUCCESS;
422}
423
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700424
Laurence Lundblade1818e632020-07-26 04:14:08 -0700425/**
426 @brief Decode the array of engine cylinders.
427
428 @param[in] pDecodeCtx The decode context from which to get items.
429 @param[out] pE The structure filled in from the decoding.
430 @param[in] pItem The data item that is the start of the array.
431
432 @return Either @ref EngineSuccess or an error.
433
434 This always consumes the whole array. If it has the wrong number of
435 items in it, an error is returned.
436 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700437EngineDecodeErrors DecodeCylinders(QCBORDecodeContext *pDecodeCtx,
Laurence Lundbladeda319282020-07-06 23:04:58 -0700438 CarEngine *pE,
Laurence Lundblade06c83042020-07-03 23:04:53 -0700439 const QCBORItem *pItem)
440{
441 int i = 0;
442 QCBORItem Item;
443
Laurence Lundblade1818e632020-07-26 04:14:08 -0700444 /* Loop getting all the items in the array. This uses
445 nesting level to detect the end so it works for both
446 definite and indefinite length arrays. */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700447 do {
448 QCBORError uErr;
449
450 uErr = QCBORDecode_GetNext(pDecodeCtx, &Item);
451 if(uErr != QCBOR_SUCCESS) {
452 return CBORNotWellFormed;
453 }
454 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
455 return CBORNotWellFormed;
456 }
457
458 if(i < MAX_CYLINDERS) {
459 pE->cylinders[i].uMeasuredCompression = Item.val.dfnum;
460 i++;
461 }
462
463 } while (Item.uNextNestLevel == pItem->uNextNestLevel);
464
465 if(i != pE->uNumCylinders) {
466 return WrongNumberOfCylinders;
467 } else {
468 return EngineSuccess;
469 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700470}
471
Laurence Lundblade06c83042020-07-03 23:04:53 -0700472
Laurence Lundbladeda319282020-07-06 23:04:58 -0700473/**
Laurence Lundblade1818e632020-07-26 04:14:08 -0700474 @brief Engine decode without advanced decode features.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700475
Laurence Lundblade1818e632020-07-26 04:14:08 -0700476 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
477 @param[out] pE The structure filled in from the decoding.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700478
Laurence Lundblade1818e632020-07-26 04:14:08 -0700479 @return The decode error or success.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700480
Laurence Lundblade1818e632020-07-26 04:14:08 -0700481 This version of the deocde is the most complex, but uses
Laurence Lundbladeda319282020-07-06 23:04:58 -0700482 significantly less code from the QCBOR library. It is also
483 the most CPU-efficient since it does only one pass
484 through the CBOR.
485
Laurence Lundblade1818e632020-07-26 04:14:08 -0700486 Code size is yet to be measured, but this is probably the smallest total
Laurence Lundbladeda319282020-07-06 23:04:58 -0700487 code size of all three, if just one CBOR protocol is being decoded. If
488 multiple protocols are being decoded the other options.
489
490 See also DecodeEngineAdvanced() and DecodeEngineAdvancedFaster().
491*/
492EngineDecodeErrors DecodeEngineBasic(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700493{
494 QCBORDecodeContext DecodeCtx;
495
496 QCBORDecode_Init(&DecodeCtx, EncodedEngine, 0);// TODO: fill in mode;
497
498 QCBORItem Item;
499 QCBORError uErr;
Laurence Lundblade06c83042020-07-03 23:04:53 -0700500 EngineDecodeErrors uReturn;
501
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700502
503 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
504 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700505 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700506 goto Done;
507 }
508 if(Item.uDataType != QCBOR_TYPE_MAP) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700509 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700510 goto Done;
511 }
512
513 while(1) {
514 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
515 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700516 if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
517 break; /* Non-error exit from the loop */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700518 } else {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700519 uReturn = CBORNotWellFormed;
520 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700521 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700522 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700523
Laurence Lundblade06c83042020-07-03 23:04:53 -0700524 uErr = CheckLabelAndType("Manufacturer", QCBOR_TYPE_TEXT_STRING, &Item);
525 if(uErr == QCBOR_SUCCESS) {
526 pE->Manufacturer = Item.val.string;
527 continue;
528 } else if(uErr != QCBOR_ERR_NOT_FOUND){
529 /* Maunfacturer field missing or badly formed */
530 return EngineProtocolerror;
531 } /* continue on and try for another match */
532
Laurence Lundblade06c83042020-07-03 23:04:53 -0700533 uErr = CheckLabelAndType("NumCylinders", QCBOR_TYPE_INT64, &Item);
534 if(uErr == QCBOR_SUCCESS) {
535 if(Item.val.int64 > MAX_CYLINDERS) {
536 return TooManyCylinders;
537 } else {
538 pE->uNumCylinders = (uint8_t)Item.val.int64;
539 continue;
540 }
541 } else if(uErr != QCBOR_ERR_NOT_FOUND){
Laurence Lundblade1818e632020-07-26 04:14:08 -0700542 /* NumCylinders field missing or badly formed */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700543 return EngineProtocolerror;
Laurence Lundblade1818e632020-07-26 04:14:08 -0700544 } /* continue on and try for another match */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700545
546 uErr = CheckLabelAndType("Cylinders", QCBOR_TYPE_ARRAY, &Item);
547 if(uErr == QCBOR_SUCCESS) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700548 DecodeCylinders(&DecodeCtx, pE, &Item);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700549 continue;
550 } else if(uErr != QCBOR_ERR_NOT_FOUND){
551 return EngineProtocolerror;
552 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700553
Laurence Lundblade06c83042020-07-03 23:04:53 -0700554 uErr = CheckLabelAndType("Displacement", QCBOR_TYPE_INT64, &Item);
555 if(uErr == QCBOR_SUCCESS) {
556 pE->uDisplacement = Item.val.int64;
557 continue;
558 } else if(uErr != QCBOR_ERR_NOT_FOUND){
559 return EngineProtocolerror;
560 }
561
562 uErr = CheckLabelAndType("Horsepower", QCBOR_TYPE_INT64, &Item);
563 if(uErr == QCBOR_SUCCESS) {
564 pE->uHorsePower = Item.val.int64;
565 continue;
566 } else if(uErr != QCBOR_ERR_NOT_FOUND){
567 return EngineProtocolerror;
568 }
569
570 uErr = CheckLabelAndType("DesignedCompression", QCBOR_TYPE_DOUBLE, &Item);
571 if(uErr == QCBOR_SUCCESS) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700572 pE->dDesignedCompresion = Item.val.dfnum;
Laurence Lundblade06c83042020-07-03 23:04:53 -0700573 continue;
574 } else if(uErr != QCBOR_ERR_NOT_FOUND){
575 return EngineProtocolerror;
576 }
577
Laurence Lundbladeda319282020-07-06 23:04:58 -0700578 uErr = CheckLabelAndType("Turbo", QCBOR_TYPE_ANY, &Item);
Laurence Lundblade1818e632020-07-26 04:14:08 -0700579 if(uErr == QCBOR_SUCCESS) {
580 if(Item.uDataType == QCBOR_TYPE_TRUE) {
581 pE->bTurboCharged = true;
582 } else if(Item.uDataType == QCBOR_TYPE_FALSE) {
583 pE->bTurboCharged = false;
584 } else {
585 return EngineProtocolerror;
586 }
587 continue;
588 } else if(uErr != QCBOR_ERR_NOT_FOUND){
589 return EngineProtocolerror;
590 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700591
592 /* Some label data item that is not known
593 (could just ignore extras data items) */
594 return EngineProtocolerror;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700595 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700596 uReturn = EngineSuccess;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700597
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700598Done:
Laurence Lundblade06c83042020-07-03 23:04:53 -0700599 return uReturn;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700600}
601
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700602
603
604
605
Laurence Lundblade1818e632020-07-26 04:14:08 -0700606int32_t RunQCborExample()
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700607{
Laurence Lundbladeda319282020-07-06 23:04:58 -0700608 CarEngine E, DecodedEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700609 MakeUsefulBufOnStack( EngineBuffer, 300);
610 UsefulBufC EncodedEngine;
611
612 MakeUsefulBufOnStack( InDefEngineBuffer, 300);
613 UsefulBufC InDefEncodedEngine;
614
Laurence Lundblade1818e632020-07-26 04:14:08 -0700615 // TODO: error codes and other clean up
616
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700617 EngineInit(&E);
618
619 EncodedEngine = EncodeEngine(&E, EngineBuffer);
620
621 printf("Engine Encoded in %zu bytes\n", EncodedEngine.len);
622
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700623 int x = (int)DecodeEngineSpiffy(EncodedEngine, &DecodedEngine);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700624 printf("Engine Decode Result: %d\n", x);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700625
626
627 InDefEncodedEngine = EncodeEngineIndefinteLen(&E, InDefEngineBuffer);
628
629 printf("Indef Engine Encoded in %zu bytes\n", InDefEncodedEngine.len);
630
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700631 x = (int)DecodeEngineSpiffy(InDefEncodedEngine, &DecodedEngine);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700632 printf("Indef Engine Decode Result: %d\n", x);
633
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700634 if(!EngineCompare(&E, &DecodedEngine)) {
635 printf("decode comparison fail\n");
636 }
637
Laurence Lundblade06c83042020-07-03 23:04:53 -0700638
639 x = (int)DecodeEngineBasic(EncodedEngine, &DecodedEngine);
640 printf("Engine Basic Decode Result: %d\n", x);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700641
642 if(!EngineCompare(&E, &DecodedEngine)) {
643 printf("decode comparison fail\n");
644 }
645
646
647 x = (int)DecodeEngineBasic(InDefEncodedEngine, &DecodedEngine);
648 printf("Indef Engine Basic Decode Result: %d\n", x);
649
650 if(!EngineCompare(&E, &DecodedEngine)) {
651 printf("indef decode comparison fail\n");
652 }
653
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700654 x = (int)DecodeEngineSpiffyFaster(EncodedEngine, &DecodedEngine);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700655 printf("Efficient Engine Basic Decode Result: %d\n", x);
656
657 if(!EngineCompare(&E, &DecodedEngine)) {
658 printf("effcieit decode comparison fail\n");
659 }
Laurence Lundblade1818e632020-07-26 04:14:08 -0700660
661 return 0;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700662}