| /* ========================================================================= |
| example.c -- Example code for QCBOR |
| |
| Copyright (c) 2020-2021, Laurence Lundblade. All rights reserved. |
| Copyright (c) 2021, Arm Limited. All rights reserved. |
| |
| SPDX-License-Identifier: BSD-3-Clause |
| |
| See BSD-3-Clause license in file named "LICENSE" |
| |
| Created on 6/30/2020 |
| ========================================================================== */ |
| |
| #include <stdio.h> |
| #include "example.h" |
| #include "qcbor/qcbor_encode.h" |
| #include "qcbor/qcbor_decode.h" |
| #include "qcbor/qcbor_spiffy_decode.h" |
| |
| |
| /** |
| * This is a simple example of encoding and decoding some CBOR from |
| * and to a C structure. |
| * |
| * This also includes a comparison between the original structure |
| * and the one decoded from the CBOR to confirm correctness. |
| */ |
| |
| |
| #define MAX_CYLINDERS 16 |
| |
| /** |
| * The data structure representing a car engine that is encoded and |
| * decoded in this example. |
| */ |
| typedef struct |
| { |
| UsefulBufC Manufacturer; |
| int64_t uDisplacement; |
| int64_t uHorsePower; |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| double dDesignedCompresion; |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| int64_t uNumCylinders; |
| bool bTurboCharged; |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| struct { |
| double dMeasuredCompression; |
| } cylinders[MAX_CYLINDERS]; |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| } CarEngine; |
| |
| |
| /** |
| * @brief Initialize the Engine data structure with values to encode. |
| * |
| * @param[out] pE The Engine structure to fill in |
| */ |
| void EngineInit(CarEngine *pE) |
| { |
| pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche"); |
| pE->uDisplacement = 3296; |
| pE->uHorsePower = 210; |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| pE->dDesignedCompresion = 9.1; |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| pE->uNumCylinders = 6; |
| pE->bTurboCharged = false; |
| |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| pE->cylinders[0].dMeasuredCompression = 9.0; |
| pE->cylinders[1].dMeasuredCompression = 9.2; |
| pE->cylinders[2].dMeasuredCompression = 8.9; |
| pE->cylinders[3].dMeasuredCompression = 8.9; |
| pE->cylinders[4].dMeasuredCompression = 9.1; |
| pE->cylinders[5].dMeasuredCompression = 9.0; |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| } |
| |
| |
| /** |
| * @brief Compare two Engine structure for equality. |
| * |
| * @param[in] pE1 First Engine to compare. |
| * @param[in] pE2 Second Engine to compare. |
| * |
| * @retval Return @c true if the two Engine data structures are exactly the |
| * same. |
| */ |
| static bool EngineCompare(const CarEngine *pE1, const CarEngine *pE2) |
| { |
| if(pE1->uNumCylinders != pE2->uNumCylinders) { |
| return false; |
| } |
| if(pE1->bTurboCharged != pE2->bTurboCharged) { |
| return false; |
| } |
| if(pE1->uDisplacement != pE2->uDisplacement) { |
| return false; |
| } |
| if(pE1->uHorsePower != pE2->uHorsePower) { |
| return false; |
| } |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) { |
| return false; |
| } |
| for(int64_t i = 0; i < pE2->uNumCylinders; i++) { |
| if(pE1->cylinders[i].dMeasuredCompression != |
| pE2->cylinders[i].dMeasuredCompression) { |
| return false; |
| } |
| } |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| |
| if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| /** |
| * @brief Encode an initialized CarEngine data structure in CBOR. |
| * |
| * @param[in] pEngine The data structure to encode. |
| * @param[in] Buffer Pointer and length of buffer to output to. |
| * |
| * @return The pointer and length of the encoded CBOR or |
| * @ref NULLUsefulBufC on error. |
| * |
| * This encodes the input structure \c pEngine as a CBOR map of |
| * label-value pairs. An array of float is one of the items in the |
| * map. |
| * |
| * This uses the UsefulBuf convention of passing in a non-const empty |
| * buffer to be filled in and returning a filled in const buffer. The |
| * buffer to write into is given as a pointer and length in a |
| * UsefulBuf. The buffer returned with the encoded CBOR is a |
| * UsefulBufC also a pointer and length. In this implementation the |
| * pointer to the returned data is exactly the same as that of the |
| * empty buffer. The returned length will be smaller than or equal to |
| * that of the empty buffer. This gives correct const-ness for the |
| * buffer passed in and the data returned. |
| * |
| * @c Buffer must be big enough to hold the output. If it is not @ref |
| * NULLUsefulBufC will be returned. @ref NULLUsefulBufC will be |
| * returned for any other encoding errors. |
| * |
| * This can be called with @c Buffer set to @ref SizeCalculateUsefulBuf |
| * in which case the size of the encoded engine will be calculated, |
| * but no actual encoded CBOR will be output. The calculated size is |
| * in @c .len of the returned @ref UsefulBufC. |
| */ |
| UsefulBufC EncodeEngine(const CarEngine *pEngine, UsefulBuf Buffer) |
| { |
| /* Set up the encoding context with the output buffer */ |
| QCBOREncodeContext EncodeCtx; |
| QCBOREncode_Init(&EncodeCtx, Buffer); |
| |
| /* Proceed to output all the items, letting the internal error |
| * tracking do its work */ |
| QCBOREncode_OpenMap(&EncodeCtx); |
| QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer); |
| QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders); |
| QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement); |
| QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower); |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion); |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders"); |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) { |
| QCBOREncode_AddDouble(&EncodeCtx, |
| pEngine->cylinders[i].dMeasuredCompression); |
| } |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| QCBOREncode_CloseArray(&EncodeCtx); |
| QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged); |
| QCBOREncode_CloseMap(&EncodeCtx); |
| |
| /* Get the pointer and length of the encoded output. If there was |
| * any encoding error, it will be returned here */ |
| UsefulBufC EncodedCBOR; |
| QCBORError uErr; |
| uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR); |
| if(uErr != QCBOR_SUCCESS) { |
| return NULLUsefulBufC; |
| } else { |
| return EncodedCBOR; |
| } |
| } |
| |
| |
| /** |
| * Error results when decoding an Engine data structure. |
| */ |
| typedef enum { |
| EngineSuccess, |
| CBORNotWellFormed, |
| TooManyCylinders, |
| EngineProtocolerror, |
| WrongNumberOfCylinders |
| } EngineDecodeErrors; |
| |
| |
| /** |
| * Convert @ref QCBORError to @ref EngineDecodeErrors. |
| */ |
| static EngineDecodeErrors ConvertError(QCBORError uErr) |
| { |
| EngineDecodeErrors uReturn; |
| |
| switch(uErr) |
| { |
| case QCBOR_SUCCESS: |
| uReturn = EngineSuccess; |
| break; |
| |
| case QCBOR_ERR_HIT_END: |
| uReturn = CBORNotWellFormed; |
| break; |
| |
| default: |
| uReturn = EngineProtocolerror; |
| break; |
| } |
| |
| return uReturn; |
| } |
| |
| |
| /** |
| * @brief Simplest engine decode using spiffy decode features. |
| * |
| * @param[in] EncodedEngine Pointer and length of CBOR-encoded engine. |
| * @param[out] pE The structure filled in from the decoding. |
| * |
| * @return The decode error or success. |
| * |
| * This decodes the CBOR into the engine structure. |
| * |
| * As QCBOR automatically supports both definite and indefinite maps |
| * and arrays, this will decode either. |
| * |
| * This uses QCBOR's spiffy decode functions, so the implementation is |
| * simple and closely parallels the encode implementation in |
| * EncodeEngineDefiniteLength(). |
| * |
| * Another way to decode without using spiffy decode functions is to |
| * use QCBORDecode_GetNext() to traverse the whole tree. This |
| * requires a more complex implementation, but is faster and will pull |
| * in less code from the CBOR library. The speed advantage is likely |
| * of consequence when decoding much much larger CBOR on slow small |
| * CPUs. |
| * |
| * A middle way is to use the spiffy decode |
| * QCBORDecode_GetItemsInMap(). The implementation has middle |
| * complexity and uses less CPU. |
| */ |
| EngineDecodeErrors DecodeEngineSpiffy(UsefulBufC EncodedEngine, CarEngine *pE) |
| { |
| QCBORError uErr; |
| QCBORDecodeContext DecodeCtx; |
| |
| /* Let QCBORDecode internal error tracking do its work. */ |
| QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL); |
| QCBORDecode_EnterMap(&DecodeCtx, NULL); |
| QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer)); |
| QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement)); |
| QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower)); |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion)); |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged)); |
| |
| QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders)); |
| |
| /* Check the internal tracked error now before going on to |
| * reference any of the decoded data, particularly |
| * pE->uNumCylinders */ |
| uErr = QCBORDecode_GetError(&DecodeCtx); |
| if(uErr != QCBOR_SUCCESS) { |
| goto Done; |
| } |
| |
| if(pE->uNumCylinders > MAX_CYLINDERS) { |
| return TooManyCylinders; |
| } |
| |
| QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders"); |
| #ifndef USEFULBUF_DISABLE_ALL_FLOAT |
| for(int64_t i = 0; i < pE->uNumCylinders; i++) { |
| QCBORDecode_GetDouble(&DecodeCtx, |
| &(pE->cylinders[i].dMeasuredCompression)); |
| } |
| #endif /* USEFULBUF_DISABLE_ALL_FLOAT */ |
| QCBORDecode_ExitArray(&DecodeCtx); |
| QCBORDecode_ExitMap(&DecodeCtx); |
| |
| /* Catch further decoding error here */ |
| uErr = QCBORDecode_Finish(&DecodeCtx); |
| |
| Done: |
| return ConvertError(uErr); |
| } |
| |
| |
| int32_t RunQCborExample(void) |
| { |
| CarEngine InitialEngine; |
| CarEngine DecodedEngine; |
| |
| /* For every buffer used by QCBOR a pointer and a length are always |
| * carried in a UsefulBuf. This is a secure coding and hygene |
| * practice to help make sure code never runs off the end of a |
| * buffer. |
| * |
| * UsefulBuf structures are passed as a stack parameter to make the |
| * code prettier. The object code generated isn't much different |
| * from passing a pointer parameter and a length parameter. |
| * |
| * This macro is equivalent to: |
| * uint8_t __pBufEngineBuffer[300]; |
| * UsefulBuf EngineBuffer = {__pBufEngineBuffer, 300}; |
| */ |
| UsefulBuf_MAKE_STACK_UB( EngineBuffer, 300); |
| |
| /* The pointer in UsefulBuf is not const and used for representing |
| * a buffer to be written to. For UsefulbufC, the pointer is const |
| * and is used to represent a buffer that has been written to. |
| */ |
| UsefulBufC EncodedEngine; |
| EngineDecodeErrors uErr; |
| |
| /* Initialize the structure with some values. */ |
| EngineInit(&InitialEngine); |
| |
| /* Encode the engine structure. */ |
| EncodedEngine = EncodeEngine(&InitialEngine, EngineBuffer); |
| if(UsefulBuf_IsNULLC(EncodedEngine)) { |
| printf("Engine encode failed\n"); |
| goto Done; |
| } |
| printf("Example: Definite Length Engine Encoded in %zu bytes\n", |
| EncodedEngine.len); |
| |
| /* Decode the CBOR */ |
| uErr = DecodeEngineSpiffy(EncodedEngine, &DecodedEngine); |
| printf("Example: Spiffy Engine Decode Result: %d\n", uErr); |
| if(uErr) { |
| goto Done; |
| } |
| |
| /* Check the results */ |
| if(!EngineCompare(&InitialEngine, &DecodedEngine)) { |
| printf("Example: Spiffy Engine Decode comparison fail\n"); |
| } |
| |
| |
| /* Further example of how to calculate the encoded size, then allocate */ |
| UsefulBufC EncodedEngineSize; |
| EncodedEngineSize = EncodeEngine(&InitialEngine, SizeCalculateUsefulBuf); |
| if(UsefulBuf_IsNULLC(EncodedEngine)) { |
| printf("Engine encode size calculation failed\n"); |
| goto Done; |
| } |
| (void)EncodedEngineSize; /* Supress unsed variable warning */ |
| /* Here malloc could be called to allocate a buffer. Then |
| * EncodeEngine() can be called a second time to actually |
| * encode. (The actual code is not live here to avoid a |
| * dependency on malloc()). |
| * UsefulBuf MallocedBuffer; |
| * MallocedBuffer.len = EncodedEngineSize.len; |
| * MallocedBuffer.ptr = malloc(EncodedEngineSize.len); |
| * EncodedEngine = EncodeEngine(&InitialEngine, MallocedBuffer); |
| */ |
| |
| Done: |
| printf("\n"); |
| |
| return 0; |
| } |