blob: 042c3b3ded9b4f48ba7f43152c6bb0eb9835e846 [file] [log] [blame]
Laurence Lundbladec4474172020-10-02 14:52:16 -07001/* =========================================================================
2 example.c -- Example code for QCBOR
Laurence Lundbladed4cd7232020-07-03 19:30:48 -07003
Laurence Lundbladec4474172020-10-02 14:52:16 -07004 Copyright (c) 2020, Laurence Lundblade. All rights reserved.
Laurence Lundbladed4cd7232020-07-03 19:30:48 -07005
Laurence Lundbladec4474172020-10-02 14:52:16 -07006 SPDX-License-Identifier: BSD-3-Clause
Laurence Lundbladed4cd7232020-07-03 19:30:48 -07007
Laurence Lundbladec4474172020-10-02 14:52:16 -07008 See BSD-3-Clause license in README.md
Laurence Lundbladed4cd7232020-07-03 19:30:48 -07009
Laurence Lundbladec4474172020-10-02 14:52:16 -070010 Created on 6/30/2020
11 ========================================================================== */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070012
13#include <stdio.h>
14#include "example.h"
15#include "qcbor/qcbor_encode.h"
16#include "qcbor/qcbor_decode.h"
Laurence Lundblade67257dc2020-07-27 03:33:37 -070017#include "qcbor/qcbor_spiffy_decode.h"
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070018
Laurence Lundbladec4474172020-10-02 14:52:16 -070019
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070020#define MAX_CYLINDERS 16
21
Laurence Lundbladeda319282020-07-06 23:04:58 -070022/**
Laurence Lundbladec4474172020-10-02 14:52:16 -070023 The data structure representing a car engine that is encoded and
24 decoded in this example.
Laurence Lundbladeda319282020-07-06 23:04:58 -070025 */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070026typedef struct
27{
Laurence Lundbladec4474172020-10-02 14:52:16 -070028 UsefulBufC Manufacturer;
29 int64_t uDisplacement;
30 int64_t uHorsePower;
31 double dDesignedCompresion;
32 int64_t uNumCylinders;
33 bool bTurboCharged;
34 struct {
35 double uMeasuredCompression;
36 } cylinders[MAX_CYLINDERS];
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/**
Laurence Lundbladec4474172020-10-02 14:52:16 -070041 @brief Initialize the Engine data structure with values to encode/decode.
42
43 @param[out] pE The Engine structure to fill in
Laurence Lundbladeda319282020-07-06 23:04:58 -070044 */
45void EngineInit(CarEngine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070046{
Laurence Lundbladec4474172020-10-02 14:52:16 -070047 pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche");
48 pE->uDisplacement = 3296;
49 pE->uHorsePower = 210;
50 pE->dDesignedCompresion = 9.1;
51 pE->uNumCylinders = 6;
52 pE->bTurboCharged = false;
53
54 pE->cylinders[0].uMeasuredCompression = 9.0;
55 pE->cylinders[1].uMeasuredCompression = 9.2;
56 pE->cylinders[2].uMeasuredCompression = 8.9;
57 pE->cylinders[3].uMeasuredCompression = 8.9;
58 pE->cylinders[4].uMeasuredCompression = 9.1;
59 pE->cylinders[5].uMeasuredCompression = 9.0;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070060}
61
62
Laurence Lundbladeda319282020-07-06 23:04:58 -070063/**
Laurence Lundbladec4474172020-10-02 14:52:16 -070064 @brief Compare two Engine structure for equality.
65
66 @param[in] pE1 First Engine to compare.
67 @param[in] pE2 Second Engine to compare.
68
69 @retval Return @c true if the two Engine data structures are exactly the
70 same.
Laurence Lundbladeda319282020-07-06 23:04:58 -070071 */
72bool EngineCompare(CarEngine *pE1, CarEngine *pE2)
Laurence Lundbladee6bbf552020-07-05 22:57:57 -070073{
74 if(pE1->uNumCylinders != pE2->uNumCylinders) {
75 return false;
76 }
77 if(pE1->bTurboCharged != pE2->bTurboCharged) {
78 return false;
79 }
80 if(pE1->uDisplacement != pE2->uDisplacement) {
81 return false;
82 }
83 if(pE1->uHorsePower != pE2->uHorsePower) {
84 return false;
85 }
86 if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) {
87 return false;
88 }
89 for(int64_t i = 0; i < pE2->uNumCylinders; i++) {
90 if(pE1->cylinders[i].uMeasuredCompression !=
91 pE2->cylinders[i].uMeasuredCompression) {
92 return false;
93 }
94 }
95
96 if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) {
97 return false;
98 }
99
100 return true;
101}
102
103
Laurence Lundbladec4474172020-10-02 14:52:16 -0700104#ifndef EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE
Laurence Lundbladeda319282020-07-06 23:04:58 -0700105/**
106 @brief Encode an initialized Engine data structure in CBOR.
107
108 @param[in] pEngine The data structure to encode.
Laurence Lundbladec4474172020-10-02 14:52:16 -0700109 @param[in] Buffer Pointer and length of buffer to output to.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700110
Laurence Lundbladec4474172020-10-02 14:52:16 -0700111 @return The pointer and length of the encoded CBOR or
112 @ref NULLUsefulBufC on error.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700113
Laurence Lundbladec4474172020-10-02 14:52:16 -0700114 This is a simple CBOR encoding example. It outputs the Engine data
115 structure as a map of label-value pairs as well as an array of
116 floating point values.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700117
Laurence Lundbladec4474172020-10-02 14:52:16 -0700118 @c Buffer must be big enough to hold the output. If it is not @ref
119 NULLUsefulBufC will be returned. @ref @ref NULLUsefulBufC will be
120 returned for any other encoding errors.
121
122 This encoding will use definite CBOR lengths. Definite lengths are
123 preferred in CBOR. See EncodeEngineIndefinteLen() that encodes using
124 indefinite lengths.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700125 */
Laurence Lundbladec4474172020-10-02 14:52:16 -0700126UsefulBufC EncodeEngineDefiniteLength(const CarEngine *pEngine, UsefulBuf Buffer)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700127{
Laurence Lundbladec4474172020-10-02 14:52:16 -0700128 /* Initialize the encoder with the buffer big enough to hold the
129 expected output. If it is too small, QCBOREncode_Finish() will
130 return an error. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700131 QCBOREncodeContext EncodeCtx;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700132 QCBOREncode_Init(&EncodeCtx, Buffer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700133
Laurence Lundbladec4474172020-10-02 14:52:16 -0700134 /* Proceed to output all the items, letting the internal error
Laurence Lundblade06c83042020-07-03 23:04:53 -0700135 tracking do its work. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700136 QCBOREncode_OpenMap(&EncodeCtx);
137 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700138 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
139 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
140 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700141 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700142 QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700143 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700144 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
145 }
146 QCBOREncode_CloseArray(&EncodeCtx);
Laurence Lundbladeda319282020-07-06 23:04:58 -0700147 QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700148 QCBOREncode_CloseMap(&EncodeCtx);
149
Laurence Lundblade06c83042020-07-03 23:04:53 -0700150 /* Get the pointer and length of the encoded output. If there was
Laurence Lundbladec4474172020-10-02 14:52:16 -0700151 any error it will be returned here. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700152 UsefulBufC EncodedCBOR;
153 QCBORError uErr;
154 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
155 if(uErr != QCBOR_SUCCESS) {
Laurence Lundbladec4474172020-10-02 14:52:16 -0700156 return NULLUsefulBufC;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700157 } else {
158 return EncodedCBOR;
159 }
160}
Laurence Lundbladec4474172020-10-02 14:52:16 -0700161#endif /* EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700162
163
Laurence Lundbladec4474172020-10-02 14:52:16 -0700164
165
166#ifndef EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE_ENCODE
Laurence Lundbladeda319282020-07-06 23:04:58 -0700167/**
Laurence Lundbladec4474172020-10-02 14:52:16 -0700168 @brief Encode an initialized Engine data structure using indefinite lengths.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700169
170 @param[in] pEngine The data structure to encode.
Laurence Lundbladec4474172020-10-02 14:52:16 -0700171 @param[in] Buffer Pointer and length of buffer to output to.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700172
Laurence Lundbladec4474172020-10-02 14:52:16 -0700173 @return The pointer and length of the encoded CBOR or
174 @ref NULLUsefulBufC on error.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700175
Laurence Lundbladec4474172020-10-02 14:52:16 -0700176 This is virtually the same as EncodeEngineDefiniteLength(). The
177 encoded CBOR is slightly different as the map and array use
178 indefinite lengths, rather than definite lengths.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700179
Laurence Lundbladec4474172020-10-02 14:52:16 -0700180 A definite length array is encoded as an integer indicating the
181 number of items in it. An indefinite length array is encoded as an
182 opening byte, the items in it and a "break" byte to end
183 it. Indefinite length arrays and maps are easier to encode, but
184 harder to decode.
185
186 The advantage of this implementation is that the encoding side will
187 be a little less object code. (Eventually QCBOR will an ifdef to
188 disable definite length encoding and the object code will be even
189 smaller). However, note that the encoding implementation for a
190 protocol is just about always much smaller than the decoding
191 implementation and that code savings for use of indefinite lengths is
192 relatively small.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700193 */
194UsefulBufC EncodeEngineIndefinteLen(const CarEngine *pEngine, UsefulBuf Buffer)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700195{
196 QCBOREncodeContext EncodeCtx;
197
198 QCBOREncode_Init(&EncodeCtx, Buffer);
199 QCBOREncode_OpenMapIndefiniteLength(&EncodeCtx);
200 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700201 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
202 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700203 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700204 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700205 QCBOREncode_OpenArrayIndefiniteLengthInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700206 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700207 QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
208 }
209 QCBOREncode_CloseArrayIndefiniteLength(&EncodeCtx);
Laurence Lundbladeda319282020-07-06 23:04:58 -0700210 QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700211 QCBOREncode_CloseMapIndefiniteLength(&EncodeCtx);
212
213 UsefulBufC EncodedCBOR;
214 QCBORError uErr;
215 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
216 if(uErr != QCBOR_SUCCESS) {
217 return NULLUsefulBufC;
218 } else {
219 return EncodedCBOR;
220 }
221}
Laurence Lundbladec4474172020-10-02 14:52:16 -0700222#endif /* EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700223
224
Laurence Lundbladeda319282020-07-06 23:04:58 -0700225/**
226 Error results when decoding an Engine data structure.
227 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700228typedef enum {
229 EngineSuccess,
230 CBORNotWellFormed,
231 TooManyCylinders,
232 EngineProtocolerror,
233 WrongNumberOfCylinders
234} EngineDecodeErrors;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700235
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700236
Laurence Lundbladeda319282020-07-06 23:04:58 -0700237/**
Laurence Lundbladec4474172020-10-02 14:52:16 -0700238 Convert @ref QCBORError to @ref EngineDecodeErrors.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700239 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700240EngineDecodeErrors ConvertError(QCBORError uErr)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700241{
Laurence Lundblade06c83042020-07-03 23:04:53 -0700242 EngineDecodeErrors uReturn;
243
244 switch(uErr)
245 {
246 case QCBOR_SUCCESS:
247 uReturn = EngineSuccess;
248 break;
249
250 case QCBOR_ERR_HIT_END:
251 uReturn = CBORNotWellFormed;
252 break;
253
254 default:
255 uReturn = EngineProtocolerror;
256 break;
257 }
258
259 return uReturn;
260}
261
262
Laurence Lundbladec4474172020-10-02 14:52:16 -0700263#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE
Laurence Lundbladeda319282020-07-06 23:04:58 -0700264/**
Laurence Lundbladec4474172020-10-02 14:52:16 -0700265 @brief Simplest engine decode using spiffy decode features.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700266
267 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
Laurence Lundbladec4474172020-10-02 14:52:16 -0700268 @param[out] pE The structure filled in from the decoding.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700269
270 @return The decode error or success.
271
Laurence Lundbladec4474172020-10-02 14:52:16 -0700272 This decodes the CBOR into the engine structure.
273
274 As QCBOR automatically supports both definite and indefinite maps and
275 arrays, this will decode either.
276
277 This uses QCBOR's spiffy decode, so the implementation is simplest
278 and closely parallels the encode implementation in
279 EncodeEngineDefiniteLength().
280
281 See two other ways to implement decoding in
282 DecodeEngineSpiffyFaster() and DecodeEngineBasic().
283
284 This version of the decoder has the simplest implementation, but
285 pulls in more code from the QCBOR library. This version uses the
286 most CPU cycles because it scans the all the CBOR each time a data
287 item is decoded. The CPU cycles used for a data structure as small as
288 this is probably insignificant. CPU use for this style of decode is
Laurence Lundbladeda319282020-07-06 23:04:58 -0700289 probably only a factor on slow CPUs with big CBOR inputs.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700290 */
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700291EngineDecodeErrors DecodeEngineSpiffy(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundblade06c83042020-07-03 23:04:53 -0700292{
293 QCBORError uErr;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700294 QCBORDecodeContext DecodeCtx;
295
296 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
Laurence Lundblade6545d1b2020-10-14 11:13:13 -0700297 QCBORDecode_EnterMap(&DecodeCtx, NULL);
Laurence Lundblade323f8a92020-09-06 19:43:09 -0700298 QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
Laurence Lundblade06c83042020-07-03 23:04:53 -0700299 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
300 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700301 QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion));
Laurence Lundbladeda319282020-07-06 23:04:58 -0700302 QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700303
Laurence Lundblade06c83042020-07-03 23:04:53 -0700304 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700305
Laurence Lundbladec4474172020-10-02 14:52:16 -0700306 /* Must check error before referencing pE->uNumCylinders to be
307 sure it is valid. If any of the above errored, it won't be
308 valid. */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700309 uErr = QCBORDecode_GetError(&DecodeCtx);
310 if(uErr != QCBOR_SUCCESS) {
311 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700312 }
313
314 if(pE->uNumCylinders > MAX_CYLINDERS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700315 return TooManyCylinders;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700316 }
317
318 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700319 for(int64_t i = 0; i < pE->uNumCylinders; i++) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700320 QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700321 }
322 QCBORDecode_ExitArray(&DecodeCtx);
323 QCBORDecode_ExitMap(&DecodeCtx);
324
Laurence Lundblade06c83042020-07-03 23:04:53 -0700325 /* Catch the remainder of errors here */
326 uErr = QCBORDecode_Finish(&DecodeCtx);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700327
Laurence Lundblade06c83042020-07-03 23:04:53 -0700328Done:
329 return ConvertError(uErr);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700330}
331
Laurence Lundbladec4474172020-10-02 14:52:16 -0700332#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700333
Laurence Lundbladec4474172020-10-02 14:52:16 -0700334
335
336#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE_FAST
Laurence Lundbladeda319282020-07-06 23:04:58 -0700337/**
Laurence Lundbladec4474172020-10-02 14:52:16 -0700338 @brief Decode an Engine structure with the faster spiffy decode features.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700339
Laurence Lundbladeda319282020-07-06 23:04:58 -0700340 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
Laurence Lundbladec4474172020-10-02 14:52:16 -0700341 @param[out] pE The structure filled in from the decoding.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700342
343 @return The decode error or success.
344
Laurence Lundbladec4474172020-10-02 14:52:16 -0700345 This decodes the same as DecodeEngineSpiffy(), but uses different
346 spiffy decode features.
347
348 This version uses QCBORDecode_GetItemsInMap() which uses less CPU
349 cycles because all the items except the array are pulled out of the
350 map in one pass, rather than having to decode the whole map for each
351 decoded item. This also pulls in less object code from the QCBOR
352 library.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700353
354 See also DecodeEngineAdvanced() and DecodeEngineBasic().
355*/
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700356EngineDecodeErrors DecodeEngineSpiffyFaster(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700357{
358 QCBORError uErr;
359 QCBORDecodeContext DecodeCtx;
360
361 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
Laurence Lundblade6545d1b2020-10-14 11:13:13 -0700362 QCBORDecode_EnterMap(&DecodeCtx, NULL);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700363
Laurence Lundbladeda319282020-07-06 23:04:58 -0700364 QCBORItem EngineItems[7];
365 EngineItems[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
366 EngineItems[0].label.string = UsefulBuf_FROM_SZ_LITERAL("Manufacturer");
367 EngineItems[0].uDataType = QCBOR_TYPE_TEXT_STRING;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700368
Laurence Lundbladeda319282020-07-06 23:04:58 -0700369 EngineItems[1].uLabelType = QCBOR_TYPE_TEXT_STRING;
370 EngineItems[1].label.string = UsefulBuf_FROM_SZ_LITERAL("Displacement");
371 EngineItems[1].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700372
Laurence Lundbladeda319282020-07-06 23:04:58 -0700373 EngineItems[2].uLabelType = QCBOR_TYPE_TEXT_STRING;
374 EngineItems[2].label.string = UsefulBuf_FROM_SZ_LITERAL("Horsepower");
375 EngineItems[2].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700376
Laurence Lundbladeda319282020-07-06 23:04:58 -0700377 EngineItems[3].uLabelType = QCBOR_TYPE_TEXT_STRING;
378 EngineItems[3].label.string = UsefulBuf_FROM_SZ_LITERAL("DesignedCompression");
379 EngineItems[3].uDataType = QCBOR_TYPE_DOUBLE;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700380
Laurence Lundbladeda319282020-07-06 23:04:58 -0700381 EngineItems[4].uLabelType = QCBOR_TYPE_TEXT_STRING;
382 EngineItems[4].label.string = UsefulBuf_FROM_SZ_LITERAL("Turbo");
383 EngineItems[4].uDataType = QCBOR_TYPE_ANY;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700384
Laurence Lundbladeda319282020-07-06 23:04:58 -0700385 EngineItems[5].uLabelType = QCBOR_TYPE_TEXT_STRING;
386 EngineItems[5].label.string = UsefulBuf_FROM_SZ_LITERAL("NumCylinders");
387 EngineItems[5].uDataType = QCBOR_TYPE_INT64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700388
Laurence Lundbladeda319282020-07-06 23:04:58 -0700389 EngineItems[6].uLabelType = QCBOR_TYPE_NONE;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700390
Laurence Lundblade5f53f832020-09-03 12:00:14 -0700391 QCBORDecode_GetItemsInMap(&DecodeCtx, EngineItems);
392 uErr = QCBORDecode_GetError(&DecodeCtx);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700393 if(uErr != QCBOR_SUCCESS) {
394 goto Done;
395 }
396
Laurence Lundbladeda319282020-07-06 23:04:58 -0700397 pE->Manufacturer = EngineItems[0].val.string;
398 pE->uDisplacement = EngineItems[1].val.int64;
399 pE->uHorsePower = EngineItems[2].val.int64;
400 pE->dDesignedCompresion = EngineItems[3].val.dfnum;
401 pE->uNumCylinders = EngineItems[5].val.int64;
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700402
Laurence Lundbladeda319282020-07-06 23:04:58 -0700403 if(EngineItems[4].uDataType == QCBOR_TYPE_TRUE) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700404 pE->bTurboCharged = true;
Laurence Lundbladeda319282020-07-06 23:04:58 -0700405 } else if(EngineItems[4].uDataType == QCBOR_TYPE_FALSE) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700406 pE->bTurboCharged = false;
407 } else {
408 return EngineProtocolerror;
409 }
410
411
Laurence Lundbladec4474172020-10-02 14:52:16 -0700412 /* Must check error before referencing pE->uNumCylinders to be
413 sure it is valid. If any of the above errored, it won't be
414 valid. */
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700415 uErr = QCBORDecode_GetError(&DecodeCtx);
416 if(uErr != QCBOR_SUCCESS) {
417 goto Done;
418 }
419
420 if(pE->uNumCylinders > MAX_CYLINDERS) {
421 return TooManyCylinders;
422 }
423
424 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
425 for(int64_t i = 0; i < pE->uNumCylinders; i++) {
426 QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
427 }
428 QCBORDecode_ExitArray(&DecodeCtx);
429 QCBORDecode_ExitMap(&DecodeCtx);
430
431 /* Catch the remainder of errors here */
432 uErr = QCBORDecode_Finish(&DecodeCtx);
433
434Done:
435 return ConvertError(uErr);
436}
437
Laurence Lundbladec4474172020-10-02 14:52:16 -0700438#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE_FAST */
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700439
440
Laurence Lundbladec4474172020-10-02 14:52:16 -0700441#ifndef EXAMPLE_DISABLE_BASIC_DECODE
Laurence Lundblade1818e632020-07-26 04:14:08 -0700442/**
443 @brief Check the type and lable of an item.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700444
Laurence Lundbladec4474172020-10-02 14:52:16 -0700445 @param[in] szLabel The expected string label.
446 @param[in] uQCBORType The expected type or @c QCBOR_TYPE_ANY.
447 @param[in] pItem The item to check.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700448
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700449 @retval QCBOR_ERR_LABEL_NOT_FOUND The label doesn't match.
Laurence Lundbladec4474172020-10-02 14:52:16 -0700450 @retval QCBOR_ERR_UNEXPECTED_TYPE The label matches, but the type is
451 not as expected.
452 @retval QCBOR_SUCCESS Both label and type match.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700453 */
Laurence Lundblade1818e632020-07-26 04:14:08 -0700454QCBORError CheckLabelAndType(const char *szLabel, uint8_t uQCBORType, const QCBORItem *pItem)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700455{
456 if(pItem->uLabelType != QCBOR_TYPE_TEXT_STRING) {
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700457 return QCBOR_ERR_LABEL_NOT_FOUND;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700458 }
459
460 UsefulBufC Label = UsefulBuf_FromSZ(szLabel);
461
Laurence Lundblade06c83042020-07-03 23:04:53 -0700462 if(UsefulBuf_Compare(Label, pItem->label.string)) {
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700463 return QCBOR_ERR_LABEL_NOT_FOUND;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700464 }
465
Laurence Lundblade06c83042020-07-03 23:04:53 -0700466 if(pItem->uDataType != uQCBORType && uQCBORType != QCBOR_TYPE_ANY) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700467 return QCBOR_ERR_UNEXPECTED_TYPE;
468 }
469
470 return QCBOR_SUCCESS;
471}
472
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700473
Laurence Lundblade1818e632020-07-26 04:14:08 -0700474/**
475 @brief Decode the array of engine cylinders.
476
477 @param[in] pDecodeCtx The decode context from which to get items.
Laurence Lundbladec4474172020-10-02 14:52:16 -0700478 @param[out] pE The structure filled in from the decoding.
479 @param[in] pItem The data item that is the start of the array.
Laurence Lundblade1818e632020-07-26 04:14:08 -0700480
481 @return Either @ref EngineSuccess or an error.
482
483 This always consumes the whole array. If it has the wrong number of
484 items in it, an error is returned.
485 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700486EngineDecodeErrors DecodeCylinders(QCBORDecodeContext *pDecodeCtx,
Laurence Lundbladec4474172020-10-02 14:52:16 -0700487 CarEngine *pE,
488 const QCBORItem *pItem)
Laurence Lundblade06c83042020-07-03 23:04:53 -0700489{
490 int i = 0;
491 QCBORItem Item;
492
Laurence Lundbladec4474172020-10-02 14:52:16 -0700493 /* Loop getting all the items in the array. This uses nesting
494 level to detect the end so it works for both definite and
495 indefinite length arrays. */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700496 do {
497 QCBORError uErr;
498
499 uErr = QCBORDecode_GetNext(pDecodeCtx, &Item);
500 if(uErr != QCBOR_SUCCESS) {
501 return CBORNotWellFormed;
502 }
503 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
504 return CBORNotWellFormed;
505 }
506
507 if(i < MAX_CYLINDERS) {
508 pE->cylinders[i].uMeasuredCompression = Item.val.dfnum;
509 i++;
510 }
511
512 } while (Item.uNextNestLevel == pItem->uNextNestLevel);
513
514 if(i != pE->uNumCylinders) {
515 return WrongNumberOfCylinders;
516 } else {
517 return EngineSuccess;
518 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700519}
520
Laurence Lundblade06c83042020-07-03 23:04:53 -0700521
Laurence Lundbladeda319282020-07-06 23:04:58 -0700522/**
Laurence Lundbladec4474172020-10-02 14:52:16 -0700523 @brief Engine decode without spiffy decode.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700524
Laurence Lundblade1818e632020-07-26 04:14:08 -0700525 @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
Laurence Lundbladec4474172020-10-02 14:52:16 -0700526 @param[out] pE The structure filled in from the decoding.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700527
Laurence Lundblade1818e632020-07-26 04:14:08 -0700528 @return The decode error or success.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700529
Laurence Lundbladec4474172020-10-02 14:52:16 -0700530 This is the third implementation of engine decoding, again
531 implementing the same functionality as DecodeEngineSpiffy() and
532 DecodeEngineSpiffyFaster().
533
Laurence Lundblade1818e632020-07-26 04:14:08 -0700534 This version of the deocde is the most complex, but uses
Laurence Lundbladec4474172020-10-02 14:52:16 -0700535 significantly less code (2-3KB less on 64-bit Intel) from the QCBOR
536 library. It is also the most CPU-efficient since it does only one
537 pass through the CBOR.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700538*/
539EngineDecodeErrors DecodeEngineBasic(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700540{
541 QCBORDecodeContext DecodeCtx;
542
Laurence Lundblade323f8a92020-09-06 19:43:09 -0700543 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700544
545 QCBORItem Item;
546 QCBORError uErr;
Laurence Lundblade06c83042020-07-03 23:04:53 -0700547 EngineDecodeErrors uReturn;
548
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700549
550 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
551 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700552 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700553 goto Done;
554 }
555 if(Item.uDataType != QCBOR_TYPE_MAP) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700556 uReturn = CBORNotWellFormed;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700557 goto Done;
558 }
559
560 while(1) {
561 uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
562 if(uErr != QCBOR_SUCCESS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700563 if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
564 break; /* Non-error exit from the loop */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700565 } else {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700566 uReturn = CBORNotWellFormed;
567 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700568 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700569 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700570
Laurence Lundblade06c83042020-07-03 23:04:53 -0700571 uErr = CheckLabelAndType("Manufacturer", QCBOR_TYPE_TEXT_STRING, &Item);
572 if(uErr == QCBOR_SUCCESS) {
573 pE->Manufacturer = Item.val.string;
574 continue;
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700575 } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
Laurence Lundblade06c83042020-07-03 23:04:53 -0700576 /* Maunfacturer field missing or badly formed */
577 return EngineProtocolerror;
578 } /* continue on and try for another match */
579
Laurence Lundblade06c83042020-07-03 23:04:53 -0700580 uErr = CheckLabelAndType("NumCylinders", QCBOR_TYPE_INT64, &Item);
581 if(uErr == QCBOR_SUCCESS) {
582 if(Item.val.int64 > MAX_CYLINDERS) {
583 return TooManyCylinders;
584 } else {
585 pE->uNumCylinders = (uint8_t)Item.val.int64;
586 continue;
587 }
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700588 } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
Laurence Lundblade1818e632020-07-26 04:14:08 -0700589 /* NumCylinders field missing or badly formed */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700590 return EngineProtocolerror;
Laurence Lundblade1818e632020-07-26 04:14:08 -0700591 } /* continue on and try for another match */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700592
593 uErr = CheckLabelAndType("Cylinders", QCBOR_TYPE_ARRAY, &Item);
594 if(uErr == QCBOR_SUCCESS) {
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700595 DecodeCylinders(&DecodeCtx, pE, &Item);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700596 continue;
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700597 } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
Laurence Lundblade06c83042020-07-03 23:04:53 -0700598 return EngineProtocolerror;
599 }
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700600
Laurence Lundblade06c83042020-07-03 23:04:53 -0700601 uErr = CheckLabelAndType("Displacement", QCBOR_TYPE_INT64, &Item);
602 if(uErr == QCBOR_SUCCESS) {
603 pE->uDisplacement = Item.val.int64;
604 continue;
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700605 } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
Laurence Lundblade06c83042020-07-03 23:04:53 -0700606 return EngineProtocolerror;
607 }
608
609 uErr = CheckLabelAndType("Horsepower", QCBOR_TYPE_INT64, &Item);
610 if(uErr == QCBOR_SUCCESS) {
611 pE->uHorsePower = Item.val.int64;
612 continue;
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700613 } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
Laurence Lundblade06c83042020-07-03 23:04:53 -0700614 return EngineProtocolerror;
615 }
616
617 uErr = CheckLabelAndType("DesignedCompression", QCBOR_TYPE_DOUBLE, &Item);
618 if(uErr == QCBOR_SUCCESS) {
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700619 pE->dDesignedCompresion = Item.val.dfnum;
Laurence Lundblade06c83042020-07-03 23:04:53 -0700620 continue;
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700621 } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
Laurence Lundblade06c83042020-07-03 23:04:53 -0700622 return EngineProtocolerror;
623 }
624
Laurence Lundbladeda319282020-07-06 23:04:58 -0700625 uErr = CheckLabelAndType("Turbo", QCBOR_TYPE_ANY, &Item);
Laurence Lundblade1818e632020-07-26 04:14:08 -0700626 if(uErr == QCBOR_SUCCESS) {
627 if(Item.uDataType == QCBOR_TYPE_TRUE) {
628 pE->bTurboCharged = true;
629 } else if(Item.uDataType == QCBOR_TYPE_FALSE) {
630 pE->bTurboCharged = false;
631 } else {
632 return EngineProtocolerror;
633 }
634 continue;
Laurence Lundbladea9489f82020-09-12 13:50:56 -0700635 } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
Laurence Lundblade1818e632020-07-26 04:14:08 -0700636 return EngineProtocolerror;
637 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700638
Laurence Lundbladec4474172020-10-02 14:52:16 -0700639 /* Some label data item that is not known (could just ignore
640 extras data items) */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700641 return EngineProtocolerror;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700642 }
Laurence Lundblade06c83042020-07-03 23:04:53 -0700643 uReturn = EngineSuccess;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700644
Laurence Lundbladec4474172020-10-02 14:52:16 -0700645 /* Catch the remainder of errors here */
646 uErr = QCBORDecode_Finish(&DecodeCtx);
647 if(uErr) {
648 uReturn = ConvertError(uErr);
649 }
650
651
652
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700653Done:
Laurence Lundblade06c83042020-07-03 23:04:53 -0700654 return uReturn;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700655}
656
Laurence Lundbladec4474172020-10-02 14:52:16 -0700657#endif /* EXAMPLE_DISABLE_BASIC_DECODE */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700658
659
Laurence Lundblade1818e632020-07-26 04:14:08 -0700660int32_t RunQCborExample()
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700661{
Laurence Lundbladec4474172020-10-02 14:52:16 -0700662 CarEngine E, DecodedEngine;
663 MakeUsefulBufOnStack( EngineBuffer, 300);
664 UsefulBufC EncodedEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700665
Laurence Lundbladec4474172020-10-02 14:52:16 -0700666 MakeUsefulBufOnStack( InDefEngineBuffer, 300);
667 UsefulBufC InDefEncodedEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700668
Laurence Lundbladec4474172020-10-02 14:52:16 -0700669 EngineDecodeErrors uErr;
Laurence Lundblade1818e632020-07-26 04:14:08 -0700670
Laurence Lundbladec4474172020-10-02 14:52:16 -0700671 EngineInit(&E);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700672
Laurence Lundbladec4474172020-10-02 14:52:16 -0700673#ifndef EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE
674 EncodedEngine = EncodeEngineDefiniteLength(&E, EngineBuffer);
675 printf("Definite Length Engine Encoded in %zu bytes\n", EncodedEngine.len);
676#endif /* EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700677
678
Laurence Lundbladec4474172020-10-02 14:52:16 -0700679#ifndef EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE_ENCODE
680 InDefEncodedEngine = EncodeEngineIndefinteLen(&E, InDefEngineBuffer);
681 printf("Indef Engine Encoded in %zu bytes\n", InDefEncodedEngine.len);
682#endif /* EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE_ENCODE */
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700683
Laurence Lundblade06c83042020-07-03 23:04:53 -0700684
Laurence Lundbladec4474172020-10-02 14:52:16 -0700685#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE
686 uErr = DecodeEngineSpiffy(EncodedEngine, &DecodedEngine);
687 printf("Spiffy Engine Decode Result: %d\n", uErr);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700688
Laurence Lundbladec4474172020-10-02 14:52:16 -0700689 if(!EngineCompare(&E, &DecodedEngine)) {
690 printf("Spiffy Engine Decode comparison fail\n");
691 }
692#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE */
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700693
Laurence Lundbladec4474172020-10-02 14:52:16 -0700694#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE_FAST
695 uErr = DecodeEngineSpiffyFaster(EncodedEngine, &DecodedEngine);
696 printf("Faster Spiffy Engine Decode Result: %d\n", uErr);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700697
Laurence Lundbladec4474172020-10-02 14:52:16 -0700698 if(!EngineCompare(&E, &DecodedEngine)) {
699 printf("Faster Spiffy Engine Decode comparison fail\n");
700 }
701#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE_FAST */
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700702
Laurence Lundbladec4474172020-10-02 14:52:16 -0700703#ifndef EXAMPLE_DISABLE_BASIC_DECODE
704 uErr = DecodeEngineBasic(EncodedEngine, &DecodedEngine);
705 printf("Engine Basic Decode Result: %d\n", uErr);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700706
Laurence Lundbladec4474172020-10-02 14:52:16 -0700707 if(!EngineCompare(&E, &DecodedEngine)) {
708 printf("Engine Basic Decode comparison fail\n");
709 }
710#endif /* EXAMPLE_DISABLE_BASIC_DECODE */
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700711
Laurence Lundbladec4474172020-10-02 14:52:16 -0700712 printf("\n");
713
714 return 0;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700715}