blob: 4921e13005814d6f822927b67db1bca9f08aa107 [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 Lundblade5c91eb32021-04-03 23:56:54 -07004 Copyright (c) 2020-2021, 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 Lundblade5c91eb32021-04-03 23:56:54 -070020/**
21 * This is a simple example of encoding and decoding some CBOR from
22 * and to a C structure.
23 *
24 * This also includes a comparison between the original structure
25 * and the one decoded from the CBOR to confirm correctness.
26 */
27
28
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070029#define MAX_CYLINDERS 16
30
Laurence Lundbladeda319282020-07-06 23:04:58 -070031/**
Laurence Lundblade5c91eb32021-04-03 23:56:54 -070032 * The data structure representing a car engine that is encoded and
33 * decoded in this example.
Laurence Lundbladeda319282020-07-06 23:04:58 -070034 */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070035typedef struct
36{
Laurence Lundbladec4474172020-10-02 14:52:16 -070037 UsefulBufC Manufacturer;
38 int64_t uDisplacement;
39 int64_t uHorsePower;
40 double dDesignedCompresion;
41 int64_t uNumCylinders;
42 bool bTurboCharged;
43 struct {
Laurence Lundblade5c91eb32021-04-03 23:56:54 -070044 double dMeasuredCompression;
Laurence Lundbladec4474172020-10-02 14:52:16 -070045 } cylinders[MAX_CYLINDERS];
Laurence Lundbladeda319282020-07-06 23:04:58 -070046} CarEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070047
48
Laurence Lundbladeda319282020-07-06 23:04:58 -070049/**
Laurence Lundblade5c91eb32021-04-03 23:56:54 -070050 * @brief Initialize the Engine data structure with values to encode.
51 *
52 * @param[out] pE The Engine structure to fill in
Laurence Lundbladeda319282020-07-06 23:04:58 -070053 */
54void EngineInit(CarEngine *pE)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070055{
Laurence Lundbladec4474172020-10-02 14:52:16 -070056 pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche");
57 pE->uDisplacement = 3296;
58 pE->uHorsePower = 210;
59 pE->dDesignedCompresion = 9.1;
60 pE->uNumCylinders = 6;
61 pE->bTurboCharged = false;
62
Laurence Lundblade5c91eb32021-04-03 23:56:54 -070063 pE->cylinders[0].dMeasuredCompression = 9.0;
64 pE->cylinders[1].dMeasuredCompression = 9.2;
65 pE->cylinders[2].dMeasuredCompression = 8.9;
66 pE->cylinders[3].dMeasuredCompression = 8.9;
67 pE->cylinders[4].dMeasuredCompression = 9.1;
68 pE->cylinders[5].dMeasuredCompression = 9.0;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -070069}
70
71
Laurence Lundbladeda319282020-07-06 23:04:58 -070072/**
Laurence Lundblade5c91eb32021-04-03 23:56:54 -070073 * @brief Compare two Engine structure for equality.
74 *
75 * @param[in] pE1 First Engine to compare.
76 * @param[in] pE2 Second Engine to compare.
77 *
78 * @retval Return @c true if the two Engine data structures are exactly the
79 * same.
Laurence Lundbladeda319282020-07-06 23:04:58 -070080 */
Laurence Lundblade5c91eb32021-04-03 23:56:54 -070081static bool EngineCompare(const CarEngine *pE1, const CarEngine *pE2)
Laurence Lundbladee6bbf552020-07-05 22:57:57 -070082{
Laurence Lundblade5c91eb32021-04-03 23:56:54 -070083 if(pE1->uNumCylinders != pE2->uNumCylinders) {
84 return false;
85 }
86 if(pE1->bTurboCharged != pE2->bTurboCharged) {
87 return false;
88 }
89 if(pE1->uDisplacement != pE2->uDisplacement) {
90 return false;
91 }
92 if(pE1->uHorsePower != pE2->uHorsePower) {
93 return false;
94 }
95 if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) {
96 return false;
97 }
98 for(int64_t i = 0; i < pE2->uNumCylinders; i++) {
99 if(pE1->cylinders[i].dMeasuredCompression !=
100 pE2->cylinders[i].dMeasuredCompression) {
101 return false;
102 }
103 }
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700104
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700105 if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) {
106 return false;
107 }
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700108
109 return true;
110}
111
112
Laurence Lundbladeda319282020-07-06 23:04:58 -0700113/**
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700114 * @brief Encode an initialized CarEngine data structure in CBOR.
115 *
116 * @param[in] pEngine The data structure to encode.
117 * @param[in] Buffer Pointer and length of buffer to output to.
118 *
119 * @return The pointer and length of the encoded CBOR or
120 * @ref NULLUsefulBufC on error.
121 *
122 * This encodes the input structure \c pEngine as a CBOR map of
123 * label-value pairs. An array of float is one of the items in the
124 * map.
125 *
126 * This uses the UsefulBuf convention of passing in a non-const empty
127 * buffer to be filled in and returning a filled in const buffer. The
128 * buffer to write into is given as a pointer and length in a
129 * UsefulBuf. The buffer returned with the encoded CBOR is a
130 * UsefulBufC also a pointer and length. In this implementation the
131 * pointer to the returned data is exactly the same as that of the
132 * empty buffer. The returned length will be smaller than or equal to
133 * that of the empty buffer. This gives correct const-ness for the
134 * buffer passed in and the data returned.
135 *
136 * @c Buffer must be big enough to hold the output. If it is not @ref
137 * NULLUsefulBufC will be returned. @ref NULLUsefulBufC will be
138 * returned for any other encoding errors.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700139 */
Laurence Lundbladec4474172020-10-02 14:52:16 -0700140UsefulBufC EncodeEngineDefiniteLength(const CarEngine *pEngine, UsefulBuf Buffer)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700141{
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700142 /* Set up the encoding context with the output buffer */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700143 QCBOREncodeContext EncodeCtx;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700144 QCBOREncode_Init(&EncodeCtx, Buffer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700145
Laurence Lundbladec4474172020-10-02 14:52:16 -0700146 /* Proceed to output all the items, letting the internal error
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700147 * tracking do its work */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700148 QCBOREncode_OpenMap(&EncodeCtx);
149 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
Laurence Lundblade06c83042020-07-03 23:04:53 -0700150 QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
151 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
152 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700153 QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700154 QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders");
Laurence Lundblade06c83042020-07-03 23:04:53 -0700155 for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700156 QCBOREncode_AddDouble(&EncodeCtx,
157 pEngine->cylinders[i].dMeasuredCompression);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700158 }
159 QCBOREncode_CloseArray(&EncodeCtx);
Laurence Lundbladeda319282020-07-06 23:04:58 -0700160 QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700161 QCBOREncode_CloseMap(&EncodeCtx);
162
Laurence Lundblade06c83042020-07-03 23:04:53 -0700163 /* Get the pointer and length of the encoded output. If there was
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700164 * any encoding error, it will be returned here */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700165 UsefulBufC EncodedCBOR;
166 QCBORError uErr;
167 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
168 if(uErr != QCBOR_SUCCESS) {
Laurence Lundbladec4474172020-10-02 14:52:16 -0700169 return NULLUsefulBufC;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700170 } else {
171 return EncodedCBOR;
172 }
173}
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700174
175
Laurence Lundbladeda319282020-07-06 23:04:58 -0700176/**
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700177 * Error results when decoding an Engine data structure.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700178 */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700179typedef enum {
180 EngineSuccess,
181 CBORNotWellFormed,
182 TooManyCylinders,
183 EngineProtocolerror,
184 WrongNumberOfCylinders
185} EngineDecodeErrors;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700186
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700187
Laurence Lundbladeda319282020-07-06 23:04:58 -0700188/**
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700189 * Convert @ref QCBORError to @ref EngineDecodeErrors.
Laurence Lundbladeda319282020-07-06 23:04:58 -0700190 */
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700191static EngineDecodeErrors ConvertError(QCBORError uErr)
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700192{
Laurence Lundblade06c83042020-07-03 23:04:53 -0700193 EngineDecodeErrors uReturn;
194
195 switch(uErr)
196 {
197 case QCBOR_SUCCESS:
198 uReturn = EngineSuccess;
199 break;
200
201 case QCBOR_ERR_HIT_END:
202 uReturn = CBORNotWellFormed;
203 break;
204
205 default:
206 uReturn = EngineProtocolerror;
207 break;
208 }
209
210 return uReturn;
211}
212
213
Laurence Lundbladeda319282020-07-06 23:04:58 -0700214/**
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700215 * @brief Simplest engine decode using spiffy decode features.
216 *
217 * @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
218 * @param[out] pE The structure filled in from the decoding.
219 *
220 * @return The decode error or success.
221 *
222 * This decodes the CBOR into the engine structure.
223 *
224 * As QCBOR automatically supports both definite and indefinite maps
225 * and arrays, this will decode either.
226 *
227 * This uses QCBOR's spiffy decode functions, so the implementation is
228 * simple and closely parallels the encode implementation in
229 * EncodeEngineDefiniteLength().
230 *
231 * Another way to decode without using spiffy decode functions is to
232 * use QCBORDecode_GetNext() to traverse the whole tree. This
233 * requires a more complex implementation, but is faster and will pull
234 * in less code from the CBOR library. The speed advantage is likely
235 * of consequence when decoding much much larger CBOR on slow small
236 * CPUs.
237 *
238 * A middle way is to use the spiffy decode
239 * QCBORDecode_GetItemsInMap(). The implementation has middle
240 * complexity and uses less CPU.
Laurence Lundblade06c83042020-07-03 23:04:53 -0700241 */
Laurence Lundblade67257dc2020-07-27 03:33:37 -0700242EngineDecodeErrors DecodeEngineSpiffy(UsefulBufC EncodedEngine, CarEngine *pE)
Laurence Lundblade06c83042020-07-03 23:04:53 -0700243{
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700244 QCBORError uErr;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700245 QCBORDecodeContext DecodeCtx;
246
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700247 /* Let QCBORDecode internal error tracking do its work. */
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700248 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
Laurence Lundblade6545d1b2020-10-14 11:13:13 -0700249 QCBORDecode_EnterMap(&DecodeCtx, NULL);
Laurence Lundblade323f8a92020-09-06 19:43:09 -0700250 QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
Laurence Lundblade06c83042020-07-03 23:04:53 -0700251 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
252 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700253 QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion));
Laurence Lundbladeda319282020-07-06 23:04:58 -0700254 QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700255
Laurence Lundblade06c83042020-07-03 23:04:53 -0700256 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700257
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700258 /* Check the internal tracked error now before going on to
259 * reference any of the decoded data, particularly
260 * pE->uNumCylinders */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700261 uErr = QCBORDecode_GetError(&DecodeCtx);
262 if(uErr != QCBOR_SUCCESS) {
263 goto Done;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700264 }
265
266 if(pE->uNumCylinders > MAX_CYLINDERS) {
Laurence Lundblade06c83042020-07-03 23:04:53 -0700267 return TooManyCylinders;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700268 }
269
270 QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700271 for(int64_t i = 0; i < pE->uNumCylinders; i++) {
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700272 QCBORDecode_GetDouble(&DecodeCtx,
273 &(pE->cylinders[i].dMeasuredCompression));
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700274 }
275 QCBORDecode_ExitArray(&DecodeCtx);
276 QCBORDecode_ExitMap(&DecodeCtx);
277
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700278 /* Catch further decoding error here */
Laurence Lundblade06c83042020-07-03 23:04:53 -0700279 uErr = QCBORDecode_Finish(&DecodeCtx);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700280
Laurence Lundblade06c83042020-07-03 23:04:53 -0700281Done:
282 return ConvertError(uErr);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700283}
284
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700285
Laurence Lundblade1818e632020-07-26 04:14:08 -0700286int32_t RunQCborExample()
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700287{
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700288 CarEngine InitialEngine;
289 CarEngine DecodedEngine;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700290
Laurence Lundblade8510f8c2020-12-01 11:31:16 -0800291 /* For every buffer used by QCBOR a pointer and a length are always
292 * carried in a UsefulBuf. This is a secure coding and hygene
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700293 * practice to help make sure code never runs off the end of a
294 * buffer.
Laurence Lundblade8510f8c2020-12-01 11:31:16 -0800295 *
296 * UsefulBuf structures are passed as a stack parameter to make the
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700297 * code prettier. The object code generated isn't much different
298 * from passing a pointer parameter and a length parameter.
Laurence Lundblade8510f8c2020-12-01 11:31:16 -0800299 *
300 * This macro is equivalent to:
301 * uint8_t __pBufEngineBuffer[300];
302 * UsefulBuf EngineBuffer = {__pBufEngineBuffer, 300};
303 */
304 UsefulBuf_MAKE_STACK_UB( EngineBuffer, 300);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700305
Laurence Lundblade8510f8c2020-12-01 11:31:16 -0800306 /* The pointer in UsefulBuf is not const and used for representing
307 * a buffer to be written to. For UsefulbufC, the pointer is const
308 * and is used to represent a buffer that has been written to.
309 */
310 UsefulBufC EncodedEngine;
Laurence Lundblade8510f8c2020-12-01 11:31:16 -0800311 EngineDecodeErrors uErr;
Laurence Lundblade1818e632020-07-26 04:14:08 -0700312
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700313 /* Initialize the structure with some values. */
314 EngineInit(&InitialEngine);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700315
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700316 /* Encode the engine structure. */
317 EncodedEngine = EncodeEngineDefiniteLength(&InitialEngine, EngineBuffer);
318 if(UsefulBuf_IsNULLC(EncodedEngine)) {
319 printf("Engine encode failed\n");
320 goto Done;
321 }
322 printf("Example: Definite Length Engine Encoded in %zu bytes\n",
323 EncodedEngine.len);
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700324
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700325 /* Decode the CBOR */
Laurence Lundbladec4474172020-10-02 14:52:16 -0700326 uErr = DecodeEngineSpiffy(EncodedEngine, &DecodedEngine);
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700327 printf("Example: Spiffy Engine Decode Result: %d\n", uErr);
328 if(uErr) {
329 goto Done;
Laurence Lundbladec4474172020-10-02 14:52:16 -0700330 }
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700331
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700332 /* Check the results */
333 if(!EngineCompare(&InitialEngine, &DecodedEngine)) {
334 printf("Example: Spiffy Engine Decode comparison fail\n");
Laurence Lundbladec4474172020-10-02 14:52:16 -0700335 }
Laurence Lundbladee6bbf552020-07-05 22:57:57 -0700336
Laurence Lundblade5c91eb32021-04-03 23:56:54 -0700337Done:
Laurence Lundbladec4474172020-10-02 14:52:16 -0700338 printf("\n");
339
340 return 0;
Laurence Lundbladed4cd7232020-07-03 19:30:48 -0700341}