QCBOR: Add CBOR encoder / decoder library
QCBOR supports encoding and decoding of most
of the CBOR standard, RFC 7049. QCBOR is open
source maintained at
https://github.com/laurencelundblade/QCBOR
Change-Id: I5632379e4a1fdb16e0df7f03dfa2374160b7ed7f
Signed-off-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/lib/ext/qcbor/README.md b/lib/ext/qcbor/README.md
new file mode 100644
index 0000000..58a7080
--- /dev/null
+++ b/lib/ext/qcbor/README.md
@@ -0,0 +1,175 @@
+# QCBOR
+
+QCBOR encodes and decodes [RFC 7049](https://tools.ietf.org/html/rfc7049) CBOR.
+
+## Characteristics
+
+**Implemented in C with minimal dependency** – Only dependencies are
+ C99, <stdint.h>, <stddef.h>, <stdbool.h> and <string.h> making it
+ highly portable. There are no #ifdefs to be configured at all.
+
+**Focused on C / native data representation** – Simpler code because
+ there is no support for encoding/decoding to/from JSON, pretty
+ printing, diagnostic notation... Only encoding from native C
+ representations and decoding to native C representations is supported.
+
+**Small simple memory model** – Malloc is not needed. The encode
+ context is 136 bytes, decode context is 104 bytes and the
+ description of decoded data item is 56 bytes. Stack use is light and
+ there is no recursion. The caller supplies the memory to hold the
+ encoded CBOR and encode/decode contexts so caller has full control
+ of memory usage making it good for embedded implementations that
+ have to run in small fixed memory.
+
+**Supports nearly all of RFC 7049** – Only minor, corner-case parts of
+ RFC 7049 are not directly supported (canonicalization, decimal
+ fractions, big floats). Decoding indefinite length strings is supported,
+ but requires a string allocator (see documentation). Encoding indefinite
+ length strings is not supported, but is also not necessary or
+ preferred.
+
+**Extensible and general** – Provides a way to handle data types that
+ are not directly supported.
+
+**Secure coding style** – Uses a construct called UsefulBuf as a
+ discipline for very safe coding the handling of binary data.
+
+**Small code size** – When optimized for size using the compiler -Os
+ option, x86 code is about 4KB (~1.1KB encode, ~2.5KB decode,
+ ~0.4KB common). Other decoders may be smaller, but they may
+ also do less for you, so overall size of the implementation may
+ be larger. For example, QCBOR internally tracks error status
+ so you don't have to check a return code on every operation.
+
+**Clear documented public interface** – The public interface is
+ separated from the implementation. It can be put to use without
+ reading the source.
+
+**Comprehensive test suite** – Easy to verify on a new platform
+ or OS with the test suite. The test suite dependencies are also
+ minimal, only additionally requiring <math.h> for floating point
+ tests.
+
+## Code Status
+
+QCBOR was originally developed by Qualcomm. It was [open sourced
+through CAF](https://source.codeaurora.org/quic/QCBOR/QCBOR/) with a
+permissive Linux license, September 2018 (thanks Qualcomm!).
+
+This code in [Laurence's
+GitHub](https://github.com/laurencelundblade/QCBOR) has diverged from
+the CAF source with some small simplifications and tidying up.
+
+From Nov 3, 2018, the interface and code are fairly stable. Large
+changes are not planned or expected, particularly in the
+interface. The test coverage is pretty good.
+
+## Building
+
+There is a simple makefile for the UNIX style command line binary that
+compiles everything to run the tests.
+
+These seven files, the contents of the src and inc directories, make
+up the entire implementation.
+
+* inc
+ * UsefulBuf.h
+ * qcbor.h
+* src
+ * UsefulBuf.c
+ * qcbor_encode.c
+ * qcbor_decode.c
+ * ieee754.h
+ * ieee754.c
+
+For most use cases you should just be able to add them to your
+project. Hopefully the easy portability of this implementation makes
+this work straight away, whatever your development environment is.
+
+The files ieee754.c and ieee754.h are support for half-precision
+floating point. The encoding side of the floating point functionality
+is about 500 bytes. If it is never called because no floating point
+numbers are ever encoded, all 500 bytes will be dead stripped and not
+impact code size. The decoding side is about 150 bytes of object
+code. It is never dead stripped because it directly referenced by the
+core decoder, however it doesn't add very much to the size.
+
+The test directory includes some tests that are nearly as portable as
+the main implementation. If your development environment doesn't
+support UNIX style command line and make, you should be able to make a
+simple project and add the test files to it. Then just call
+RunTests() to invoke them all.
+
+
+## Changes from CAF Version
+* Float support is restored
+* Minimal length float encoding is added
+* indefinite length arrays/maps are supported
+* indefinite length strings are supported
+* Tag decoding is changed; unlimited number of tags supported, any tag
+value supported, tag utility function for easier tag checking
+* Addition functions in UsefulBuf
+* QCBOREncode_Init takes a UsefulBuf instead of a pointer and size
+* QCBOREncode_Finish takes a UsefulBufC and EncodedCBOR is remove
+* bstr wrapping of arrays/maps is replaced with OpenBstrwrap
+* AddRaw renamed to AddEncoded and can now only add whole arrays or maps,
+not partial maps and arrays (simplification; was a dangerous feature)
+* Finish cannot be called repeatedly on a partial decode (some tests used
+this, but it is not really a good thing to use in the first place)
+* UsefulOutBuf_OutUBuf changed to work differently
+* UsefulOutBuf_Init works differently
+* The "_3" functions are replaced with a small number of simpler functions
+* There is a new AddTag functon instead of the "_3" functions, making
+the interface simpler and saving some code
+* QCBOREncode_AddRawSimple_2 is removed (the macros that referenced
+still exist and work the same)
+
+## Credits
+* Ganesh Kanike for porting to QSEE
+* Mark Bapst for sponsorship and release as open source by Qualcomm
+* Sachin Sharma for release through CAF
+* Tamas Ban for porting to TF-M and 32-bit ARM
+
+## Copyright and License
+
+QCBOR is available under what is essentially the 3-Clause BSD License.
+
+Files created inside Qualcomm and open-sourced through CAF (The Code
+Aurora Forum) have a slightly modified 3-Clause BSD License. The
+modification additionally disclaims NON-INFRINGEMENT.
+
+Files created after release to CAF use the standard 3-Clause BSD
+License with no modification. These files have the SPDX license
+identifier, "SPDX-License-Identifier: BSD-3-Clause" in them.
+
+### BSD-3-Clause license
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+### Copyright for this README
+
+Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+
+
diff --git a/lib/ext/qcbor/inc/UsefulBuf.h b/lib/ext/qcbor/inc/UsefulBuf.h
new file mode 100644
index 0000000..fa17b6c
--- /dev/null
+++ b/lib/ext/qcbor/inc/UsefulBuf.h
@@ -0,0 +1,1536 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+/*===================================================================================
+ FILE: UsefulBuf.h
+
+ DESCRIPTION: General purpose input and output buffers
+
+ EDIT HISTORY FOR FILE:
+
+ This section contains comments describing changes made to the module.
+ Notice that changes are listed in reverse chronological order.
+
+ when who what, where, why
+ -------- ---- ---------------------------------------------------
+ 12/17/2018 llundblade Remove const from UsefulBuf and UsefulBufC .len
+ 12/13/2018 llundblade Documentation improvements
+ 09/18/2018 llundblade Cleaner distinction between UsefulBuf and UsefulBufC
+ 02/02/18 llundbla Full support for integers in and out; fix pointer
+ alignment bug. Incompatible change: integers in/out
+ are now in network byte order.
+ 08/12/17 llundbla Added UsefulOutBuf_AtStart and UsefulBuf_Find
+ 06/27/17 llundbla Fix UsefulBuf_Compare() bug. Only affected comparison
+ for < or > for unequal length buffers. Added
+ UsefulBuf_Set() function.
+ 05/30/17 llundbla Functions for NULL UsefulBufs and const / unconst
+ 11/13/16 llundbla Initial Version.
+
+
+ =====================================================================================*/
+
+#ifndef _UsefulBuf_h
+#define _UsefulBuf_h
+
+
+#include <stdint.h> // for uint8_t, uint16_t....
+#include <string.h> // for strlen, memcpy, memmove, memset
+#include <stddef.h> // for size_t
+
+/**
+ @file UsefulBuf.h
+
+ The goal of this code is to make buffer and pointer manipulation
+ easier and safer when working with binary data.
+
+ You use the UsefulBuf, UsefulOutBuf and UsefulInputBuf
+ structures to represent buffers rather than ad hoc pointers and lengths.
+
+ With these it will often be possible to write code that does little or no
+ direct pointer manipulation for copying and formatting data. For example
+ the QCBOR encoder was rewritten using these and has no direct pointer
+ manipulation.
+
+ While it is true that object code using these functions will be a little
+ larger and slower than a white-knuckle clever use of pointers might be, but
+ not by that much or enough to have an affect for most use cases. For
+ security-oriented code this is highly worthwhile. Clarity, simplicity,
+ reviewability and are more important.
+
+ There are some extra sanity and double checks in this code to help catch
+ coding errors and simple memory corruption. They are helpful, but not a
+ substitute for proper code review, input validation and such.
+
+ This code consists of a lot of inline functions and a few that are not.
+ It should not generate very much object code, especially with the
+ optimizer turned up to -Os or -O3. The idea is that the inline
+ functions are easier to review and understand and the optimizer does
+ the work of making the code small.
+ */
+
+
+/*...... This is a ruler that is 80 characters long...........................*/
+
+/**
+ UsefulBufC and UsefulBuf are simple data structures to hold a pointer and
+ length for a binary data. In C99 this data structure can be passed on the
+ stack making a lot of code cleaner than carrying around a pointer and
+ length as two parameters.
+
+ This is also conducive to secure code practice as the lengths are
+ always carried with the pointer and the convention for handling a
+ pointer and a length is clear.
+
+ While it might be possible to write buffer and pointer code more
+ efficiently in some use cases, the thought is that unless there is an
+ extreme need for performance (e.g., you are building a gigabit-per-second
+ IP router), it is probably better to have cleaner code you can be most
+ certain about the security of.
+
+ The non-const UsefulBuf is usually used to refer a buffer to be filled in.
+ The length is the size of the buffer.
+
+ The const UsefulBufC is usually used to refer to some data that has been
+ filled in. The length is amount of valid data pointed to.
+
+ A common use is to pass a UsefulBuf to a function, the function fills it
+ in, the function returns a UsefulBufC. The pointer is the same in both.
+
+ A UsefulBuf is NULL, it has no value, when the ptr in it is NULL.
+
+ There are utility functions for the following:
+ - Checking for UsefulBufs that are NULL, empty or both
+ - Copying, copying with offset, copying head or tail
+ - Comparing and finding substrings
+ - Initializating
+ - Create initialized const UsefulBufC from compiler literals
+ - Create initialized const UsefulBufC from NULL-terminated string
+ - Make an empty UsefulBuf on the stack
+
+ See also UsefulOutBuf. It is a richer structure that has both the size of
+ the valid data and the size of the buffer.
+
+ UsefulBuf is only 16 or 8 bytes on a 64- or 32-bit machine so it can go
+ on the stack and be a function parameter or return value.
+
+ UsefulBuf is kind of like the Useful Pot Pooh gave Eeyore on his birthday.
+ Eeyore's balloon fits beautifully, "it goes in and out like anything".
+
+*/
+typedef struct useful_buf_c {
+ const void *ptr;
+ size_t len;
+} UsefulBufC;
+
+
+/**
+ The non-const UsefulBuf typically used for some allocated memory
+ that is to be filled in. The len is the amount of memory,
+ not the length of the valid data in the buffer.
+ */
+typedef struct useful_buf {
+ void *ptr;
+ size_t len;
+} UsefulBuf;
+
+
+/**
+ A "NULL" UsefulBufC is one that has no value in the same way a NULL pointer has no value.
+ A UsefulBuf is NULL when the ptr field is NULL. It doesn't matter what len is.
+ See UsefulBuf_IsEmpty() for the distinction between NULL and empty.
+ */
+#define NULLUsefulBufC ((UsefulBufC) {NULL, 0})
+
+/** A NULL UsefulBuf is one that has no memory associated the say way
+ NULL points to nothing. It does not matter what len is.
+ */
+#define NULLUsefulBuf ((UsefulBuf) {NULL, 0})
+
+
+/**
+ @brief Check if a UsefulBuf is NULL or not
+
+ @param[in] UB The UsefulBuf to check
+
+ @return 1 if it is NULL, 0 if not.
+ */
+static inline int UsefulBuf_IsNULL(UsefulBuf UB) {
+ return !UB.ptr;
+}
+
+
+/**
+ @brief Check if a UsefulBufC is NULL or not
+
+ @param[in] UB The UsefulBufC to check
+
+ @return 1 if it is NULL, 0 if not.
+ */
+static inline int UsefulBuf_IsNULLC(UsefulBufC UB) {
+ return !UB.ptr;
+}
+
+
+/**
+ @brief Check if a UsefulBuf is empty or not
+
+ @param[in] UB The UsefulBuf to check
+
+ @return 1 if it is empty, 0 if not.
+
+ An "Empty" UsefulBuf is one that has a value and can be considered to be set,
+ but that value is of zero length. It is empty when len is zero. It
+ doesn't matter what the ptr is.
+
+ A lot of uses will not need to clearly distinguish a NULL UsefulBuf
+ from an empty one and can have the ptr NULL and the len 0. However
+ if a use of UsefulBuf needs to make a distinction then ptr should
+ not be NULL when the UsefulBuf is considered empty, but not NULL.
+
+ */
+static inline int UsefulBuf_IsEmpty(UsefulBuf UB) {
+ return !UB.len;
+}
+
+
+/**
+ @brief Check if a UsefulBufC is empty or not
+
+ @param[in] UB The UsefulBufC to check
+
+ @return 1 if it is empty, 0 if not.
+ */
+static inline int UsefulBuf_IsEmptyC(UsefulBufC UB) {
+ return !UB.len;
+}
+
+
+/**
+ @brief Check if a UsefulBuf is NULL or empty
+
+ @param[in] UB The UsefulBuf to check
+
+ @return 1 if it is either NULL or empty, 0 if not.
+ */
+static inline int UsefulBuf_IsNULLOrEmpty(UsefulBuf UB) {
+ return UsefulBuf_IsEmpty(UB) || UsefulBuf_IsNULL(UB);
+}
+
+
+/**
+ @brief Check if a UsefulBufC is NULL or empty
+
+ @param[in] UB The UsefulBufC to check
+
+ @return 1 if it is either NULL or empty, 0 if not.
+ */
+static inline int UsefulBuf_IsNULLOrEmptyC(UsefulBufC UB) {
+ return UsefulBuf_IsEmptyC(UB) || UsefulBuf_IsNULLC(UB);
+}
+
+
+/**
+ @brief Convert a non const UsefulBuf to a const UsefulBufC
+
+ @param[in] UB The UsefulBuf to convert
+
+ Returns: a UsefulBufC struct
+ */
+
+static inline UsefulBufC UsefulBuf_Const(const UsefulBuf UB)
+{
+ return (UsefulBufC){UB.ptr, UB.len};
+}
+
+
+/**
+ @brief Convert a const UsefulBufC to a non-const UsefulBuf
+
+ @param[in] UBC The UsefulBuf to convert
+
+ Returns: a non const UsefulBuf struct
+ */
+static inline UsefulBuf UsefulBuf_Unconst(const UsefulBufC UBC)
+{
+ return (UsefulBuf){(void *)UBC.ptr, UBC.len};
+}
+
+
+/**
+ Convert a literal string to a UsefulBufC.
+
+ szString must be a literal string that you can take sizeof.
+ This is better for literal strings than UsefulBuf_FromSZ()
+ because it generates less code. It will not work on
+ non-literal strings.
+
+ The terminating \0 (NULL) is NOT included in the length!
+
+ */
+#define UsefulBuf_FROM_SZ_LITERAL(szString) \
+ ((UsefulBufC) {(szString), sizeof(szString)-1})
+
+
+/**
+ Convert a literal byte array to a UsefulBufC.
+
+ pBytes must be a literal string that you can take sizeof.
+ It will not work on non-literal arrays.
+
+ */
+#define UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBytes) \
+ ((UsefulBufC) {(pBytes), sizeof(pBytes)})
+
+
+/**
+ Make an automatic variable with name of type UsefulBuf and point it to a stack
+ variable of the give size
+ */
+#define UsefulBuf_MAKE_STACK_UB(name, size) \
+ uint8_t __pBuf##name[(size)];\
+ UsefulBuf name = {__pBuf##name , sizeof( __pBuf##name )}
+
+
+/**
+ Make a byte array in to a UsefulBuf
+ */
+#define UsefulBuf_FROM_BYTE_ARRAY(pBytes) \
+ ((UsefulBuf) {(pBytes), sizeof(pBytes)})
+
+/**
+ @brief Convert a NULL terminated string to a UsefulBufC.
+
+ @param[in] szString The string to convert
+
+ @return a UsefulBufC struct
+
+ UsefulBufC.ptr points to the string so it's lifetime
+ must be maintained.
+
+ The terminating \0 (NULL) is NOT included in the length!
+
+ */
+static inline UsefulBufC UsefulBuf_FromSZ(const char *szString){
+ return ((UsefulBufC) {szString, strlen(szString)});
+}
+
+
+/**
+ @brief Copy one UsefulBuf into another at an offset
+
+ @param[in] Dest Destiation buffer to copy into
+ @param[in] uOffset The byte offset in Dest at which to copy to
+ @param[in] Src The bytes to copy
+
+ @return Pointer and length of the copy
+
+ This fails and returns NULLUsefulBufC Src.len + uOffset > Dest.len.
+
+ Like memcpy, there is no check for NULL. If NULL is passed
+ this will crash.
+
+ There is an assumption that there is valid data in Dest up to
+ uOffset as the resulting UsefulBufC returned starts
+ at the beginning of Dest and goes to Src.len + uOffset.
+
+ */
+UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src);
+
+
+/**
+ @brief Copy one UsefulBuf into another
+
+ @param[in] Dest The destination buffer to copy into
+ @param[out] Src The source to copy from
+
+ @return filled in UsefulBufC on success, NULLUsefulBufC on failure
+
+ This fails if Src.len is greater than Dest.len.
+
+ Note that like memcpy, the pointers are not checked and
+ this will crash, rather than return NULLUsefulBufC if
+ they are NULL or invalid.
+
+ Results are undefined if Dest and Src overlap.
+
+ */
+static inline UsefulBufC UsefulBuf_Copy(UsefulBuf Dest, const UsefulBufC Src) {
+ return UsefulBuf_CopyOffset(Dest, 0, Src);
+}
+
+
+/**
+ @brief Set all bytes in a UsefulBuf to a value, for example 0
+
+ @param[in] pDest The destination buffer to copy into
+ @param[in] value The value to set the bytes to
+
+ Note that like memset, the pointer in pDest is not checked and
+ this will crash if NULL or invalid.
+
+ */
+static inline UsefulBufC UsefulBuf_Set(UsefulBuf pDest, uint8_t value)
+{
+ memset(pDest.ptr, value, pDest.len);
+ return (UsefulBufC){pDest.ptr, pDest.len};
+}
+
+
+/**
+ @brief Copy a pointer into a UsefulBuf
+
+ @param[in,out] Dest The destination buffer to copy into
+ @param[in] ptr The source to copy from
+ @param[in] len Length of the source; amoutn to copy
+
+ @return 0 on success, 1 on failure
+
+ This fails and returns NULLUsefulBufC if len is greater than
+ pDest->len.
+
+ Note that like memcpy, the pointers are not checked and
+ this will crash, rather than return 1 if they are NULL
+ or invalid.
+
+ */
+inline static UsefulBufC UsefulBuf_CopyPtr(UsefulBuf Dest, const void *ptr, size_t len)
+{
+ return UsefulBuf_Copy(Dest, (UsefulBufC){ptr, len});
+}
+
+
+/**
+ @brief Returns a truncation of a UsefulBufC
+
+ @param[in] UB The buffer to get the head of
+ @param[in] uAmount The number of bytes in the head
+
+ @return A UsefulBufC that is the head of UB
+
+ */
+static inline UsefulBufC UsefulBuf_Head(UsefulBufC UB, size_t uAmount)
+{
+ if(uAmount > UB.len) {
+ return NULLUsefulBufC;
+ }
+ return (UsefulBufC){UB.ptr, uAmount};
+}
+
+
+/**
+ @brief Returns bytes from the end of a UsefulBufC
+
+ @param[in] UB The buffer to get the tail of
+ @param[in] uAmount The offset from the start where the tail is to begin
+
+ @return A UsefulBufC that is the tail of UB or NULLUsefulBufC if
+ uAmount is greater than the length of the UsefulBufC
+
+ If the input UsefulBufC is NULL, but the len is not, then the
+ length of the tail will be calculated and returned along
+ with a NULL ptr.
+ */
+static inline UsefulBufC UsefulBuf_Tail(UsefulBufC UB, size_t uAmount)
+{
+ UsefulBufC ReturnValue;
+
+ if(uAmount > UB.len) {
+ ReturnValue = NULLUsefulBufC;
+ } else if(UB.ptr == NULL) {
+ ReturnValue = (UsefulBufC){NULL, UB.len - uAmount};
+ } else {
+ ReturnValue = (UsefulBufC){(uint8_t *)UB.ptr + uAmount, UB.len - uAmount};
+ }
+
+ return ReturnValue;
+}
+
+
+/**
+ @brief Compare two UsefulBufCs
+
+ @param[in] UB1 The destination buffer to copy into
+ @param[in] UB2 The source to copy from
+
+ @return 0 if equal...
+
+ Returns a negative value if UB1 if is less than UB2. UB1 is
+ less than UB2 if it is shorter or the first byte that is not
+ the same is less.
+
+ Returns 0 if the UsefulBufs are the same.
+
+ Returns a positive value if UB2 is less than UB1.
+
+ All that is of significance is that the result is positive,
+ negative or 0. (This doesn't return the difference between
+ the first non-matching byte like memcmp).
+
+ */
+int UsefulBuf_Compare(const UsefulBufC UB1, const UsefulBufC UB2);
+
+
+/**
+ @brief Find one UsefulBuf in another
+
+ @param[in] BytesToSearch UsefulBuf to search through
+ @param[in] BytesToFind UsefulBuf with bytes to be found
+
+ @return position of found bytes or SIZE_MAX if not found.
+
+ */
+size_t UsefulBuf_FindBytes(UsefulBufC BytesToSearch, UsefulBufC BytesToFind);
+
+
+
+
+#if 0 // NOT_DEPRECATED
+/** Deprecated macro; use UsefulBuf_FROM_SZ_LITERAL instead */
+#define SZLiteralToUsefulBufC(szString) \
+ ((UsefulBufC) {(szString), sizeof(szString)-1})
+
+/** Deprecated macro; use UsefulBuf_MAKE_STACK_UB instead */
+#define MakeUsefulBufOnStack(name, size) \
+ uint8_t __pBuf##name[(size)];\
+ UsefulBuf name = {__pBuf##name , sizeof( __pBuf##name )}
+
+/** Deprecated macro; use UsefulBuf_FROM_BYTE_ARRAY_LITERAL instead */
+#define ByteArrayLiteralToUsefulBufC(pBytes) \
+ ((UsefulBufC) {(pBytes), sizeof(pBytes)})
+
+/** Deprecated function; use UsefulBuf_Unconst() instead */
+static inline UsefulBuf UsefulBufC_Unconst(const UsefulBufC UBC)
+{
+ return (UsefulBuf){(void *)UBC.ptr, UBC.len};
+}
+#endif
+
+
+
+/*
+ Convenient functions to avoid type punning, compiler warnings and such
+ The optimizer reduces them to a simple assignment
+ This is a crusty corner of C. It shouldn't be this hard.
+ */
+static inline uint32_t UsefulBufUtil_CopyFloatToUint32(float f)
+{
+ uint32_t u32;
+ memcpy(&u32, &f, sizeof(uint32_t));
+ return u32;
+}
+
+static inline uint64_t UsefulBufUtil_CopyDoubleToUint64(double d)
+{
+ uint64_t u64;
+ memcpy(&u64, &d, sizeof(uint64_t));
+ return u64;
+}
+
+static inline double UsefulBufUtil_CopyUint64ToDouble(uint64_t u64)
+{
+ double d;
+ memcpy(&d, &u64, sizeof(uint64_t));
+ return d;
+}
+
+static inline float UsefulBufUtil_CopyUint32ToFloat(uint32_t u32)
+{
+ float f;
+ memcpy(&f, &u32, sizeof(uint32_t));
+ return f;
+}
+
+
+
+
+
+/**
+ UsefulOutBuf is a structure and functions (an object) that are good
+ for serializing data into a buffer such as is often done with network
+ protocols or data written to files.
+
+ The main idea is that all the pointer manipulation for adding data is
+ done by UsefulOutBuf functions so the caller doesn't have to do any.
+ All the pointer manipulation is centralized here. This code will
+ have been reviewed and written carefully so it spares the caller of
+ much of this work and results in much safer code with much less work.
+
+ The functions to add data to the output buffer always check the
+ length and will never write off the end of the output buffer. If an
+ attempt to add data that will not fit is made, an internal error flag
+ will be set and further attempts to add data will not do anything.
+
+ Basically, if you initialized with the correct buffer, there is no
+ way to ever write off the end of that buffer when calling the Add
+ and Insert functions here.
+
+ The functions to add data do not return an error. The working model
+ is that the caller just makes all the calls to add data without any
+ error checking on each one. The error is instead checked after all the
+ data is added when the result is to be used. This makes the caller's
+ code cleaner.
+
+ There is a utility function to get the error status anytime along the
+ way if the caller wants. There are functions to see how much room is
+ left and see if some data will fit too, but their use is generally
+ not necessary.
+
+ The general call flow is like this:
+
+ - Initialize the UsefulOutBuf with the buffer that is to have the
+ data added. The caller allocates the buffer. It can be heap
+ or stack or shared memory (or other).
+
+ - Make calls to add data to the output buffer. Insert and append
+ are both supported. The append and insert calls will never write
+ off the end of the buffer.
+
+ - When all data is added, check the error status to make sure
+ everything fit.
+
+ - Get the resulting serialized data either as a UsefulBuf (a
+ pointer and length) or have it copied to another buffer.
+
+ UsefulOutBuf can be initialized with just a buffer length by passing
+ NULL as the pointer to the output buffer. This is useful if you want
+ to go through the whole serialization process to either see if it
+ will fit into a given buffer or compute the size of the buffer
+ needed. Pass a very large buffer size when calling Init, if you want
+ just to compute the size.
+
+ Some inexpensive simple sanity checks are performed before every data
+ addition to guard against use of an uninitialized or corrupted
+ UsefulOutBuf.
+
+ This has been used to create a CBOR encoder. The CBOR encoder has
+ almost no pointer manipulation in it, is much easier to read, and
+ easier to review.
+
+ A UsefulOutBuf is 27 bytes or 15 bytes on 64- or 32-bit machines so it
+ can go on the stack or be a C99 function parameter.
+ */
+
+typedef struct useful_out_buf {
+ UsefulBuf UB; // Memory that is being output to
+ size_t data_len; // length of the data
+ uint16_t magic; // Used to detect corruption and lack of initialization
+ uint8_t err;
+} UsefulOutBuf;
+
+
+/**
+ @brief Initialize and supply the actual output buffer
+
+ @param[out] me The UsefulOutBuf to initialize
+ @param[in] Storage Buffer to output into
+
+ Intializes the UsefulOutBuf with storage. Sets the current position
+ to the beginning of the buffer clears the error.
+
+ This must be called before the UsefulOutBuf is used.
+ */
+void UsefulOutBuf_Init(UsefulOutBuf *me, UsefulBuf Storage);
+
+
+
+
+/** Convenience marco to make a UsefulOutBuf on the stack and
+ initialize it with stack buffer
+ */
+#define UsefulOutBuf_MakeOnStack(name, size) \
+ uint8_t __pBuf##name[(size)];\
+ UsefulOutBuf name;\
+ UsefulOutBuf_Init(&(name), (UsefulBuf){__pBuf##name, (size)});
+
+
+
+/**
+ @brief Reset a UsefulOutBuf for re use
+
+ @param[in] me Pointer to the UsefulOutBuf
+
+ This sets the amount of data in the output buffer to none and
+ clears the error state.
+
+ The output buffer is still the same one and size as from the
+ UsefulOutBuf_Init() call.
+
+ It doesn't zero the data, just resets to 0 bytes of valid data.
+ */
+static inline void UsefulOutBuf_Reset(UsefulOutBuf *me)
+{
+ me->data_len = 0;
+ me->err = 0;
+}
+
+
+/**
+ @brief Returns position of end of data in the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+
+ @return position of end of data
+
+ On a freshly initialized UsefulOutBuf with no data added, this will
+ return 0. After ten bytes have been added, it will return 10 and so
+ on.
+
+ Generally callers will not need this function for most uses of
+ UsefulOutBuf.
+
+ */
+static inline size_t UsefulOutBuf_GetEndPosition(UsefulOutBuf *me)
+{
+ return me->data_len;
+}
+
+
+/**
+ @brief Returns whether any data has been added to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+
+ @return 1 if output position is at start
+
+ */
+static inline int UsefulOutBuf_AtStart(UsefulOutBuf *me)
+{
+ return 0 == me->data_len;
+}
+
+
+/**
+ @brief Inserts bytes into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] NewData UsefulBuf with the bytes to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ NewData is the pointer and length for the bytes to be added to the
+ output buffer. There must be room in the output buffer for all of
+ NewData or an error will occur.
+
+ The insertion point must be between 0 and the current valid data. If
+ not an error will occur. Appending data to the output buffer is
+ achieved by inserting at the end of the valid data. This can be
+ retrieved by calling UsefulOutBuf_GetEndPosition().
+
+ When insertion is performed, the bytes between the insertion point and
+ the end of data previously added to the output buffer is slid to the
+ right to make room for the new data.
+
+ Overlapping buffers are OK. NewData can point to data in the output
+ buffer.
+
+ If an error occurs an error state is set in the UsefulOutBuf. No
+ error is returned. All subsequent attempts to add data will do
+ nothing.
+
+ Call UsefulOutBuf_GetError() to find out if there is an error. This
+ is usually not needed until all additions of data are complete.
+
+ */
+void UsefulOutBuf_InsertUsefulBuf(UsefulOutBuf *me, UsefulBufC NewData, size_t uPos);
+
+
+/**
+ @brief Insert a data buffer into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBul
+ @param[in] pBytes Pointer to the bytes to insert
+ @param[in] uLen Length of the bytes to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with
+ the difference being a pointer and length is passed in rather than an
+ UsefulBuf.
+
+ */
+static inline void UsefulOutBuf_InsertData(UsefulOutBuf *me, const void *pBytes, size_t uLen, size_t uPos)
+{
+ UsefulBufC Data = {pBytes, uLen};
+ UsefulOutBuf_InsertUsefulBuf(me, Data, uPos);
+}
+
+
+/**
+ @brief Insert a NULL-terminated string into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] szString string to append
+
+ */
+static inline void UsefulOutBuf_InsertString(UsefulOutBuf *me, const char *szString, size_t uPos)
+{
+ UsefulOutBuf_InsertUsefulBuf(me, (UsefulBufC){szString, strlen(szString)}, uPos);
+}
+
+
+/**
+ @brief Insert a byte into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBul
+ @param[in] byte Bytes to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with
+ the difference being a single byte is to be inserted.
+ */
+static inline void UsefulOutBuf_InsertByte(UsefulOutBuf *me, uint8_t byte, size_t uPos)
+{
+ UsefulOutBuf_InsertData(me, &byte, 1, uPos);
+}
+
+
+/**
+ @brief Insert a 16-bit integer into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBul
+ @param[in] uInteger16 Integer to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with
+ the difference being a single byte is to be inserted.
+
+ The integer will be inserted in network byte order (big endian)
+ */
+static inline void UsefulOutBuf_InsertUint16(UsefulOutBuf *me, uint16_t uInteger16, size_t uPos)
+{
+ // Converts native integer format to network byte order (big endian)
+ uint8_t tmp[2];
+ tmp[0] = (uInteger16 & 0xff00) >> 8;
+ tmp[1] = (uInteger16 & 0xff);
+ UsefulOutBuf_InsertData(me, tmp, 2, uPos);
+}
+
+
+/**
+ @brief Insert a 32-bit integer into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBul
+ @param[in] uInteger32 Integer to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with
+ the difference being a single byte is to be inserted.
+
+ The integer will be inserted in network byte order (big endian)
+ */
+static inline void UsefulOutBuf_InsertUint32(UsefulOutBuf *me, uint32_t uInteger32, size_t uPos)
+{
+ // Converts native integer format to network byte order (big endian)
+ uint8_t tmp[4];
+ tmp[0] = (uInteger32 & 0xff000000) >> 24;
+ tmp[1] = (uInteger32 & 0xff0000) >> 16;
+ tmp[2] = (uInteger32 & 0xff00) >> 8;
+ tmp[3] = (uInteger32 & 0xff);
+ UsefulOutBuf_InsertData(me, tmp, 4, uPos);
+}
+
+
+/**
+ @brief Insert a 64-bit integer into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBul
+ @param[in] uInteger64 Integer to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with
+ the difference being a single byte is to be inserted.
+
+ The integer will be inserted in network byte order (big endian)
+ */
+static inline void UsefulOutBuf_InsertUint64(UsefulOutBuf *me, uint64_t uInteger64, size_t uPos)
+{
+ // Converts native integer format to network byte order (big endian)
+ uint8_t tmp[8];
+ tmp[0] = (uInteger64 & 0xff00000000000000) >> 56;
+ tmp[1] = (uInteger64 & 0xff000000000000) >> 48;
+ tmp[2] = (uInteger64 & 0xff0000000000) >> 40;
+ tmp[3] = (uInteger64 & 0xff00000000) >> 32;
+ tmp[4] = (uInteger64 & 0xff000000) >> 24;
+ tmp[5] = (uInteger64 & 0xff0000) >> 16;
+ tmp[6] = (uInteger64 & 0xff00) >> 8;
+ tmp[7] = (uInteger64 & 0xff);
+ UsefulOutBuf_InsertData(me, tmp, 8, uPos);
+}
+
+
+/**
+ @brief Insert a float into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBul
+ @param[in] f Integer to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with
+ the difference being a single byte is to be inserted.
+
+ The float will be inserted in network byte order (big endian)
+ */
+static inline void UsefulOutBuf_InsertFloat(UsefulOutBuf *me, float f, size_t uPos)
+{
+ UsefulOutBuf_InsertUint32(me, UsefulBufUtil_CopyFloatToUint32(f), uPos);
+}
+
+
+/**
+ @brief Insert a double into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBul
+ @param[in] d Integer to insert
+ @param[in] uPos Index in output buffer at which to insert
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with
+ the difference being a single byte is to be inserted.
+
+ The double will be inserted in network byte order (big endian)
+ */
+static inline void UsefulOutBuf_InsertDouble(UsefulOutBuf *me, double d, size_t uPos)
+{
+ UsefulOutBuf_InsertUint64(me, UsefulBufUtil_CopyDoubleToUint64(d), uPos);
+}
+
+
+
+/**
+ Append a UsefulBuf into the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] NewData UsefulBuf with the bytes to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+
+*/
+static inline void UsefulOutBuf_AppendUsefulBuf(UsefulOutBuf *me, UsefulBufC NewData)
+{
+ // An append is just a insert at the end
+ UsefulOutBuf_InsertUsefulBuf(me, NewData, UsefulOutBuf_GetEndPosition(me));
+}
+
+
+/**
+ Append bytes to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] pBytes Pointer to bytes to append
+ @param[in] uLen Index in output buffer at which to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+ */
+
+static inline void UsefulOutBuf_AppendData(UsefulOutBuf *me, const void *pBytes, size_t uLen)
+{
+ UsefulBufC Data = {pBytes, uLen};
+ UsefulOutBuf_AppendUsefulBuf(me, Data);
+}
+
+
+/**
+ Append a NULL-terminated string to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] szString string to append
+
+ */
+static inline void UsefulOutBuf_AppendString(UsefulOutBuf *me, const char *szString)
+{
+ UsefulOutBuf_AppendUsefulBuf(me, (UsefulBufC){szString, strlen(szString)});
+}
+
+
+/**
+ @brief Append a byte to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] byte Bytes to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+ */
+static inline void UsefulOutBuf_AppendByte(UsefulOutBuf *me, uint8_t byte)
+{
+ UsefulOutBuf_AppendData(me, &byte, 1);
+}
+
+/**
+ @brief Append an integer to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] uInteger16 Integer to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+
+ The integer will be appended in network byte order (big endian).
+ */
+static inline void UsefulOutBuf_AppendUint16(UsefulOutBuf *me, uint16_t uInteger16){
+ UsefulOutBuf_InsertUint16(me, uInteger16, UsefulOutBuf_GetEndPosition(me));
+}
+
+/**
+ @brief Append an integer to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] uInteger32 Integer to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+
+ The integer will be appended in network byte order (big endian).
+ */
+static inline void UsefulOutBuf_AppendUint32(UsefulOutBuf *me, uint32_t uInteger32){
+ UsefulOutBuf_InsertUint32(me, uInteger32, UsefulOutBuf_GetEndPosition(me));
+}
+
+/**
+ @brief Append an integer to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] uInteger64 Integer to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+
+ The integer will be appended in network byte order (big endian).
+ */
+static inline void UsefulOutBuf_AppendUint64(UsefulOutBuf *me, uint64_t uInteger64){
+ UsefulOutBuf_InsertUint64(me, uInteger64, UsefulOutBuf_GetEndPosition(me));
+}
+
+
+/**
+ @brief Append a float to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] f Float to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+
+ The float will be appended in network byte order (big endian).
+ */
+static inline void UsefulOutBuf_AppendFloat(UsefulOutBuf *me, float f){
+ UsefulOutBuf_InsertFloat(me, f, UsefulOutBuf_GetEndPosition(me));
+}
+
+/**
+ @brief Append a float to the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] d Double to append
+
+ See UsefulOutBuf_InsertUsefulBuf() for details. This does the same
+ with the insertion point at the end of the valid data.
+
+ The double will be appended in network byte order (big endian).
+ */
+static inline void UsefulOutBuf_AppendDouble(UsefulOutBuf *me, double d){
+ UsefulOutBuf_InsertDouble(me, d, UsefulOutBuf_GetEndPosition(me));
+}
+
+/**
+ @brief Returns the current error status
+
+ @param[in] me Pointer to the UsefulOutBuf
+
+ @return 0 if all OK, 1 on error
+
+ This is the error status since the call to either
+ UsefulOutBuf_Reset() of UsefulOutBuf_Init(). Once it goes into error
+ state it will stay until one of those functions is called.
+
+ Possible error conditions are:
+ - bytes to be inserted will not fit
+ - insertion point is out of buffer or past valid data
+ - current position is off end of buffer (probably corruption or uninitialized)
+ - detect corruption / uninitialized by bad magic number
+ */
+
+static inline int UsefulOutBuf_GetError(UsefulOutBuf *me)
+{
+ return me->err;
+}
+
+
+/**
+ @brief Returns number of bytes unused used in the output buffer
+
+ @param[in] me Pointer to the UsefulOutBuf
+
+ @return Number of unused bytes or zero
+
+ Because of the error handling strategy and checks in UsefulOutBuf_InsertUsefulBuf()
+ it is usually not necessary to use this.
+ */
+
+static inline size_t UsefulOutBuf_RoomLeft(UsefulOutBuf *me)
+{
+ return me->UB.len - me->data_len;
+}
+
+
+/**
+ @brief Returns true / false if some number of bytes will fit in the UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[in] uLen Number of bytes for which to check
+
+ @return 1 or 0 if nLen bytes would fit
+
+ Because of the error handling strategy and checks in UsefulOutBuf_InsertUsefulBuf()
+ it is usually not necessary to use this.
+ */
+
+static inline int UsefulOutBuf_WillItFit(UsefulOutBuf *me, size_t uLen)
+{
+ return uLen <= UsefulOutBuf_RoomLeft(me);
+}
+
+
+/**
+ @brief Returns the resulting valid data in a UsefulOutBuf
+
+ @param[in] me Pointer to the UsefulOutBuf.
+
+ @return The valid data in UsefulOutBuf.
+
+ The storage for the returned data is Storage parameter passed
+ to UsefulOutBuf_Init(). See also UsefulOutBuf_CopyOut().
+
+ This can be called anytime and many times to get intermediate
+ results. It doesn't change the data or reset the current position
+ so you can keep adding data.
+ */
+
+UsefulBufC UsefulOutBuf_OutUBuf(UsefulOutBuf *me);
+
+
+/**
+ @brief Copies the valid data out into a supplied buffer
+
+ @param[in] me Pointer to the UsefulOutBuf
+ @param[out] Dest The destination buffer to copy into
+
+ @return Pointer and length of copied data.
+
+ This is the same as UsefulOutBuf_OutUBuf() except it copies the data.
+*/
+
+UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *me, UsefulBuf Dest);
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ UsefulInputBuf is the counterpart to UsefulOutBuf and is for parsing
+ data read or received. Initialize it with the data
+ from the network and its length. Then use the functions
+ here to get the various data types out of it. It maintains a position
+ for getting the next item. This means you don't have to track a
+ pointer as you get each object. UsefulInputBuf does that for you and
+ makes sure it never goes off the end of the buffer. The QCBOR
+ implementation parser makes use of this for all its pointer math and
+ length checking.
+
+ UsefulInputBuf also maintains an internal error state so you do not have
+ to. Once data has been requested off the end of the buffer, it goes
+ into an error state. You can keep calling functions to get more data
+ but they will either return 0 or NULL. As long as you don't
+ dereference the NULL, you can wait until all data items have been
+ fetched before checking for the error and this can simplify your
+ code.
+
+ The integer and float parsing expects network byte order (big endian).
+ Network byte order is what is used by TCP/IP, CBOR and most internet
+ protocols.
+
+ Lots of inlining is used to keep code size down. The code optimizer,
+ particularly with the -Os, also reduces code size a lot. The only
+ non-inline code is UsefulInputBuf_GetBytes() which is less than 100
+ bytes so use of UsefulInputBuf doesn't add much code for all the messy
+ hard-to-get right issues with parsing in C that is solves.
+
+ The parse context size is:
+ 64-bit machine: 16 + 8 + 2 + 1 (5 bytes padding to align) = 32 bytes
+ 32-bit machine: 8 + 4 + 2 + 1 (1 byte padding to align) = 16 bytes
+
+ */
+
+#define UIB_MAGIC (0xB00F)
+
+typedef struct useful_input_buf {
+ // Private data structure
+ UsefulBufC UB; // Data being parsed
+ size_t cursor; // Current offset in data being parse
+ uint16_t magic; // Check for corrupted or uninitialized UsefulInputBuf
+ uint8_t err; // Set request goes off end or magic number is bad
+} UsefulInputBuf;
+
+
+
+/**
+ @brief Initialize the UsefulInputBuf structure before use.
+
+ @param[in] me Pointer to the UsefulInputBuf instance.
+ @param[in] UB Pointer to the data to parse.
+
+ */
+static inline void UsefulInputBuf_Init(UsefulInputBuf *me, UsefulBufC UB)
+{
+ me->cursor = 0;
+ me->err = 0;
+ me->magic = UIB_MAGIC;
+ me->UB = UB;
+}
+
+
+/**
+ @brief Returns current position in input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return Integer position of the cursor
+
+ The position that the next bytes will be returned from.
+
+ */
+static inline size_t UsefulInputBuf_Tell(UsefulInputBuf *me)
+{
+ return me->cursor;
+}
+
+
+/**
+ @brief Sets current position in input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+ @param[in] uPos Position to set to
+
+ If the position is off the end of the input buffer, the error state
+ is entered and all functions will do nothing.
+
+ Seeking to a valid position in the buffer will not reset the error
+ state. Only re initialization will do that.
+
+ */
+static inline void UsefulInputBuf_Seek(UsefulInputBuf *me, size_t uPos)
+{
+ if(uPos > me->UB.len) {
+ me->err = 1;
+ } else {
+ me->cursor = uPos;
+ }
+}
+
+
+/**
+ @brief Returns the number of bytes from the cursor to the end of the buffer,
+ the uncomsummed bytes.
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return number of bytes unconsumed or 0 on error.
+
+ This is a critical function for input length validation. This does
+ some pointer / offset math.
+
+ Returns 0 if the cursor it invalid or corruption of the structure is
+ detected.
+
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+static inline size_t UsefulInputBuf_BytesUnconsumed(UsefulInputBuf *me)
+{
+ // Magic number is messed up. Either the structure got overwritten
+ // or was never initialized.
+ if(me->magic != UIB_MAGIC) {
+ return 0;
+ }
+
+ // The cursor is off the end of the input buffer given
+ // Presuming there are no bugs in this code, this should never happen.
+ // If it so, the struct was corrupted. The check is retained as
+ // as a defense in case there is a bug in this code or the struct is corrupted.
+ if(me->cursor > me->UB.len) {
+ return 0;
+ }
+
+ // subtraction can't go neative because of check above
+ return me->UB.len - me->cursor;
+}
+
+
+/**
+ @brief Check if there are any unconsumed bytes
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return 1 if len bytes are available after the cursor, and 0 if not
+
+ */
+static inline int UsefulInputBuf_BytesAvailable(UsefulInputBuf *me, size_t uLen)
+{
+ return UsefulInputBuf_BytesUnconsumed(me) >= uLen ? 1 : 0;
+}
+
+
+/**
+ @brief Get pointer to bytes out of the input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+ @param[in] uNum Number of bytes to get
+
+ @return Pointer to bytes.
+
+ This consumes n bytes from the input buffer. It returns a pointer to
+ the start of the n bytes.
+
+ If there are not n bytes in the input buffer, NULL will be returned
+ and an error will be set.
+
+ It advances the current position by n bytes.
+ */
+const void * UsefulInputBuf_GetBytes(UsefulInputBuf *me, size_t uNum);
+
+
+/**
+ @brief Get UsefulBuf out of the input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+ @param[in] uNum Number of bytes to get
+
+ @return UsefulBufC with ptr and length for bytes consumed.
+
+ This consumes n bytes from the input buffer and returns the pointer
+ and len to them as a UsefulBufC. The len returned will always be n.
+
+ If there are not n bytes in the input buffer, UsefulBufC.ptr will be
+ NULL and UsefulBufC.len will be 0. An error will be set.
+
+ It advances the current position by n bytes.
+ */
+static inline UsefulBufC UsefulInputBuf_GetUsefulBuf(UsefulInputBuf *me, size_t uNum)
+{
+ const void *pResult = UsefulInputBuf_GetBytes(me, uNum);
+ if(!pResult) {
+ return NULLUsefulBufC;
+ } else {
+ return (UsefulBufC){pResult, uNum};
+ }
+}
+
+
+/**
+ @brief Get a byte out of the input buffer.
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return The byte
+
+ This consumes 1 byte from the input buffer. It returns the byte.
+
+ If there is not 1 byte in the buffer, 0 will be returned for the byte
+ and an error set internally. You must check the error at some point
+ to know whether the 0 was the real value or just returned in error,
+ but you may not have to do that right away. Check the error state
+ with UsefulInputBuf_GetError(). You can also know you are in the
+ error state if UsefulInputBuf_GetBytes() returns NULL or the ptr from
+ UsefulInputBuf_GetUsefulBuf() is NULL.
+
+ It advances the current position by 1 byte.
+ */
+static inline uint8_t UsefulInputBuf_GetByte(UsefulInputBuf *me)
+{
+ const void *pResult = UsefulInputBuf_GetBytes(me, sizeof(uint8_t));
+
+ return pResult ? *(uint8_t *)pResult : 0;
+}
+
+
+/**
+ @brief Get a uint16_t out of the input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return The uint16_t
+
+ See UsefulInputBuf_GetByte(). This works the same, except it returns
+ a uint16_t and two bytes are consumed.
+
+ The input bytes must be in network order (big endian).
+ */
+static inline uint16_t UsefulInputBuf_GetUint16(UsefulInputBuf *me)
+{
+ const uint8_t *pResult = (const uint8_t *)UsefulInputBuf_GetBytes(me, sizeof(uint16_t));
+
+ if(!pResult) {
+ return 0;
+ }
+
+ return ((uint16_t)pResult[0] << 8) + (uint16_t)pResult[1];
+}
+
+
+/**
+ @brief Get a uint32_t out of the input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return The uint32_t
+
+ See UsefulInputBuf_GetByte(). This works the same, except it returns
+ a uint32_t and four bytes are consumed.
+
+ The input bytes must be in network order (big endian).
+ */
+static inline uint32_t UsefulInputBuf_GetUint32(UsefulInputBuf *me)
+{
+ const uint8_t *pResult = (const uint8_t *)UsefulInputBuf_GetBytes(me, sizeof(uint32_t));
+
+ if(!pResult) {
+ return 0;
+ }
+
+ return ((uint32_t)pResult[0]<<24) +
+ ((uint32_t)pResult[1]<<16) +
+ ((uint32_t)pResult[2]<<8) +
+ (uint32_t)pResult[3];
+}
+
+
+/**
+ @brief Get a uint64_t out of the input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return The uint64_t
+
+ See UsefulInputBuf_GetByte(). This works the same, except it returns
+ a uint64_t and eight bytes are consumed.
+
+ The input bytes must be in network order (big endian).
+ */
+static inline uint64_t UsefulInputBuf_GetUint64(UsefulInputBuf *me)
+{
+ const uint8_t *pResult = (const uint8_t *)UsefulInputBuf_GetBytes(me, sizeof(uint64_t));
+
+ if(!pResult) {
+ return 0;
+ }
+
+ return ((uint64_t)pResult[0]<<56) +
+ ((uint64_t)pResult[1]<<48) +
+ ((uint64_t)pResult[2]<<40) +
+ ((uint64_t)pResult[3]<<32) +
+ ((uint64_t)pResult[4]<<24) +
+ ((uint64_t)pResult[5]<<16) +
+ ((uint64_t)pResult[6]<<8) +
+ (uint64_t)pResult[7];
+}
+
+
+/**
+ @brief Get a float out of the input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return The float
+
+ See UsefulInputBuf_GetByte(). This works the same, except it returns
+ a float and four bytes are consumed.
+
+ The input bytes must be in network order (big endian).
+ */
+static inline float UsefulInputBuf_GetFloat(UsefulInputBuf *me)
+{
+ uint32_t uResult = UsefulInputBuf_GetUint32(me);
+
+ return uResult ? UsefulBufUtil_CopyUint32ToFloat(uResult) : 0;
+}
+
+/**
+ @brief Get a double out of the input buffer
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return The double
+
+ See UsefulInputBuf_GetByte(). This works the same, except it returns
+ a double and eight bytes are consumed.
+
+ The input bytes must be in network order (big endian).
+ */
+static inline double UsefulInputBuf_GetDouble(UsefulInputBuf *me)
+{
+ uint64_t uResult = UsefulInputBuf_GetUint64(me);
+
+ return uResult ? UsefulBufUtil_CopyUint64ToDouble(uResult) : 0;
+}
+
+
+/**
+ @brief Get the error status
+
+ @param[in] me Pointer to the UsefulInputBuf.
+
+ @return The error.
+
+ Zero is success, non-zero is error. Once in the error state, the only
+ way to clear it is to call Init again.
+
+ You may be able to only check the error state at the end after all
+ the Get()'s have been done, but if what you get later depends on what
+ you get sooner you cannot. For example if you get a length or count
+ of following items you will have to check the error.
+
+ */
+static inline int UsefulInputBuf_GetError(UsefulInputBuf *me)
+{
+ return me->err;
+}
+
+
+#endif // _UsefulBuf_h
+
+
diff --git a/lib/ext/qcbor/inc/qcbor.h b/lib/ext/qcbor/inc/qcbor.h
new file mode 100644
index 0000000..3011cc6
--- /dev/null
+++ b/lib/ext/qcbor/inc/qcbor.h
@@ -0,0 +1,2597 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+
+/*===================================================================================
+ FILE: qcbor.h
+
+ DESCRIPTION: This is the full public API and data structures for QCBOR
+
+ EDIT HISTORY FOR FILE:
+
+ This section contains comments describing changes made to the module.
+ Notice that changes are listed in reverse chronological order.
+
+ when who what, where, why
+ -------- ---- ---------------------------------------------------
+ 12/18/18 llundblade Move decode malloc optional code to separate repository
+ 12/13/18 llundblade Documentatation improvements
+ 11/29/18 llundblade Rework to simpler handling of tags and labels.
+ 11/9/18 llundblade Error codes are now enums.
+ 11/1/18 llundblade Floating support.
+ 10/31/18 llundblade Switch to one license that is almost BSD-3.
+ 10/15/18 llundblade Indefinite length maps and arrays supported
+ 10/8/18 llundblade Indefinite length strings supported
+ 09/28/18 llundblade Added bstr wrapping feature for COSE implementation.
+ 07/05/17 llundbla Add bstr wrapping of maps/arrays for COSE.
+ 03/01/17 llundbla More data types; decoding improvements and fixes.
+ 11/13/16 llundbla Integrate most TZ changes back into github version.
+ 09/30/16 gkanike Porting to TZ.
+ 03/15/16 llundbla Initial Version.
+
+ =====================================================================================*/
+
+#ifndef __QCBOR__qcbor__
+#define __QCBOR__qcbor__
+
+/*...... This is a ruler that is 80 characters long...........................*/
+
+/* ===========================================================================
+ BEGINNING OF PRIVATE PART OF THIS FILE
+
+ Caller of QCBOR should not reference any of the details below up until
+ the start of the public part.
+ =========================================================================== */
+
+/*
+ Standard integer types are used in the interface to be precise about
+ sizes to be better at preventing underflow/overflow errors.
+ */
+#include <stdint.h>
+#include <stdbool.h>
+#include "UsefulBuf.h"
+
+
+/*
+ The maxium nesting of arrays and maps when encoding or decoding.
+ (Further down in the file there is a definition that refers to this
+ that is public. This is done this way so there can be a nice
+ separation of public and private parts in this file.
+*/
+#define QCBOR_MAX_ARRAY_NESTING1 15 // Do not increase this over 255
+
+
+/* The largest offset to the start of an array or map. It is slightly
+ less than UINT32_MAX so the error condition can be tests on 32-bit machines.
+ UINT32_MAX comes from uStart in QCBORTrackNesting being a uin32_t.
+
+ This will cause trouble on a machine where size_t is less than 32-bits.
+ */
+#define QCBOR_MAX_ARRAY_OFFSET (UINT32_MAX - 100)
+
+/*
+ PRIVATE DATA STRUCTURE
+
+ Holds the data for tracking array and map nesting during encoding. Pairs up with
+ the Nesting_xxx functions to make an "object" to handle nesting encoding.
+
+ uStart is a uint32_t instead of a size_t to keep the size of this
+ struct down so it can be on the stack without any concern. It would be about
+ double if size_t was used instead.
+
+ Size approximation (varies with CPU/compiler):
+ 64-bit machine: (15 + 1) * (4 + 2 + 1 + 1 pad) + 8 = 136 bytes
+ 32-bit machine: (15 + 1) * (4 + 2 + 1 + 1 pad) + 4 = 132 bytes
+*/
+typedef struct __QCBORTrackNesting {
+ // PRIVATE DATA STRUCTURE
+ struct {
+ // See function OpenArrayInternal() for detailed comments on how this works
+ uint32_t uStart; // uStart is the byte position where the array starts
+ uint16_t uCount; // Number of items in the arrary or map; counts items in a map, not pairs of items
+ uint8_t uMajorType; // Indicates if item is a map or an array
+ } pArrays[QCBOR_MAX_ARRAY_NESTING1+1], // stored state for the nesting levels
+ *pCurrentNesting; // the current nesting level
+} QCBORTrackNesting;
+
+
+/*
+ PRIVATE DATA STRUCTURE
+
+ Context / data object for encoding some CBOR. Used by all encode functions to
+ form a public "object" that does the job of encdoing.
+
+ Size approximation (varies with CPU/compiler):
+ 64-bit machine: 27 + 1 (+ 4 padding) + 136 = 32 + 136 = 168 bytes
+ 32-bit machine: 15 + 1 + 132 = 148 bytes
+*/
+struct _QCBOREncodeContext {
+ // PRIVATE DATA STRUCTURE
+ UsefulOutBuf OutBuf; // Pointer to output buffer, its length and position in it
+ uint8_t uError; // Error state
+ QCBORTrackNesting nesting; // Keep track of array and map nesting
+};
+
+
+/*
+ PRIVATE DATA STRUCTURE
+
+ Holds the data for array and map nesting for decoding work. This structure
+ and the DecodeNesting_xxx functions form an "object" that does the work
+ for arrays and maps.
+
+ Size approximation (varies with CPU/compiler):
+ 64-bit machine: 4 * 16 + 8 = 72
+ 32-bit machine: 4 * 16 + 4 = 68
+ */
+typedef struct __QCBORDecodeNesting {
+ // PRIVATE DATA STRUCTURE
+ struct {
+ uint16_t uCount;
+ uint8_t uMajorType;
+ } pMapsAndArrays[QCBOR_MAX_ARRAY_NESTING1+1],
+ *pCurrent;
+} QCBORDecodeNesting;
+
+
+/*
+ PRIVATE DATA STRUCTURE
+
+ The decode context. This data structure plus the public QCBORDecode_xxx
+ functions form an "object" that does CBOR decoding.
+
+ Size approximation (varies with CPU/compiler):
+ 64-bit machine: 32 + 1 + 1 + 6 bytes padding + 72 + 16 = 128 bytes
+ 32-bit machine: 16 + 1 + 1 + 2 bytes padding + 68 + 8 = 68 bytes
+ */
+struct _QCBORDecodeContext {
+ // PRIVATE DATA STRUCTURE
+ UsefulInputBuf InBuf;
+
+ uint8_t uDecodeMode;
+ uint8_t bStringAllocateAll;
+
+ QCBORDecodeNesting nesting;
+
+ // This is NULL or points to a QCBORStringAllocator. It is void
+ // here because _QCBORDecodeContext is defined early in the
+ // private part of this file and QCBORStringAllocat is defined
+ // later in the public part of this file.
+ void *pStringAllocator;
+
+ // This is NULL or points to QCBORTagList.
+ // It is type void for the same reason as above.
+ const void *pCallerConfiguredTagList;
+};
+
+// Used internally in the impementation here
+// Must not conflict with any of the official CBOR types
+#define CBOR_MAJOR_NONE_TYPE_RAW 9
+#define CBOR_MAJOR_NONE_TAG_LABEL_REORDER 10
+
+
+/* ===========================================================================
+ END OF PRIVATE PART OF THIS FILE
+
+ BEGINNING OF PUBLIC PART OF THIS FILE
+ =========================================================================== */
+
+
+
+/* ===========================================================================
+ BEGINNING OF CONSTANTS THAT COME FROM THE CBOR STANDARD, RFC 7049
+
+ It is not necessary to use these directly when encoding or decoding
+ CBOR with this implementation.
+ =========================================================================== */
+
+/* Standard CBOR Major type for positive integers of various lengths */
+#define CBOR_MAJOR_TYPE_POSITIVE_INT 0
+
+/* Standard CBOR Major type for negative integer of various lengths */
+#define CBOR_MAJOR_TYPE_NEGATIVE_INT 1
+
+/* Standard CBOR Major type for an array of arbitrary 8-bit bytes. */
+#define CBOR_MAJOR_TYPE_BYTE_STRING 2
+
+/* Standard CBOR Major type for a UTF-8 string. Note this is true 8-bit UTF8
+ with no encoding and no NULL termination */
+#define CBOR_MAJOR_TYPE_TEXT_STRING 3
+
+/* Standard CBOR Major type for an ordered array of other CBOR data items */
+#define CBOR_MAJOR_TYPE_ARRAY 4
+
+/* Standard CBOR Major type for CBOR MAP. Maps an array of pairs. The
+ first item in the pair is the "label" (key, name or identfier) and the second
+ item is the value. */
+#define CBOR_MAJOR_TYPE_MAP 5
+
+/* Standard CBOR optional tagging. This tags things like dates and URLs */
+#define CBOR_MAJOR_TYPE_OPTIONAL 6
+
+/* Standard CBOR extra simple types like floats and the values true and false */
+#define CBOR_MAJOR_TYPE_SIMPLE 7
+
+
+/*
+ These are special values for the AdditionalInfo bits that are part of the first byte.
+ Mostly they encode the length of the data item.
+ */
+#define LEN_IS_ONE_BYTE 24
+#define LEN_IS_TWO_BYTES 25
+#define LEN_IS_FOUR_BYTES 26
+#define LEN_IS_EIGHT_BYTES 27
+#define ADDINFO_RESERVED1 28
+#define ADDINFO_RESERVED2 29
+#define ADDINFO_RESERVED3 30
+#define LEN_IS_INDEFINITE 31
+
+
+/*
+ 24 is a special number for CBOR. Integers and lengths
+ less than it are encoded in the same byte as the major type
+ */
+#define CBOR_TWENTY_FOUR 24
+
+
+/*
+ Tags that are used with CBOR_MAJOR_TYPE_OPTIONAL. These are
+ the ones defined in the CBOR spec.
+ */
+/** See QCBOREncode_AddDateString() below */
+#define CBOR_TAG_DATE_STRING 0
+/** See QCBOREncode_AddDateEpoch_2() */
+#define CBOR_TAG_DATE_EPOCH 1
+#define CBOR_TAG_POS_BIGNUM 2
+#define CBOR_TAG_NEG_BIGNUM 3
+#define CBOR_TAG_FRACTION 4
+#define CBOR_TAG_BIGFLOAT 5
+
+#define CBOR_TAG_COSE_ENCRYPTO 16
+#define CBOR_TAG_COSE_MAC0 17
+#define CBOR_TAG_COSE_SIGN1 18
+
+/* The data in byte string should be converted in base 64 URL when encoding in JSON or similar text-based representations */
+#define CBOR_TAG_ENC_AS_B64URL 21
+/* The data in byte string should be encoded in base 64 when encoding in JSON */
+#define CBOR_TAG_ENC_AS_B64 22
+/* The data in byte string should be encoded in base 16 when encoding in JSON */
+#define CBOR_TAG_ENC_AS_B16 23
+#define CBOR_TAG_CBOR 24
+/** The data in the string is a URIs, as defined in RFC3986 */
+#define CBOR_TAG_URI 32
+/** The data in the string is a base 64'd URL */
+#define CBOR_TAG_B64URL 33
+/** The data in the string is base 64'd */
+#define CBOR_TAG_B64 34
+/** regular expressions in Perl Compatible Regular Expressions (PCRE) / JavaScript syntax ECMA262. */
+#define CBOR_TAG_REGEX 35
+/** MIME messages (including all headers), as defined in RFC2045 */
+#define CBOR_TAG_MIME 36
+/** Binary UUID */
+#define CBOR_TAG_BIN_UUID 37
+
+#define CBOR_TAG_CWT 61
+
+#define CBOR_TAG_ENCRYPT 96
+#define CBOR_TAG_MAC 97
+#define CBOR_TAG_SIGN 98
+
+#define CBOR_TAG_GEO_COORD 103
+
+
+/** The data is CBOR data */
+#define CBOR_TAG_CBOR_MAGIC 55799
+#define CBOR_TAG_NONE UINT64_MAX
+
+
+/*
+ Values for the 5 bits for items of major type 7
+ */
+#define CBOR_SIMPLEV_FALSE 20
+#define CBOR_SIMPLEV_TRUE 21
+#define CBOR_SIMPLEV_NULL 22
+#define CBOR_SIMPLEV_UNDEF 23
+#define CBOR_SIMPLEV_ONEBYTE 24
+#define HALF_PREC_FLOAT 25
+#define SINGLE_PREC_FLOAT 26
+#define DOUBLE_PREC_FLOAT 27
+#define CBOR_SIMPLE_BREAK 31
+
+
+
+/* ===========================================================================
+
+ END OF CONSTANTS THAT COME FROM THE CBOR STANDARD, RFC 7049
+
+ BEGINNING OF PUBLIC INTERFACE FOR QCBOR ENCODER / DECODER
+
+ =========================================================================== */
+
+/**
+
+ @file qcbor.h
+
+ Q C B O R E n c o d e / D e c o d e
+
+ This implements CBOR -- Concise Binary Object Representation as defined
+ in RFC 7049. More info is at http://cbor.io. This is a near-complete
+ implementation of the specification. Limitations are listed further down.
+
+ CBOR is intentionally designed to be translatable to JSON, but not
+ all CBOR can convert to JSON. See RFC 7049 for more info on how to
+ construct CBOR that is the most JSON friendly.
+
+ The memory model for encoding and decoding is that encoded CBOR
+ must be in a contiguous buffer in memory. During encoding the
+ caller must supply an output buffer and if the encoding would go
+ off the end of the buffer an error is returned. During decoding
+ the caller supplies the encoded CBOR in a contiguous buffer
+ and the decoder returns pointers and lengths into that buffer
+ for strings.
+
+ This implementation does not require malloc. All data structures
+ passed in/out of the APIs can fit on the stack.
+
+ Decoding of indefinite length strings is a special case that requires
+ a "string allocator" to allocate memory into which the segments of
+ the string are coalesced. Without this, decoding will error out if
+ an indefinite length string is encountered (indefinite length maps
+ and arrays do not require the string allocator). A simple string
+ allocator called MemPool is built-in and will work if supplied with
+ a block of memory to allocate. The string allocator can optionally
+ use malloc() or some other custom scheme.
+
+ Here are some terms and definitions:
+
+ - "Item", "Data Item": An integer or string or such. The basic "thing" that
+ CBOR is about. An array is an item itself that contains some items.
+
+ - "Array": An ordered sequence of items, the same as JSON.
+
+ - "Map": A collection of label/value pairs. Each pair is a data
+ item. A JSON "object" is the same as a CBOR "map".
+
+ - "Label": The data item in a pair in a map that names or identifies the
+ pair, not the value. This implementation refers to it as a "label".
+ JSON refers to it as the "name". The CBOR RFC refers to it this as a "key".
+ This implementation chooses label instead because key is too easily confused
+ with a cryptographic key. The COSE standard, which uses CBOR, has also
+ chosen to use the term "label" rather than "key" for this same reason.
+
+ - "Key": See "Label" above.
+
+ - "Tag": Optional info that can be added before each data item. This is always
+ CBOR major type 6.
+
+ - "Initial Byte": The first byte of an encoded item. Encoding and decoding of
+ this byte is taken care of by the implementation.
+
+ - "Additional Info": In addition to the major type, all data items have some
+ other info. This is usually the length of the data, but can be several
+ other things. Encoding and decoding of this is taken care of by the
+ implementation.
+
+ CBOR has two mechanisms for tagging and labeling the data
+ values like integers and strings. For example, an integer that
+ represents someone's birthday in epoch seconds since Jan 1, 1970
+ could be encoded like this:
+
+ - First it is CBOR_MAJOR_TYPE_POSITIVE_INT, the primitive positive
+ integer.
+ - Next it has a "tag" CBOR_TAG_DATE_EPOCH indicating the integer
+ represents a date in the form of the number of seconds since
+ Jan 1, 1970.
+ - Last it has a string "label" like "BirthDate" indicating
+ the meaning of the data.
+
+ The encoded binary looks like this:
+ a1 # Map of 1 item
+ 69 # Indicates text string of 9 bytes
+ 426972746844617465 # The text "BirthDate"
+ c1 # Tags next int as epoch date
+ 1a # Indicates 4 byte integer
+ 580d4172 # unsigned integer date 1477263730
+
+ Implementors using this API will primarily work with labels. Generally
+ tags are only needed for making up new data types. This implementation
+ covers most of the data types defined in the RFC using tags. It also,
+ allows for the creation of news tags if necessary.
+
+ This implementation explicitly supports labels that are text strings
+ and integers. Text strings translate nicely into JSON objects and
+ are very readable. Integer labels are much less readable, but
+ can be very compact. If they are in the range of -23 to
+ 23 they take up only one byte.
+
+ CBOR allows a label to be any type of data including an array or
+ a map. It is possible to use this API to construct and
+ parse such labels, but it is not explicitly supported.
+
+ A common encoding usage mode is to invoke the encoding twice. First
+ with no output buffer to compute the length of the needed output
+ buffer. Then the correct sized output buffer is allocated. Last the
+ encoder is invoked again, this time with the output buffer.
+
+ The double invocation is not required if the max output buffer size
+ can be predicted. This is usually possible for simple CBOR structures.
+ If the double invocation is implemented, it can be
+ in a loop or function as in the example code so that the code doesn't
+ have to actually be written twice, saving code size.
+
+ If a buffer too small to hold the encoded output is given, the error
+ QCBOR_ERR_BUFFER_TOO_SMALL will be returned. Data will never be
+ written off the end of the output buffer no matter which functions
+ here are called or what parameters are passed to them.
+
+ The error handling is simple. The only possible errors are trying to
+ encode structures that are too large or too complex. There are no
+ internal malloc calls so there will be no failures for out of memory.
+ Only the final call, QCBOREncode_Finish(), returns an error code.
+ Once an error happens, the encoder goes into an error state and calls
+ to it will do nothing so the encoding can just go on. An error
+ check is not needed after every data item is added.
+
+ Encoding generally proceeds by calling QCBOREncode_Init(), calling
+ lots of "Add" functions and calling QCBOREncode_Finish(). There
+ are many "Add" functions for various data types. The input
+ buffers need only to be valid during the "Add" calls. The
+ data is copied into the output buf during the "Add" call.
+
+ There are three `Add` functions for each data type. The first
+ / main one for the type is for adding the data item to an array.
+ The second one's name ends in `ToMap`, is used for adding
+ data items to maps and takes a string
+ argument that is its label in the map. The third one ends in
+ `ToMapN`, is also used for adding data items to maps, and
+ takes an integer argument that is its label in the map.
+
+ The simplest aggregate type is an array, which is a simple ordered
+ set of items without labels the same as JSON arrays. Call
+ QCBOREncode_OpenArray() to open a new array, then "Add" to
+ put items in the array and then QCBOREncode_CloseArray(). Nesting
+ to a limit is allowed. All opens must be matched by closes or an
+ encoding error will be returned.
+
+ The other aggregate type is a map which does use labels. The
+ `Add` functions that end in `ToMap` and `ToMapN` are convenient
+ ways to add labeled data items to a map. You can also call
+ any type of `Add` function once to add a label of any time and
+ then call any type of `Add` again to add its value.
+
+ Note that when you nest arrays or maps in a map, the nested
+ array or map has a label.
+
+ Usually it is not necessary to add tags explicitly as most
+ tagged types have functions here, but they can be added by
+ calling QCBOREncode_AddTag(). There is an IANA registry for new tags that are
+ for broad use and standardization as per RFC 7049. It is also
+ allowed for protocols to make up new tags in the range above 256.
+ Note that even arrays and maps can be tagged.
+
+ Summary Limits of this implementation:
+ - The entire encoded CBOR must fit into contiguous memory.
+ - Max size of encoded / decoded CBOR data is UINT32_MAX (4GB).
+ - Max array / map nesting level when encoding / decoding is
+ QCBOR_MAX_ARRAY_NESTING (this is typically 15).
+ - Max items in an array or map when encoding / decoding is
+ QCBOR_MAX_ITEMS_IN_ARRAY (typically 65,536).
+ - Does not support encoding indefinite lengths (decoding is supported).
+ - Does not directly support some tagged types: decimal fractions, big floats
+ - Does not directly support labels in maps other than text strings and ints.
+ - Does not directly support int labels greater than INT64_MAX
+ - Epoch dates limited to INT64_MAX (+/- 292 billion years)
+ - Tags on labels are ignored during decoding
+
+ This implementation is intended to run on 32 and 64-bit CPUs. Minor
+ modifications are needed for it to work on 16-bit CPUs.
+
+ The public interface uses size_t for all lengths. Internally the
+ implementation uses 32-bit lengths by design to use less memory and
+ fit structures on the stack. This limits the encoded
+ CBOR it can work with to size UINT32_MAX (4GB) which should be
+ enough.
+
+ This implementation assumes two's compliment integer
+ machines. Stdint.h also requires this. It of course would be easy to
+ fix this implementation for another integer representation, but all
+ modern machines seem to be two's compliment.
+
+ */
+
+
+/**
+ The maximum number of items in a single array or map when encoding of decoding.
+*/
+// -1 is because the value UINT16_MAX is used to track indefinite length arraysUINT16_MAX
+#define QCBOR_MAX_ITEMS_IN_ARRAY (UINT16_MAX-1)
+
+/**
+ The maximum nesting of arrays and maps when encoding or decoding. The
+ error QCBOR_ERR_ARRAY_NESTING_TOO_DEEP will be returned on encoding
+ of decoding if it is exceeded
+*/
+#define QCBOR_MAX_ARRAY_NESTING QCBOR_MAX_ARRAY_NESTING1
+
+/**
+ The maximum number of tags that can be in QCBORTagListIn and passed to
+ QCBORDecode_SetCallerConfiguredTagList()
+ */
+#define QCBOR_MAX_CUSTOM_TAGS 16
+
+
+typedef enum {
+ /** The encode or decode completely correctly. */
+ QCBOR_SUCCESS = 0,
+
+ /** The buffer provided for the encoded output when doing encoding was
+ too small and the encoded output will not fit. Also, when the buffer
+ given to QCBORDecode_SetMemPool() is too small. */
+ QCBOR_ERR_BUFFER_TOO_SMALL,
+
+ /** During encoding or decoding, the array or map nesting was deeper than
+ this implementation can handle. Note that in the interest of code size
+ and memory use, this implementation has a hard limit on array nesting. The
+ limit is defined as the constant QCBOR_MAX_ARRAY_NESTING. */
+ QCBOR_ERR_ARRAY_NESTING_TOO_DEEP,
+
+ /** During decoding or encoding, the array or map had too many items in it.
+ This limit QCBOR_MAX_ITEMS_IN_ARRAY, typically 65,535. */
+ QCBOR_ERR_ARRAY_TOO_LONG,
+
+ /** During encoding, more arrays or maps were closed than opened. This is a
+ coding error on the part of the caller of the encoder. */
+ QCBOR_ERR_TOO_MANY_CLOSES,
+
+ /** During decoding, some CBOR construct was encountered that this decoder
+ doesn't support, primarily this is the reserved additional info values,
+ 28 through 30. */
+ QCBOR_ERR_UNSUPPORTED,
+
+ /** During decoding, hit the end of the given data to decode. For example,
+ a byte string of 100 bytes was expected, but the end of the input was
+ hit before finding those 100 bytes. Corrupted CBOR input will often
+ result in this error. */
+ QCBOR_ERR_HIT_END,
+
+ /** During encoding, the length of the encoded CBOR exceeded UINT32_MAX.
+ */
+ QCBOR_ERR_BUFFER_TOO_LARGE,
+
+ /** During decoding, an integer smaller than INT64_MIN was received (CBOR
+ can represent integers smaller than INT64_MIN, but C cannot). */
+ QCBOR_ERR_INT_OVERFLOW,
+
+ /** During decoding, the label for a map entry is bad. What causes this
+ error depends on the decoding mode. */
+ QCBOR_ERR_MAP_LABEL_TYPE,
+
+ /** During encoding or decoding, the number of array or map opens was not
+ matched by the number of closes. */
+ QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN,
+
+ /** During encoding, the simple value is not between CBOR_SIMPLEV_FALSE
+ and CBOR_SIMPLEV_UNDEF. */
+ QCBOR_ERR_BAD_SIMPLE,
+
+ /** During decoding, a date greater than +- 292 billion years from Jan 1
+ 1970 encountered during parsing. */
+ QCBOR_ERR_DATE_OVERFLOW,
+
+ /** During decoding, the CBOR is not valid, primarily a simple type is encoded in
+ a prohibited way. */
+ QCBOR_ERR_INVALID_CBOR,
+
+ /** Optional tagging that doesn't make sense (an int is tagged as a
+ date string) or can't be handled. */
+ QCBOR_ERR_BAD_OPT_TAG,
+
+ /** Returned by QCBORDecode_Finish() if all the inputs bytes have not
+ been consumed. */
+ QCBOR_ERR_EXTRA_BYTES,
+
+ /** During encoding, QCBOREncode_Close() call with a different type than
+ is currently open. */
+ QCBOR_ERR_CLOSE_MISMATCH,
+
+ /** Unable to decode an indefinite length string because no string
+ allocator was configured. */
+ QCBOR_ERR_NO_STRING_ALLOCATOR,
+
+ /** One of the chunks in an indefinite length string is not of the type of
+ the string. */
+ QCBOR_ERR_INDEFINITE_STRING_CHUNK,
+
+ /** Error allocating space for a string, usually for an indefinite length
+ string. */
+ QCBOR_ERR_STRING_ALLOCATE,
+
+ /** During decoding, a break occurred outside an indefinite length item. */
+ QCBOR_ERR_BAD_BREAK,
+
+ /** During decoding, too many tags in the caller-configured tag list, or not
+ enough space in QCBORTagListOut. */
+ QCBOR_ERR_TOO_MANY_TAGS,
+
+ /** Returned by QCBORDecode_SetMemPool() when xx is too small. This should
+ never happen on a machine with 64-bit or smaller pointers. Fixing
+ it is probably by increasing QCBOR_DECODE_MIN_MEM_POOL_SIZE. */
+ QCBOR_ERR_MEM_POOL_INTERNAL
+
+} QCBORError;
+
+
+typedef enum {
+ /** See QCBORDecode_Init() */
+ QCBOR_DECODE_MODE_NORMAL = 0,
+ /** See QCBORDecode_Init() */
+ QCBOR_DECODE_MODE_MAP_STRINGS_ONLY = 1,
+ /** See QCBORDecode_Init() */
+ QCBOR_DECODE_MODE_MAP_AS_ARRAY = 2
+} QCBORDecodeMode;
+
+
+
+
+
+/* Do not renumber these. Code depends on some of these values. */
+/** The type is unknown, unset or invalid */
+#define QCBOR_TYPE_NONE 0
+/** Type for an integer that decoded either between INT64_MIN and INT32_MIN or INT32_MAX and INT64_MAX; val.int64 */
+#define QCBOR_TYPE_INT64 2
+/** Type for an integer that decoded to a more than INT64_MAX and UINT64_MAX; val.uint64 */
+#define QCBOR_TYPE_UINT64 3
+/** Type for an array. The number of items in the array is in val.uCount. */
+#define QCBOR_TYPE_ARRAY 4
+/** Type for a map; number of items in map is in val.uCount */
+#define QCBOR_TYPE_MAP 5
+/** Type for a buffer full of bytes. Data is in val.string. */
+#define QCBOR_TYPE_BYTE_STRING 6
+/** Type for a UTF-8 string. It is not NULL terminated. Data is in val.string. */
+#define QCBOR_TYPE_TEXT_STRING 7
+/** Type for a positive big number. Data is in val.bignum, a pointer and a length. */
+#define QCBOR_TYPE_POSBIGNUM 9
+/** Type for a negative big number. Data is in val.bignum, a pointer and a length. */
+#define QCBOR_TYPE_NEGBIGNUM 10
+/** Type for RFC 3339 date string, possibly with time zone. Data is in val.dateString */
+#define QCBOR_TYPE_DATE_STRING 11
+/** Type for integer seconds since Jan 1970 + floating point fraction. Data is in val.epochDate */
+#define QCBOR_TYPE_DATE_EPOCH 12
+/** A simple type that this CBOR implementation doesn't know about; Type is in val.uSimple. */
+#define QCBOR_TYPE_UKNOWN_SIMPLE 13
+/** Type for the simple value false; nothing more; nothing in val union. */
+#define QCBOR_TYPE_FALSE 20
+/** Type for the simple value true; nothing more; nothing in val union. */
+#define QCBOR_TYPE_TRUE 21
+/** Type for the simple value null; nothing more; nothing in val union. */
+#define QCBOR_TYPE_NULL 22
+/** Type for the simple value undef; nothing more; nothing in val union. */
+#define QCBOR_TYPE_UNDEF 23
+/** Type for a floating point number. Data is in val.float. */
+#define QCBOR_TYPE_FLOAT 26
+/** Type for a double floating point number. Data is in val.double. */
+#define QCBOR_TYPE_DOUBLE 27
+/** For QCBOR_DECODE_MODE_MAP_AS_ARRAY decode mode, a map that is being traversed as an array. See QCBORDecode_Init() */
+#define QCBOR_TYPE_MAP_AS_ARRAY 32
+
+#define QCBOR_TYPE_BREAK 31 // Used internally; never returned
+
+#define QCBOR_TYPE_OPTTAG 254 // Used internally; never returned
+
+
+
+/*
+ Approx Size of this:
+ 8 + 8 + 1 + 1 + 1 + (1 padding) + (4 padding on 64-bit machine) = 24 for first part (20 on a 32-bit machine)
+ 16 bytes for the val union
+ 16 bytes for label union
+ total = 56 bytes (52 bytes on 32-bit machine)
+ */
+
+/**
+ QCBORItem holds the type, value and other info for a decoded item returned by GetNextItem().
+ */
+typedef struct _QCBORItem {
+ uint8_t uDataType; /** Tells what element of the val union to use. One of QCBOR_TYPE_XXXX */
+ uint8_t uNestingLevel; /** How deep the nesting from arrays and maps are. 0 is the top level with no arrays or maps entered */
+ uint8_t uLabelType; /** Tells what element of the label union to use */
+ uint8_t uDataAlloc; /** 1 if allocated with string allocator, 0 if not. See QCBORDecode_MakeMallocStringAllocator() */
+ uint8_t uLabelAlloc; /** Like uDataAlloc, but for label */
+ uint8_t uNextNestLevel; /** If not equal to uNestingLevel, this item closed out at least one map/array */
+
+ union {
+ int64_t int64; /** The value for uDataType QCBOR_TYPE_INT64 */
+ uint64_t uint64; /** The value for uDataType QCBOR_TYPE_UINT64 */
+
+ UsefulBufC string; /** The value for uDataType QCBOR_TYPE_BYTE_STRING and QCBOR_TYPE_TEXT_STRING */
+ uint16_t uCount; /** The "value" for uDataType QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP -- the number of items in the array or map
+ UINT16_MAX when decoding indefinite lengths maps and arrays. */
+ double dfnum; /** The value for uDataType QCBOR_TYPE_DOUBLE */
+ struct {
+ int64_t nSeconds;
+ double fSecondsFraction;
+ } epochDate; /** The value for uDataType QCBOR_TYPE_DATE_EPOCH */
+ UsefulBufC dateString; /** The value for uDataType QCBOR_TYPE_DATE_STRING */
+ UsefulBufC bigNum; /** The value for uDataType QCBOR_TYPE_BIGNUM */
+ uint8_t uSimple; /** The integer value for unknown simple types */
+ uint64_t uTagV;
+
+ } val; /** The union holding the item's value. Select union member based on uDataType */
+
+ union {
+ UsefulBufC string; /** The label for uLabelType QCBOR_TYPE_BYTE_STRING and QCBOR_TYPE_TEXT_STRING */
+ int64_t int64; /** The label for uLabelType for QCBOR_TYPE_INT64 */
+ uint64_t uint64; /** The label for uLabelType for QCBOR_TYPE_UINT64 */
+ } label; /** Union holding the different label types selected based on uLabelType */
+
+ uint64_t uTagBits; /** Bit indicating which tags (major type 6) on this item. */
+
+} QCBORItem;
+
+
+/**
+ This is a set of functions and pointer context (in object-oriented parlance,
+ an "object") used to allocate memory for coalescing the segments of an indefinite
+ length string into one.
+
+ The fAllocate function works as an initial allocator and a reallocator to
+ expand the string for each new segment. When it is an initial allocator
+ pOldMem is NULL.
+
+ The fFree function is called to clean up an individual allocation when an error occurs.
+
+ The fDesctructor function is called when QCBORDecode_Finish is called.
+
+ Any memory allocated with this will be marked by setting uDataAlloc
+ or uLabelAlloc in the QCBORItem structure so the caller knows they
+ have to free it.
+
+ fAllocate is only ever called to increase the single most recent
+ allocation made, making implementation of a memory pool very simple.
+
+ fFree is also only called on the single most recent allocation.
+ */
+typedef struct {
+ void *pAllocaterContext;
+ UsefulBuf (*fAllocate)(void *pAllocaterContext, void *pOldMem, size_t uNewSize);
+ void (*fFree)(void *pAllocaterContext, void *pMem);
+ void (*fDestructor)(void *pAllocaterContext);
+} QCBORStringAllocator;
+
+
+/**
+ This only matters if you use a string allocator
+ and and set it up with QCBORDecode_SetMemPool(). It is
+ the size of the overhead needed needed by
+ QCBORDecode_SetMemPool(). If you write your own
+ string allocator or use the separately available malloc
+ based string allocator, this size will not apply
+ */
+#define QCBOR_DECODE_MIN_MEM_POOL_SIZE 72
+
+
+/**
+ This is used to tell the decoder about tags that it should
+ record in uTagBits in QCBORItem beyond the built-in
+ tags. puTags points to an
+ array of uint64_t integers that are the tags. uNumTags
+ is the number of integers in the array. The maximum
+ size is QCBOR_MAX_CUSTOM_TAGS. See QCBORDecode_IsTagged()
+ and QCBORDecode_SetCallerAddedTagMap().
+ */
+typedef struct {
+ uint8_t uNumTags;
+ const uint64_t *puTags;
+} QCBORTagListIn;
+
+
+/**
+ This is for QCBORDecode_GetNextWithTags() to be able to return the
+ full list of tags on an item. It not needed for most CBOR protocol
+ implementations. Its primary use is for pretty-printing CBOR or
+ protocol conversion to another format.
+
+ On input, puTags points to a buffer to be filled in
+ and uNumAllocated is the number of uint64_t values
+ in the buffer.
+
+ On output the buffer contains the tags for the item.
+ uNumUsed tells how many there are.
+ */
+typedef struct {
+ uint8_t uNumUsed;
+ uint8_t uNumAllocated;
+ uint64_t *puTags;
+} QCBORTagListOut;
+
+
+/**
+ QCBOREncodeContext is the data type that holds context for all the
+ encoding functions. It is less than 200 bytes, so it can go on
+ the stack. The contents are opaque, and the caller should not access
+ any internal items. A context may be re used serially as long as
+ it is re initialized.
+ */
+typedef struct _QCBOREncodeContext QCBOREncodeContext;
+
+
+/**
+ Initialize the the encoder to prepare to encode some CBOR.
+
+ @param[in,out] pCtx The encoder context to initialize.
+ @param[in] Storage The buffer into which this encoded result will be placed.
+
+ Call this once at the start of an encoding of a CBOR structure. Then
+ call the various QCBOREncode_AddXXX() functions to add the data
+ items. Then call QCBOREncode_Finish().
+
+ The maximum output buffer is UINT32_MAX (4GB). This is not a practical
+ limit in any way and reduces the memory needed by the implementation.
+ The error QCBOR_ERR_BUFFER_TOO_LARGE will be returned by QCBOR_Finish()
+ if a larger buffer length is passed in.
+
+ If this is called with pBuf as NULL and uBufLen a large value like
+ UINT32_MAX, all the QCBOREncode_AddXXXX() functions and
+ QCBORE_Encode_Finish() can still be called. No data will be encoded,
+ but the length of what would be encoded will be calculated. The
+ length of the encoded structure will be handed back in the call to
+ QCBOREncode_Finish(). You can then allocate a buffer of that size
+ and call all the encoding again, this time to fill in the buffer.
+
+ A QCBORContext can be reused over and over as long as
+ QCBOREncode_Init() is called.
+ */
+void QCBOREncode_Init(QCBOREncodeContext *pCtx, UsefulBuf Storage);
+
+
+/**
+ @brief Add a signed 64-bit integer to the encoded output.
+
+ @param[in] pCtx The encoding context to add the integer to.
+ @param[in] nNum The integer to add.
+
+ The integer will be encoded and added to the CBOR output.
+
+ This function figures out the size and the sign and encodes in the
+ correct minimal CBOR. Specifically, it will select CBOR major type 0 or 1
+ based on sign and will encode to 1, 2, 4 or 8 bytes depending on the
+ value of the integer. Values less than 24 effectively encode to one
+ byte because they are encoded in with the CBOR major type. This is
+ a neat and efficient characteristic of CBOR that can be taken
+ advantage of when designing CBOR-based protocols. If integers like
+ tags can be kept between -23 and 23 they will be encoded in one byte
+ including the major type.
+
+ If you pass a smaller int, say an int16_t or a small value, say 100,
+ the encoding will still be CBOR's most compact that can represent the
+ value. For example, CBOR always encodes the value 0 as one byte,
+ 0x00. The representation as 0x00 includes identification of the type
+ as an integer too as the major type for an integer is 0. See RFC 7049
+ Appendix A for more examples of CBOR encoding. This compact encoding
+ is also canonical CBOR as per section 3.9 in RFC 7049.
+
+ There are no functions to add int16_t or int32_t because they are
+ not necessary because this always encodes to the smallest number
+ of bytes based on the value (If this code is running on a 32-bit
+ machine having a way to add 32-bit integers would reduce code size some).
+
+ If the encoding context is in an error state, this will do
+ nothing. If an error occurs when adding this integer, the internal
+ error flag will be set, and the error will be returned when
+ QCBOREncode_Finish() is called.
+
+ See also QCBOREncode_AddUInt64().
+ */
+void QCBOREncode_AddInt64(QCBOREncodeContext *pCtx, int64_t nNum);
+
+static void QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t uNum);
+
+static void QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t uNum);
+
+
+/**
+ @brief Add an unsigned 64-bit integer to the encoded output.
+
+ @param[in] pCtx The encoding context to add the integer to.
+ @param[in] uNum The integer to add.
+
+ The integer will be encoded and added to the CBOR output.
+
+ The only reason so use this function is for integers larger than
+ INT64_MAX and smaller than UINT64_MAX. Otherwise QCBOREncode_AddInt64()
+ will work fine.
+
+ Error handling is the same as for QCBOREncode_AddInt64().
+ */
+void QCBOREncode_AddUInt64(QCBOREncodeContext *pCtx, uint64_t uNum);
+
+static void QCBOREncode_AddUInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, uint64_t uNum);
+
+static void QCBOREncode_AddUInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, uint64_t uNum);
+
+
+/**
+
+ @brief Add a UTF-8 text string to the encoded output
+
+ @param[in] pCtx The context to initialize.
+ @param[in] Text Pointer and length of text to add.
+
+ The text passed in must be unencoded UTF-8 according to RFC
+ 3629. There is no NULL termination. The text is added as CBOR
+ major type 3.
+
+ If called with nBytesLen equal to 0, an empty string will be
+ added. When nBytesLen is 0, pBytes may be NULL.
+
+ Note that the restriction of the buffer length to an uint32_t is
+ entirely intentional as this encoder is not capable of encoding
+ lengths greater. This limit to 4GB for a text string should not be a
+ problem.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+static void QCBOREncode_AddText(QCBOREncodeContext *pCtx, UsefulBufC Text);
+
+static void QCBOREncode_AddTextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Text);
+
+static void QCBOREncode_AddTextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Text);
+
+
+/**
+ @brief Add a UTF-8 text string to the encoded output
+
+ @param[in] pCtx The context to initialize.
+ @param[in] szString Null-terminated text to add.
+
+ This works the same as QCBOREncode_AddText().
+ */
+static void QCBOREncode_AddSZString(QCBOREncodeContext *pCtx, const char *szString);
+
+static void QCBOREncode_AddSZStringToMap(QCBOREncodeContext *pCtx, const char *szLabel, const char *szString);
+
+static void QCBOREncode_AddSZStringToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, const char *szString);
+
+
+/**
+ @brief Add a floating-point number to the encoded output
+
+ @param[in] pCtx The encoding context to add the float to.
+ @param[in] dNum The double precision number to add.
+
+ This outputs a floating-point number with CBOR major type 7.
+
+ This will selectively encode the double-precision floating point
+ number as either double-precision, single-precision or
+ half-precision. It will always encode infinity, NaN and 0 has half
+ precision. If no precision will be lost in the conversion to
+ half-precision then it will be converted and encoded. If not and no
+ precision will be lost in conversion to single-precision, then it
+ will be converted and encoded. If not, then no conversion is
+ performed, and it encoded as a double.
+
+ Half-precision floating point numbers take up 2 bytes, half that of
+ single-precision, one quarter of double-precision
+
+ This automatically reduces the size of encoded messages a lot, maybe
+ even by four if most of values are 0, infinity or NaN.
+
+ On decode, these will always be returned as a double.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+void QCBOREncode_AddDouble(QCBOREncodeContext *pCtx, double dNum);
+
+static void QCBOREncode_AddDoubleToMap(QCBOREncodeContext *pCtx, const char *szLabel, double dNum);
+
+static void QCBOREncode_AddDoubleToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, double dNum);
+
+
+/**
+ @brief[in] Add an optional tag
+
+ @param[in] pCtx The encoding context to add the integer to.
+ @param[in] uTag The tag to add
+
+ This outputs a CBOR major type 6 optional tag.
+
+ The tag is applied to the next data item added to the encoded
+ output. That data item that is to be tagged can be of any major
+ CBOR type. Any number of tags can be added to a data item by calling
+ this multiple times before the data item is added.
+
+ For many of the common standard tags a function to encode
+ data using it already exists and this is not needed. For example,
+ QCBOREncode_AddDateEpoch() already exists to output
+ integers representing dates with the right tag.
+*/
+void QCBOREncode_AddTag(QCBOREncodeContext *pCtx,uint64_t uTag);
+
+
+/**
+ @brief Add an epoch-based date
+
+ @param[in] pCtx The encoding context to add the simple value to.
+ @param[in] date Number of seconds since 1970-01-01T00:00Z in UTC time.
+
+ As per RFC 7049 this is similar to UNIX/Linux/POSIX dates. This is
+ the most compact way to specify a date and time in CBOR. Note that this
+ is always UTC and does not include the time zone. Use
+ QCBOREncode_AddDateString() if you want to include the time zone.
+
+ The integer encoding rules apply here so the date will be encoded in a
+ minimal number of 1, 2 4 or 8 bytes. Until about the year 2106 these
+ dates should encode in 6 bytes -- one byte for the tag, one byte for the type
+ and 4 bytes for the integer.
+
+ If you care about leap-seconds and that level of accuracy, make sure the
+ system you are running this code on does it correctly. This code just takes
+ the value passed in.
+
+ This implementation cannot encode fractional seconds using float or double
+ even though that is allowed by CBOR, but you can encode them if you
+ want to by calling QCBOREncode_AddDouble()
+ with the right parameters.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+static void QCBOREncode_AddDateEpoch(QCBOREncodeContext *pCtx, int64_t date);
+
+static void QCBOREncode_AddDateEpochToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t date);
+
+static void QCBOREncode_AddDateEpochToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t date);
+
+
+/**
+ @brief Add a byte string to the encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] Bytes Pointer and length of the input data.
+
+ Simply adds the bytes to the encoded output as CBOR major type 2.
+
+ If called with Bytes.len equal to 0, an empty string will be
+ added. When Bytes.len is 0, Bytes.ptr may be NULL.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+static void QCBOREncode_AddBytes(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
+
+static void QCBOREncode_AddBytesToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes);
+
+static void QCBOREncode_AddBytesToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes);
+
+
+
+/**
+ @brief Add a binary UUID to the encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] Bytes Pointer and length of the binary UUID.
+
+ A binary UUID as defined in RFC 4122 is added to the ouput.
+
+ It is output as CBOR major type 2, a binary string, with
+ optional tag 36 indicating the binary string is a UUID.
+ */
+static void QCBOREncode_AddBinaryUUID(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
+
+static void QCBOREncode_AddBinaryUUIDToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes);
+
+static void QCBOREncode_AddBinaryUUIDToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes);
+
+
+/**
+ @brief Add a positive big number to the encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] Bytes Pointer and length of the big number.
+
+ Big numbers are integers larger than 64-bits. Their format
+ is described in RFC 7049.
+
+ It is output as CBOR major type 2, a binary string, with
+ optional tag 2 indicating the binary string is a positive big
+ number.
+
+ Often big numbers are used to represent cryptographic keys,
+ however, COSE which defines representations for keys chose not
+ to use this particular type.
+ */
+static void QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
+
+static void QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes);
+
+static void QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes);
+
+
+/**
+ @brief Add a negative big number to the encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] Bytes Pointer and length of the big number.
+
+ Big numbers are integers larger than 64-bits. Their format
+ is described in RFC 7049.
+
+ It is output as CBOR major type 2, a binary string, with
+ optional tag 2 indicating the binary string is a negative big
+ number.
+
+ Often big numbers are used to represent cryptographic keys,
+ however, COSE which defines representations for keys chose not
+ to use this particular type.
+ */
+static void QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
+
+static void QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes);
+
+static void QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes);
+
+
+/**
+ @brief Add a text URI to the encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] URI Pointer and length of the URI.
+
+ The format of URI is RFC 3986.
+
+ It is output as CBOR major type 3, a text string, with
+ optional tag 32 indicating the text string is a URI.
+ */
+static void QCBOREncode_AddURI(QCBOREncodeContext *pCtx, UsefulBufC URI);
+
+static void QCBOREncode_AddURIToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC URI);
+
+static void QCBOREncode_AddURIToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC URI);
+
+
+/**
+ @brief Add base 64-encoded text to encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] B64Text Pointer and length of the base-64 encoded text.
+
+ The text content is base 64 encoded data per RFC 4648.
+
+ It is output as CBOR major type 3, a text string, with
+ optional tag 34 indicating the text string is a URI.
+ */
+static void QCBOREncode_AddB64Text(QCBOREncodeContext *pCtx, UsefulBufC B64Text);
+
+static void QCBOREncode_AddB64TextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text);
+
+static void QCBOREncode_AddB64TextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text);
+
+
+/**
+ @brief Add base 64URL -encoded URL to encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] B64Text Pointer and length of the base-64 encoded text.
+
+ The text content is base 64 URL format encoded text as per RFC 4648.
+
+ It is output as CBOR major type 3, a text string, with
+ optional tag 33 indicating the text string is a URI.
+ */
+static void QCBOREncode_AddB64URLText(QCBOREncodeContext *pCtx, UsefulBufC B64Text);
+
+static void QCBOREncode_AddB64URLTextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text);
+
+static void QCBOREncode_AddB64URLTextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text);
+
+
+/**
+ @brief Add Perl Compatible Regular Expression
+
+ @param[in] pCtx The context to initialize.
+ @param[in] Regex Pointer and length of the regular expression.
+
+ The text content is Perl Compatible Regular
+ Expressions (PCRE) / JavaScript syntax [ECMA262].
+
+ It is output as CBOR major type 3, a text string, with
+ optional tag 35 indicating the text string is a regular expression.
+ */
+static void QCBOREncode_AddRegex(QCBOREncodeContext *pCtx, UsefulBufC Regex);
+
+static void QCBOREncode_AddRegexToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Regex);
+
+static void QCBOREncode_AddRegexToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Regex);
+
+
+/**
+ @brief MIME encoded text to the encoded output.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] MIMEData Pointer and length of the regular expression.
+
+ The text content is in MIME format per RFC 2045 including the headers.
+
+ It is output as CBOR major type 3, a text string, with
+ optional tag 36 indicating the text string is MIME data.
+ */
+static void QCBOREncode_AddMIMEData(QCBOREncodeContext *pCtx, UsefulBufC MIMEData);
+
+static void QCBOREncode_AddMIMEDataToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC MIMEData);
+
+static void QCBOREncode_AddMIMEDataToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC MIMEData);
+
+
+/**
+ @brief Add an RFC 3339 date string
+
+ @param[in] pCtx The encoding context to add the simple value to.
+ @param[in] szDate Null-terminated string with date to add
+
+ The string szDate should be in the form of RFC 3339 as defined by section
+ 3.3 in RFC 4287. This is as described in section 2.4.1 in RFC 7049.
+
+ Note that this function doesn't validate the format of the date string
+ at all. If you add an incorrect format date string, the generated
+ CBOR will be incorrect and the receiver may not be able to handle it.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+static void QCBOREncode_AddDateString(QCBOREncodeContext *pCtx, const char *szDate);
+
+static void QCBOREncode_AddDateStringToMap(QCBOREncodeContext *pCtx, const char *szLabel, const char *szDate);
+
+static void QCBOREncode_AddDateStringToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, const char *szDate);
+
+
+/**
+ @brief Add a standard boolean.
+
+ @param[in] pCtx The encoding context to add the simple value to.
+ @param[in] b true or false from stdbool. Anything will result in an error.
+
+ Adds a boolean value as CBOR major type 7.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+static void QCBOREncode_AddBool(QCBOREncodeContext *pCtx, bool b);
+
+static void QCBOREncode_AddBoolToMap(QCBOREncodeContext *pCtx, const char *szLabel, bool b);
+
+static void QCBOREncode_AddBoolToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, bool b);
+
+
+
+/**
+ @brief Add a NULL to the encoded output.
+
+ @param[in] pCtx The encoding context to add the simple value to.
+
+ Adds the NULL value as CBOR major type 7.
+
+ This NULL doesn't have any special meaning in CBOR such as a terminating
+ value for a string or an empty value.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+static void QCBOREncode_AddNULL(QCBOREncodeContext *pCtx);
+
+static void QCBOREncode_AddNULLToMap(QCBOREncodeContext *pCtx, const char *szLabel);
+
+static void QCBOREncode_AddNULLToMapN(QCBOREncodeContext *pCtx, int64_t nLabel);
+
+
+/**
+ @brief Add an "undef" to the encoded output.
+
+ @param[in] pCtx The encoding context to add the simple value to.
+
+ Adds the undef value as CBOR major type 7.
+
+ Note that this value will not translate to JSON.
+
+ This Undef doesn't have any special meaning in CBOR such as a terminating
+ value for a string or an empty value.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+static void QCBOREncode_AddUndef(QCBOREncodeContext *pCtx);
+
+static void QCBOREncode_AddUndefToMap(QCBOREncodeContext *pCtx, const char *szLabel);
+
+static void QCBOREncode_AddUndefToMapN(QCBOREncodeContext *pCtx, int64_t nLabel);
+
+
+/**
+ @brief Indicates that the next items added are in an array.
+
+ @param[in] pCtx The encoding context to open the array in.
+
+ Arrays are the basic CBOR aggregate or structure type. Call this
+ function to start or open an array. Then call the various AddXXX
+ functions to add the items that go into the array. Then call
+ QCBOREncode_CloseArray() when all items have been added. The data
+ items in the array can be of any type and can be of mixed types.
+
+ Nesting of arrays and maps is allowed and supported just by calling
+ QCBOREncode_OpenArray() again before calling CloseArray. While CBOR
+ has no limit on nesting, this implementation does in order to keep it
+ smaller and simpler. The limit is QCBOR_MAX_ARRAY_NESTING. This is
+ the max number of times this can be called without calling
+ QCBOREncode_CloseArray(). QCBOREncode_Finish() will return
+ QCBOR_ERR_ARRAY_NESTING_TOO_DEEP when it is called as this function
+ just sets an error state and returns no value when this occurs.
+
+ If you try to add more than QCBOR_MAX_ITEMS_IN_ARRAY items to a
+ single array or map, QCBOR_ERR_ARRAY_TOO_LONG will be returned when
+ QCBOREncode_Finish() is called.
+
+ An array itself must have a label if it is being added to a map.
+ Note that array elements do not have labels (but map elements do).
+
+ An array itself may be tagged.
+ */
+static void QCBOREncode_OpenArray(QCBOREncodeContext *pCtx);
+
+static void QCBOREncode_OpenArrayInMap(QCBOREncodeContext *pCtx, const char *szLabel);
+
+static void QCBOREncode_OpenArrayInMapN(QCBOREncodeContext *pCtx, int64_t nLabel);
+
+
+/**
+ @brief Close an open array.
+
+ @param[in] pCtx The context to add to.
+
+ The closes an array opened by QCBOREncode_OpenArray(). It reduces
+ nesting level by one. All arrays (and maps) must be closed before
+ calling QCBOREncode_Finish().
+
+ When an error occurs as a result of this call, the encoder records
+ the error and enters the error state. The error will be returned when
+ QCBOREncode_Finish() is called.
+
+ If this has been called more times than QCBOREncode_OpenArray(), then
+ QCBOR_ERR_TOO_MANY_CLOSES will be returned when QCBOREncode_Finish()
+ is called.
+
+ If this is called and it is not an array that is currently open,
+ QCBOR_ERR_CLOSE_MISMATCH will be returned when QCBOREncode_Finish()
+ is called.
+ */
+static void QCBOREncode_CloseArray(QCBOREncodeContext *pCtx);
+
+
+/**
+ @brief Indicates that the next items added are in a map.
+
+ @param[in] pCtx The context to add to.
+
+ See QCBOREncode_OpenArray() for more information, particularly error
+ handling.
+
+ CBOR maps are an aggregate type where each item in the map consists
+ of a label and a value. They are similar to JSON objects.
+
+ The value can be any CBOR type including another map.
+
+ The label can also be any CBOR type, but in practice they are
+ typically, integers as this gives the most compact output. They might
+ also be text strings which gives readability and translation to JSON.
+
+ Every QCBOREncode_AddXXX() call has once version that is "InMap" for
+ adding items to maps with string labels and on that is "InMapN" that
+ is for adding with integer labels.
+
+ RFC 7049 uses the term "key" instead of "label".
+
+ If you wish to use map labels that are neither integer labels or
+ text strings, then just call the QCBOREncode_AddXXX() function
+ explicitly to add the label. Then call it again to add the value.
+
+ See the RFC7049 for a lot more information on creating maps.
+ */
+static void QCBOREncode_OpenMap(QCBOREncodeContext *pCtx);
+
+static void QCBOREncode_OpenMapInMap(QCBOREncodeContext *pCtx, const char *szLabel);
+
+static void QCBOREncode_OpenMapInMapN(QCBOREncodeContext *pCtx, int64_t nLabel);
+
+
+
+/**
+ @brief Close an open map.
+
+ @param[in] pCtx The context to add to.
+
+ The closes a map opened by QCBOREncode_OpenMap(). It reduces nesting
+ level by one.
+
+ When an error occurs as a result of this call, the encoder records
+ the error and enters the error state. The error will be returned when
+ QCBOREncode_Finish() is called.
+
+ If this has been called more times than QCBOREncode_OpenMap(),
+ then QCBOR_ERR_TOO_MANY_CLOSES will be returned when
+ QCBOREncode_Finish() is called.
+
+ If this is called and it is not a map that is currently
+ open, QCBOR_ERR_CLOSE_MISMATCH will be returned when QCBOREncode_Finish()
+ is called.
+ */
+static void QCBOREncode_CloseMap(QCBOREncodeContext *pCtx);
+
+
+/**
+ @brief Indicate start of encoded CBOR to be wrapped in a bstr.
+
+ @param[in] pCtx The context to add to.
+
+ All added encoded items between this call and a call to
+ QCBOREncode_CloseBstrWrap() will be wrapped in a bstr. They will
+ appear in the final output as a byte string. That byte string will
+ contain encoded CBOR.
+
+ The typical use case is for encoded CBOR that is to be
+ cryptographically hashed, as part of a COSE (RFC 8152)
+ implementation. This avoids having to encode the items first in one
+ buffer (e.g., the COSE payload) and then add that buffer as a bstr to
+ another encoding (e.g. the COSE to-be-signed bytes, the
+ Sig_structure) potentially saving a lot of memory.
+
+ When constructing cryptographically signed CBOR objects, maps or
+ arrays, they typically are encoded normally and then wrapped as a
+ byte string. The COSE standard for example does this. The wrapping is
+ simply treating the encoded CBOR map as a byte string.
+
+ The stated purpose of this wrapping is to prevent code relaying the
+ signed data but not verifying it from tampering with the signed data
+ thus making the signature unverifiable. It is also quite beneficial
+ for the signature verification code. Standard CBOR parsers usually do
+ not give access to partially parsed CBOR as would be need to check
+ the signature of some CBOR. With this wrapping, standard CBOR parsers
+ can be used to get to all the data needed for a signature
+ verification.
+ */
+static void QCBOREncode_BstrWrap(QCBOREncodeContext *pCtx);
+
+static void QCBOREncode_BstrWrapInMap(QCBOREncodeContext *pCtx, const char *szLabel);
+
+static void QCBOREncode_BstrWrapInMapN(QCBOREncodeContext *pCtx, int64_t nLabel);
+
+
+/**
+ @brief Close a wrapping bstr.
+
+ @param[in] pCtx The context to add to.
+ @param[out] pWrappedCBOR UsefulBufC containing wrapped bytes
+
+ The closes a wrapping bstr opened by QCBOREncode_BstrWrap(). It reduces
+ nesting level by one.
+
+ A pointer and length of the enclosed encoded CBOR is returned in
+ *pWrappedCBOR if it is not NULL. The main purpose of this is so this
+ data can be hashed (e.g., with SHA-256) as part of a COSE (RFC 8152)
+ implementation. **WARNING**, this pointer and length should be used
+ right away before any other calls to QCBOREncode_xxxx() as they will
+ move data around and the pointer and length will no longer be to the
+ correct encoded CBOR.
+
+ When an error occurs as a result of this call, the encoder records
+ the error and enters the error state. The error will be returned when
+ QCBOREncode_Finish() is called.
+
+ If this has been called more times then QCBOREncode_BstrWrap(),
+ then QCBOR_ERR_TOO_MANY_CLOSES will be returned when
+ QCBOREncode_Finish() is called.
+
+ If this is called and it is not a wrapping bstr that is currently
+ open, QCBOR_ERR_CLOSE_MISMATCH will be returned when QCBOREncode_Finish()
+ is called.
+ */
+static void QCBOREncode_CloseBstrWrap(QCBOREncodeContext *pCtx, UsefulBufC *pWrappedCBOR);
+
+
+/**
+ @brief Add some already-encoded CBOR bytes.
+
+ @param[in] pCtx The context to add to.
+ @param[in] Encoded The already-encoded CBOR to add to the context.
+
+ The encoded CBOR being added must be fully conforming CBOR. It must
+ be complete with no arrays or maps that are incomplete. While this
+ encoder doesn't ever produce indefinite lengths, it is OK for the
+ raw CBOR added here to have indefinite lengths.
+
+ The raw CBOR added here is not checked in anyway. If it is not
+ conforming or has open arrays or such, the final encoded CBOR
+ will probably be wrong or not what was intended.
+
+ If the encoded CBOR being added here contains multiple items, they
+ must be enclosed in a map or array. At the top level the raw
+ CBOR must be a single data item.
+ */
+static void QCBOREncode_AddEncoded(QCBOREncodeContext *pCtx, UsefulBufC Encoded);
+
+static void QCBOREncode_AddEncodedToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Encoded);
+
+static void QCBOREncode_AddEncodedToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Encoded);
+
+
+/**
+ @brief Get the encoded result.
+
+ @param[in] pCtx The context to finish encoding with.
+ @param[out] pEncodedCBOR Pointer and length of encoded CBOR.
+
+ @return
+ One of the CBOR error codes.
+
+ If this returns success QCBOR_SUCCESS the encoding was a success and
+ the return length is correct and complete.
+
+ If no buffer was passed to QCBOR_Init(), then only the length and
+ number of items was computed. The length is in
+ pEncodedCBOR->Bytes.len. pEncodedCBOR->Bytes.ptr is NULL.
+
+ If a buffer was passed, then pEncodedCBOR->Bytes.ptr is the same as
+ the buffer passed to QCBOR_Init() and contains the encoded CBOR
+ and the length is filled in.
+
+ If an error is returned, the buffer may have partially encoded
+ incorrect CBOR in it and it should not be used. Likewise, the length
+ may be incorrect and should not be used.
+
+ Note that the error could have occurred in one of the many
+ QCBOR_AddXXX calls long before QCBOREncode_Finish() was called. This
+ error handling approach reduces the CBOR implementation size, but makes
+ debugging a problem a little more difficult.
+ */
+QCBORError QCBOREncode_Finish(QCBOREncodeContext *pCtx, UsefulBufC *pEncodedCBOR);
+
+
+/**
+ @brief Get the encoded CBOR and error status.
+
+ @param[in] pCtx The context to finish encoding with.
+ @param[out] uEncodedLen The length of the encoded or potentially encoded CBOR in bytes.
+
+ @return
+ One of the CBOR error codes.
+
+ If this returns success QCBOR_SUCCESS the encoding was a success and
+ the return length is correct and complete.
+
+ If no buffer was passed to QCBOR_Init(), then only the length was
+ computed. If a buffer was passed, then the encoded CBOR is in the
+ buffer.
+
+ If an error is returned, the buffer may have partially encoded
+ incorrect CBOR in it and it should not be used. Likewise, the length
+ may be incorrect and should not be used.
+
+ Note that the error could have occurred in one of the many
+ QCBOR_AddXXX calls long before QCBOREncode_Finish() was called. This
+ error handling reduces the CBOR implementation size, but makes
+ debugging harder.
+ */
+QCBORError QCBOREncode_FinishGetSize(QCBOREncodeContext *pCtx, size_t *uEncodedLen);
+
+
+
+
+
+
+/**
+ QCBORDecodeContext is the data type that holds context decoding the
+ data items for some received CBOR. It is about 100 bytes, so it can go
+ on the stack. The contents are opaque, and the caller should not
+ access any internal items. A context may be re used serially as long
+ as it is re initialized.
+ */
+typedef struct _QCBORDecodeContext QCBORDecodeContext;
+
+
+/**
+ Initialize the CBOR decoder context.
+
+ @param[in] pCtx The context to initialize.
+ @param[in] EncodedCBOR The buffer with CBOR encoded bytes to be decoded.
+ @param[in] nMode One of QCBOR_DECODE_MODE_xxx
+
+ Initialize context for a pre-order travesal of the encoded CBOR tree.
+
+ Most CBOR decoding can be completed by calling this function to start
+ and QCBORDecode_GetNext() in a loop.
+
+ If indefinite length strings are to be decoded, then
+ QCBORDecode_SetMemPool() or QCBORDecode_SetUpAllocator() must be
+ called to set up a string allocator.
+
+ If tags other than built-in tags are to be recognized, then
+ QCBORDecode_SetCallerAddedTagMap() must be called. The built-in tags
+ are those for which a macro of the form CBOR_TAG_XXX is defined.
+
+ Three decoding modes are supported. In normal mode,
+ QCBOR_DECODE_MODE_NORMAL, maps are decoded and strings and ints are
+ accepted as map labels. If a label is other than these, the error
+ QCBOR_ERR_MAP_LABEL_TYPE is returned by QCBORDecode_GetNext().
+
+ In strings-only mode, QCBOR_DECODE_MODE_MAP_STRINGS_ONLY, only text
+ strings are accepted for map labels. This lines up with CBOR that
+ converts to JSON. The error QCBOR_ERR_MAP_LABEL_TYPE is returned by
+ QCBORDecode_GetNext() if anything but a text string label is
+ encountered.
+
+ In QCBOR_DECODE_MODE_MAP_AS_ARRAY maps are treated as special arrays.
+ They will be return with special uDataType QCBOR_TYPE_MAP_AS_ARRAY
+ and uCount, the number of items, will be double what it would be
+ for a normal map because the labels are also counted. This mode
+ is useful for decoding CBOR that has labels that are not
+ integers or text strings, but the caller must manage much of
+ the map decoding.
+ */
+void QCBORDecode_Init(QCBORDecodeContext *pCtx, UsefulBufC EncodedCBOR, QCBORDecodeMode nMode);
+
+
+/**
+ @brief Set up the MemPool string allocator for indefinite length strings.
+
+ @param[in] pCtx The decode context.
+ @param[in] MemPool The pointer and length of the memory pool.
+ @param[in] bAllStrings true means to put even definite length strings in the pool.
+
+ @return error if the MemPool was less than QCBOR_DECODE_MIN_MEM_POOL_SIZE.
+
+ Indefinite length strings (text and byte) cannot be decoded unless
+ there is a string allocator configured. MemPool is a simple built-in
+ string allocator that allocates bytes from a memory pool handed to it
+ by calling this function. The memory pool is just a pointer and
+ length for some block of memory that is to be used for string
+ allocation. It can come from the stack, heap or other.
+
+ The memory pool must be QCBOR_DECODE_MIN_MEM_POOL_SIZE plus space for
+ all the strings allocated. There is no overhead per string allocated
+
+ This memory pool is used for all indefinite length strings that are
+ text strings or byte strings, including strings used as labels.
+
+ The pointers to strings in QCBORItem will point into the memory pool set
+ here. They do not need to be individually freed. Just discard the buffer
+ when they are no longer needed.
+
+ If bAllStrings is set, then the size will be the overhead plus the
+ space to hold **all** strings, definite and indefinite length, value
+ or label. The advantage of this is that after the decode is complete,
+ the original memory holding the encoded CBOR does not need to remain
+ valid.
+
+ If this function is never called because there is no need to support
+ indefinite length strings, the MemPool implementation should be
+ dead-stripped by the loader and not add to code size.
+ */
+QCBORError QCBORDecode_SetMemPool(QCBORDecodeContext *pCtx, UsefulBuf MemPool, bool bAllStrings);
+
+
+/**
+ @brief Sets up a custom string allocator for indefinite length strings
+
+ @param[in] pCtx The decoder context to set up an allocator for
+ @param[in] pAllocator The string allocator "object"
+
+ See QCBORStringAllocator for the requirements of the string allocator.
+
+ Typically, this is used if the simple MemPool allocator isn't desired.
+
+ A malloc based string allocator can be obtained by calling
+ QCBOR_DMalloc(). This function is supply separately from qcbor
+ to keep qcbor smaller and neater. It is in a separate
+ GitHub repository.
+
+ You can also write your own allocator. Create the allocate, free,
+ and destroy functions and put pointers to them in a QCBORStringAllocator.
+ */
+void QCBORDecode_SetUpAllocator(QCBORDecodeContext *pCtx, const QCBORStringAllocator *pAllocator, bool bAllStrings);
+
+
+/**
+ @brief Configure list of caller selected tags to be recognized
+
+ @param[in] pCtx The decode context.
+ @param[out] pTagList Structure holding the list of tags to configure
+
+ This is used to tell the decoder about tags beyond those that are
+ built-in that should be recognized. The built-in tags are those
+ with macros of the form CBOR_TAG_XXX.
+
+ See description of QCBORTagListIn.
+ */
+void QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext *pCtx, const QCBORTagListIn *pTagList);
+
+
+/**
+ @brief Gets the next item (integer, byte string, array...) in pre order traversal of CBOR tree
+
+ @param[in] pCtx The decoder context.
+ @param[out] pDecodedItem Holds the CBOR item just decoded.
+
+ @return 0 or error. All errors except QCBOR_ERR_TOO_MANY_TAGS
+ and QCBOR_ERR_STRING_ALLOCATE indicate that the CBOR input
+ could not be decoded. In most cases
+ this is because the CBOR is invalid. In a few cases
+ (QCBOR_ERR_ARRAY_NESTING_TOO_DEEP, QCBOR_ERR_INT_OVERFLOW,
+ QCBOR_ERR_DATE_OVERFLOW) it is because the CBOR is beyond
+ the limits of what this implementation can handle.
+ QCBOR_ERR_NO_STRING_ALLOCATOR indicates CBOR that cannot
+ be handled unless a string allocator is configured.
+ QCBOR_ERR_MAP_LABEL_TYPE is in a way a limitation of
+ this implementation, but can be avoided by decoding
+ in QCBOR_DECODE_MODE_MAP_AS_ARRAY mode.
+
+ pDecodedItem is filled in with the value parsed. Generally, the
+ following data is returned in the structure.
+
+ - The data type in uDataType which indicates which member of the val
+ union the data is in. This decoder figures out the type based on the
+ CBOR major type, the CBOR "additionalInfo", the CBOR optional tags
+ and the value of the integer.
+
+ - The value of the item, which might be an integer, a pointer and a
+ length, the count of items in an array, a floating-point number or
+ other.
+
+ - The nesting level for maps and arrays.
+
+ - The label for an item in a map, which may be a text or byte string or an integer.
+
+ - The CBOR optional tag or tags.
+
+ See documentation on in the data type QCBORItem for all the details
+ on what is returned.
+
+ This function also handles arrays and maps. When first encountered a
+ QCBORItem will be returned with major type CBOR_MAJOR_TYPE_ARRAY or
+ CBOR_MAJOR_TYPE_ARRAY_MAP. QCBORItem.val.uCount will indicate the number
+ of Items in the array or map. Typically, an implementation will call
+ QCBORDecode_GetNext() in a for loop to fetch them all. When decoding
+ indefinite length maps and arrays, QCBORItem.val.uCount is UINT16_MAX
+ and uNextNestLevel must be used to know when the end of a map
+ or array is reached.
+
+ Nesting level 0 is the outside top-most nesting level. For example, in
+ a CBOR structure with two items, an integer and a byte string only,
+ both would be at nesting level 0. A CBOR structure with an array
+ open, an integer and a byte string, would have the integer and byte
+ string as nesting level 1.
+
+ Here is an example of how the nesting level is reported with no arrays
+ or maps at all
+
+ @verbatim
+ CBOR Structure Nesting Level
+ Integer 0
+ Byte String 0
+ @endverbatim
+
+ Here is an example of how the nesting level is reported with an a simple
+ array and some top-level items.
+
+ @verbatim
+ Integer 0
+ Array (with 2 items) 0
+ Byte String 1
+ Byte string 1
+ Integer 0
+ @endverbatim
+
+
+ Here's a more complex example
+ @verbatim
+
+ Map with 2 items 0
+ Text string 1
+ Array with 3 integers 1
+ integer 2
+ integer 2
+ integer 2
+ text string 1
+ byte string 1
+ @endverbatim
+
+ In QCBORItem, uNextNestLevel is the nesting level for the next call
+ to QCBORDecode_GetNext(). It indicates if any maps or arrays were closed
+ out during the processing of the just-fecthed QCBORItem. This processing
+ includes a look-ahead for any breaks that close out indefinite length
+ arrays or maps. This value is needed to be able to understand the
+ hierarchical structure. If uNextNestLevel is not equal to uNestLevel
+ the end of the current map or array has been encountered. This
+ works the same for both definite and indefinite length arrays.
+
+ Most uses of this decoder will not need to do anything extra for
+ tag handling. The built-in tags, those with a macro of the form
+ CBOR_TAG_XXXX, will be enough.
+
+ If tags beyond built-in tags are to be recognized, they must be
+ configured by calling QCBORDecode_SetCallerConfiguredTags(). If
+ a tag is not recognized it is silently ignored.
+
+ Several tagged types are automatically recognized and decoded and
+ returned in their decoded form.
+
+ To find out if a QCBORItem was tagged with a particular tag
+ call QCBORDecode_IsTagged(). This works only for built-in
+ tags and caller-configured tags.
+
+ To get the full list of tags on an Item without having to
+ pre-configure any predetermined list of tags use
+ QCBORDecode_GetNextWithTags().
+ */
+QCBORError QCBORDecode_GetNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
+
+
+/**
+ @brief Gets the next item including full list of tags for item
+
+ @param[in] pCtx The decoder context.
+ @param[out] pDecodedItem Holds the CBOR item just decoded.
+ @param[in,out] pTagList On input array to put tags in; on output the tags on this item.
+
+ @return 0 or error.
+
+ This works the same as QCBORDecode_GetNext() except that it also returns
+ the full list of tags for the data item. This function should only
+ be needed when parsing CBOR to print it out or convert it to some other
+ format. It should not be needed in an actual CBOR protocol implementation.
+
+ Tags will be returned here whether or not they are in the built-in or
+ caller-configured tag lists.
+
+ CBOR has no upper bound of limit on the number of tags that can be
+ associated with a data item. In practice the number of tags on an item
+ will usually be small, perhaps less than five. This will return an error
+ if the array in pTagList is too small to hold all the tags for an item.
+
+ (This function is separate from QCBORDecode_GetNext() so as to not have to
+ make QCBORItem large enough to be able to hold a full list of tags. Even a list of
+ five tags would nearly double its size because tags can be a uint64_t).
+ */
+QCBORError QCBORDecode_GetNextWithTags(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem, QCBORTagListOut *pTagList);
+
+
+/**
+ @brief Determine if a CBOR item was tagged with a particular tag
+
+ @param[in] pCtx The decoder context.
+ @param[in] pItem The CBOR item to check
+ @param[in] uTag The tag to check
+
+ @return 1 if it was tagged, 0 if not
+
+ QCBORDecode_GetNext() processes tags by looking them up
+ in two lists and setting a bit corresponding to the tag
+ in uTagBits in the QCBORItem. To find out if a
+ QCBORItem was tagged with a particular tag, call
+ this function. It handles the mapping between
+ the two lists of tags and the bits set for it.
+
+ The first tag list is the built-in tags, those
+ with a macro of the form CBOR_TAG_XXX in this
+ header file. There are up to 48 of these,
+ corresponding to the lower 48 tag bits.
+
+ The other optional tag list is the ones
+ the caller configured using QCBORDecode_SetCallerConfiguredTagList()
+ There are QCBOR_MAX_CUSTOM_TAGS (16) of these corresponding to the
+ upper 16 tag bits.
+
+ See also QCBORDecode_GetTags() and QCBORDecode_GetNextWithTags().
+ */
+int QCBORDecode_IsTagged(QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint64_t uTag);
+
+
+/**
+ Check whether all the bytes have been decoded and maps and arrays closed.
+
+ @param[in] pCtx The context to check
+
+ @return QCBOR_SUCCESS or error
+
+ This tells you if all the bytes given to QCBORDecode_Init() have
+ been consumed and whether all maps and arrays were closed.
+ The decode is considered to be incorrect or incomplete if not
+ and an error will be returned.
+ */
+QCBORError QCBORDecode_Finish(QCBORDecodeContext *pCtx);
+
+
+
+
+/**
+ Convert int64_t to smaller int's safely
+
+ @param [in] src An int64_t
+ @param [out] dest A smaller sized int to convert to
+
+ @return 0 on success -1 if not
+
+ When decoding an integer, the CBOR decoder will return the value as an
+ int64_t unless the integer is in the range of INT64_MAX and
+ UINT64_MAX. That is, unless the value is so large that it can only be
+ represented as a uint64_t, it will be an int64_t.
+
+ CBOR itself doesn't size the individual integers it carries at
+ all. The only limits it puts on the major integer types is that they
+ are 8 bytes or less in length. Then encoders like this one use the
+ smallest number of 1, 2, 4 or 8 bytes to represent the integer based
+ on its value. There is thus no notion that one data item in CBOR is
+ an 1 byte integer and another is a 4 byte integer.
+
+ The interface to this CBOR encoder only uses 64-bit integers. Some
+ CBOR protocols or implementations of CBOR protocols may not want to
+ work with something smaller than a 64-bit integer. Perhaps an array
+ of 1000 integers needs to be sent and none has a value larger than
+ 50,000 and are represented as uint16_t.
+
+ The sending / encoding side is easy. Integers are temporarily widened
+ to 64-bits as a parameter passing through QCBOREncode_AddInt64() and
+ encoded in the smallest way possible for their value, possibly in
+ less than an uint16_t.
+
+ On the decoding side the integers will be returned at int64_t even if
+ they are small and were represented by only 1 or 2 bytes in the
+ encoded CBOR. The functions here will convert integers to a small
+ representation with an overflow check.
+
+ (The decoder could have support 8 different integer types and
+ represented the integer with the smallest type automatically, but
+ this would have made the decoder more complex and code calling the
+ decoder more complex in most use cases. In most use cases on 64-bit
+ machines it is no burden to carry around even small integers as
+ 64-bit values).
+ */
+static inline int QCBOR_Int64ToInt32(int64_t src, int32_t *dest)
+{
+ if(src > INT32_MAX || src < INT32_MIN) {
+ return -1;
+ } else {
+ *dest = (int32_t) src;
+ }
+ return 0;
+}
+
+static inline int QCBOR_Int64ToInt16(int64_t src, int16_t *dest)
+{
+ if(src > INT16_MAX || src < INT16_MIN) {
+ return -1;
+ } else {
+ *dest = (int16_t) src;
+ }
+ return 0;
+}
+
+static inline int QCBOR_Int64ToInt8(int64_t src, int8_t *dest)
+{
+ if(src > INT8_MAX || src < INT8_MIN) {
+ return -1;
+ } else {
+ *dest = (int8_t) src;
+ }
+ return 0;
+}
+
+static inline int QCBOR_Int64ToUInt32(int64_t src, uint32_t *dest)
+{
+ if(src > UINT32_MAX || src < 0) {
+ return -1;
+ } else {
+ *dest = (uint32_t) src;
+ }
+ return 0;
+}
+
+static inline int QCBOR_Int64UToInt16(int64_t src, uint16_t *dest)
+{
+ if(src > UINT16_MAX || src < 0) {
+ return -1;
+ } else {
+ *dest = (uint16_t) src;
+ }
+ return 0;
+}
+
+static inline int QCBOR_Int64ToUInt8(int64_t src, uint8_t *dest)
+{
+ if(src > UINT8_MAX || src < 0) {
+ return -1;
+ } else {
+ *dest = (uint8_t) src;
+ }
+ return 0;
+}
+
+static inline int QCBOR_Int64ToUInt64(int64_t src, uint64_t *dest)
+{
+ if(src > 0) {
+ return -1;
+ } else {
+ *dest = (uint64_t) src;
+ }
+ return 0;
+}
+
+
+
+
+
+/* ===========================================================================
+ BEGINNING OF PRIVATE INLINE IMPLEMENTATION
+
+ =========================================================================== */
+
+/**
+ @brief Semi-private method to add a buffer full of bytes to encoded output
+
+ @param[in] pCtx The encoding context to add the integer to.
+ @param[in] uMajorType The CBOR major type of the bytes.
+ @param[in] Bytes The bytes to add.
+
+ Use QCBOREncode_AddText() or QCBOREncode_AddBytes() or
+ QCBOREncode_AddEncoded() instead. They are inline functions
+ that call this and supply the correct major type. This function
+ is public to make the inline functions work to keep the overall
+ code size down and because the C language has no way to make
+ it private.
+
+ If this is called the major type should be CBOR_MAJOR_TYPE_TEXT_STRING,
+ CBOR_MAJOR_TYPE_BYTE_STRING or CBOR_MAJOR_NONE_TYPE_RAW. The last
+ one is special for adding already-encoded CBOR.
+ */
+void QCBOREncode_AddBuffer(QCBOREncodeContext *pCtx, uint8_t uMajorType, UsefulBufC Bytes);
+
+
+/**
+ @brief Semi-private method to open a map, array or bstr wrapped CBOR
+
+ @param[in] pCtx The context to add to.
+ @param[in] uMajorType The major CBOR type to close
+
+ Call QCBOREncode_OpenArray(), QCBOREncode_OpenMap() or
+ QCBOREncode_BstrWrap() instead of this.
+ */
+void QCBOREncode_OpenMapOrArray(QCBOREncodeContext *pCtx, uint8_t uMajorType);
+
+
+/**
+ @brief Semi-private method to close a map, array or bstr wrapped CBOR
+
+ @param[in] pCtx The context to add to.
+ @param[in] uMajorType The major CBOR type to close
+ @param[out] pWrappedCBOR UsefulBufC containing wrapped bytes
+
+ Call QCBOREncode_CloseArray(), QCBOREncode_CloseMap() or
+ QCBOREncode_CloseBstrWrap() instead of this.
+ */
+void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *pCtx, uint8_t uMajorType, UsefulBufC *pWrappedCBOR);
+
+
+/**
+ @brief Semi-private method to add simple types.
+
+ @param[in] pCtx The encoding context to add the simple value to.
+ @param[in] uSize Minimum encoding size for uNum. Usually 0.
+ @param[in] uNum One of CBOR_SIMPLEV_FALSE through _UNDEF or other.
+
+ This is used to add simple types like true and false.
+
+ Call QCBOREncode_AddBool(), QCBOREncode_AddNULL(), QCBOREncode_AddUndef()
+ instead of this.
+
+ This function can add simple values that are not defined by CBOR yet. This expansion
+ point in CBOR should not be used unless they are standardized.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+ */
+void QCBOREncode_AddType7(QCBOREncodeContext *pCtx, size_t uSize, uint64_t uNum);
+
+
+static inline void QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t uNum)
+{
+ QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel)); // AddSZString not defined yet
+ QCBOREncode_AddInt64(pCtx, uNum);
+}
+
+static inline void QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t uNum)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddInt64(pCtx, uNum);
+}
+
+
+static inline void QCBOREncode_AddUInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, uint64_t uNum)
+{
+ QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel)); // AddSZString not defined yet
+ QCBOREncode_AddUInt64(pCtx, uNum);
+}
+
+static inline void QCBOREncode_AddUInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, uint64_t uNum)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddUInt64(pCtx, uNum);
+}
+
+
+static inline void QCBOREncode_AddText(QCBOREncodeContext *pCtx, UsefulBufC Text)
+{
+ QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_TEXT_STRING, Text);
+}
+
+static inline void QCBOREncode_AddTextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Text)
+{
+ QCBOREncode_AddText(pCtx, UsefulBuf_FromSZ(szLabel)); // AddSZString not defined yet
+ QCBOREncode_AddText(pCtx, Text);
+}
+
+static inline void QCBOREncode_AddTextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Text)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddText(pCtx, Text);
+}
+
+
+inline static void QCBOREncode_AddSZString(QCBOREncodeContext *pCtx, const char *szString)
+{
+ QCBOREncode_AddText(pCtx, UsefulBuf_FromSZ(szString));
+}
+
+static inline void QCBOREncode_AddSZStringToMap(QCBOREncodeContext *pCtx, const char *szLabel, const char *szString)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddSZString(pCtx, szString);
+}
+
+static inline void QCBOREncode_AddSZStringToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, const char *szString)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddSZString(pCtx, szString);
+}
+
+
+static inline void QCBOREncode_AddDoubleToMap(QCBOREncodeContext *pCtx, const char *szLabel, double dNum)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddDouble(pCtx, dNum);
+}
+
+static inline void QCBOREncode_AddDoubleToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, double dNum)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddDouble(pCtx, dNum);
+}
+
+
+static inline void QCBOREncode_AddDateEpoch(QCBOREncodeContext *pCtx, int64_t date)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_EPOCH);
+ QCBOREncode_AddInt64(pCtx, date);
+}
+
+static inline void QCBOREncode_AddDateEpochToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t date)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_EPOCH);
+ QCBOREncode_AddInt64(pCtx, date);
+}
+
+static inline void QCBOREncode_AddDateEpochToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t date)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_EPOCH);
+ QCBOREncode_AddInt64(pCtx, date);
+}
+
+
+static inline void QCBOREncode_AddBytes(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+{
+ QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_BYTE_STRING, Bytes);
+}
+
+static inline void QCBOREncode_AddBytesToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddBytesToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+
+static inline void QCBOREncode_AddBinaryUUID(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_BIN_UUID);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddBinaryUUIDToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_BIN_UUID);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddBinaryUUIDToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_BIN_UUID);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+
+static inline void QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_POS_BIGNUM);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_POS_BIGNUM);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_POS_BIGNUM);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+
+static inline void QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_NEG_BIGNUM);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_NEG_BIGNUM);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_NEG_BIGNUM);
+ QCBOREncode_AddBytes(pCtx, Bytes);
+}
+
+
+static inline void QCBOREncode_AddURI(QCBOREncodeContext *pCtx, UsefulBufC URI)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_URI);
+ QCBOREncode_AddText(pCtx, URI);
+}
+
+static inline void QCBOREncode_AddURIToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC URI)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_URI);
+ QCBOREncode_AddText(pCtx, URI);
+}
+
+static inline void QCBOREncode_AddURIToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC URI)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_URI);
+ QCBOREncode_AddText(pCtx, URI);
+}
+
+
+
+static inline void QCBOREncode_AddB64Text(QCBOREncodeContext *pCtx, UsefulBufC B64Text)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_B64);
+ QCBOREncode_AddText(pCtx, B64Text);
+}
+
+static inline void QCBOREncode_AddB64TextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_B64);
+ QCBOREncode_AddText(pCtx, B64Text);
+}
+
+static inline void QCBOREncode_AddB64TextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_B64);
+ QCBOREncode_AddText(pCtx, B64Text);
+}
+
+
+static inline void QCBOREncode_AddB64URLText(QCBOREncodeContext *pCtx, UsefulBufC B64Text)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_B64URL);
+ QCBOREncode_AddText(pCtx, B64Text);
+}
+
+static inline void QCBOREncode_AddB64URLTextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_B64URL);
+ QCBOREncode_AddText(pCtx, B64Text);
+}
+
+static inline void QCBOREncode_AddB64URLTextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_B64URL);
+ QCBOREncode_AddText(pCtx, B64Text);
+}
+
+
+static inline void QCBOREncode_AddRegex(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_REGEX);
+ QCBOREncode_AddText(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddRegexToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_REGEX);
+ QCBOREncode_AddText(pCtx, Bytes);
+}
+
+static inline void QCBOREncode_AddRegexToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_REGEX);
+ QCBOREncode_AddText(pCtx, Bytes);
+}
+
+
+static inline void QCBOREncode_AddMIMEData(QCBOREncodeContext *pCtx, UsefulBufC MIMEData)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_MIME);
+ QCBOREncode_AddText(pCtx, MIMEData);
+}
+
+static inline void QCBOREncode_AddMIMEDataToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC MIMEData)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_MIME);
+ QCBOREncode_AddText(pCtx, MIMEData);
+}
+
+static inline void QCBOREncode_AddMIMEDataToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC MIMEData)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_MIME);
+ QCBOREncode_AddText(pCtx, MIMEData);
+}
+
+
+static inline void QCBOREncode_AddDateString(QCBOREncodeContext *pCtx, const char *szDate)
+{
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_STRING);
+ QCBOREncode_AddSZString(pCtx, szDate);
+}
+
+static inline void QCBOREncode_AddDateStringToMap(QCBOREncodeContext *pCtx, const char *szLabel, const char *szDate)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_STRING);
+ QCBOREncode_AddSZString(pCtx, szDate);
+}
+
+static inline void QCBOREncode_AddDateStringToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, const char *szDate)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_STRING);
+ QCBOREncode_AddSZString(pCtx, szDate);
+}
+
+
+static inline void QCBOREncode_AddSimple(QCBOREncodeContext *pCtx, uint64_t uNum)
+{
+ QCBOREncode_AddType7(pCtx, 0, uNum);
+}
+
+static inline void QCBOREncode_AddSimpleToMap(QCBOREncodeContext *pCtx, const char *szLabel, uint8_t uSimple)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddSimple(pCtx, uSimple);
+}
+
+static inline void QCBOREncode_AddSimpleToMapN(QCBOREncodeContext *pCtx, int nLabel, uint8_t uSimple)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddSimple(pCtx, uSimple);
+}
+
+
+static inline void QCBOREncode_AddBool(QCBOREncodeContext *pCtx, bool b)
+{
+ uint8_t uSimple = CBOR_SIMPLEV_FALSE;
+ if(b) {
+ uSimple = CBOR_SIMPLEV_TRUE;
+ }
+ QCBOREncode_AddSimple(pCtx, uSimple);
+}
+
+static inline void QCBOREncode_AddBoolToMap(QCBOREncodeContext *pCtx, const char *szLabel, bool b)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddBool(pCtx, b);
+}
+
+static inline void QCBOREncode_AddBoolToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, bool b)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddBool(pCtx, b);
+}
+
+
+static inline void QCBOREncode_AddNULL(QCBOREncodeContext *pCtx)
+{
+ QCBOREncode_AddSimple(pCtx, CBOR_SIMPLEV_NULL);
+}
+
+static inline void QCBOREncode_AddNULLToMap(QCBOREncodeContext *pCtx, const char *szLabel)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddNULL(pCtx);
+}
+
+static inline void QCBOREncode_AddNULLToMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddNULL(pCtx);
+}
+
+
+static inline void QCBOREncode_AddUndef(QCBOREncodeContext *pCtx)
+{
+ QCBOREncode_AddSimple(pCtx, CBOR_SIMPLEV_UNDEF);
+}
+
+static inline void QCBOREncode_AddUndefToMap(QCBOREncodeContext *pCtx, const char *szLabel)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddUndef(pCtx);
+}
+
+static inline void QCBOREncode_AddUndefToMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddUndef(pCtx);
+}
+
+
+static inline void QCBOREncode_OpenArray(QCBOREncodeContext *pCtx)
+{
+ QCBOREncode_OpenMapOrArray(pCtx, CBOR_MAJOR_TYPE_ARRAY);
+}
+
+static inline void QCBOREncode_OpenArrayInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_OpenArray(pCtx);
+}
+
+static inline void QCBOREncode_OpenArrayInMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_OpenArray(pCtx);
+}
+
+static inline void QCBOREncode_CloseArray(QCBOREncodeContext *pCtx)
+{
+ QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_ARRAY, NULL);
+}
+
+
+static inline void QCBOREncode_OpenMap(QCBOREncodeContext *pCtx)
+{
+ QCBOREncode_OpenMapOrArray(pCtx, CBOR_MAJOR_TYPE_MAP);
+}
+
+static inline void QCBOREncode_OpenMapInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_OpenMap(pCtx);
+}
+
+static inline void QCBOREncode_OpenMapInMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_OpenMap(pCtx);
+}
+
+static inline void QCBOREncode_CloseMap(QCBOREncodeContext *pCtx)
+{
+ QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_MAP, NULL);
+}
+
+
+static inline void QCBOREncode_BstrWrap(QCBOREncodeContext *pCtx)
+{
+ QCBOREncode_OpenMapOrArray(pCtx, CBOR_MAJOR_TYPE_BYTE_STRING);
+}
+
+static inline void QCBOREncode_BstrWrapInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_BstrWrap(pCtx);
+}
+
+static inline void QCBOREncode_BstrWrapInMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_BstrWrap(pCtx);
+}
+
+static inline void QCBOREncode_CloseBstrWrap(QCBOREncodeContext *pCtx, UsefulBufC *pWrappedCBOR)
+{
+ QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_BYTE_STRING, pWrappedCBOR);
+}
+
+
+static inline void QCBOREncode_AddEncoded(QCBOREncodeContext *pCtx, UsefulBufC Encoded)
+{
+ QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_NONE_TYPE_RAW, Encoded);
+}
+
+static inline void QCBOREncode_AddEncodedToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Encoded)
+{
+ QCBOREncode_AddSZString(pCtx, szLabel);
+ QCBOREncode_AddEncoded(pCtx, Encoded);
+}
+
+static inline void QCBOREncode_AddEncodedToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Encoded)
+{
+ QCBOREncode_AddInt64(pCtx, nLabel);
+ QCBOREncode_AddEncoded(pCtx, Encoded);
+}
+
+
+/* ===========================================================================
+ END OF PRIVATE INLINE IMPLEMENTATION
+
+ =========================================================================== */
+
+#endif /* defined(__QCBOR__qcbor__) */
+
diff --git a/lib/ext/qcbor/inc/useful_buf.h b/lib/ext/qcbor/inc/useful_buf.h
new file mode 100644
index 0000000..dcb88b8
--- /dev/null
+++ b/lib/ext/qcbor/inc/useful_buf.h
@@ -0,0 +1,143 @@
+/*
+ * useful_buf.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.mdE.
+ */
+
+
+#ifndef __USEFUL_BUF_H__
+#define __USEFUL_BUF_H__
+
+#include "UsefulBuf.h"
+
+
+/**
+ * \file useful_buf.h
+ *
+ * \brief This is a TF-M coding style version of UsefulBuf.
+ * See UsefulBuf for documentation of these functions.
+ */
+
+
+#define NULL_USEFUL_BUF_C NULLUsefulBufC
+
+#define NULL_USEFUL_BUF NULLUsefulBuf
+
+
+static inline int useful_buf_c_is_null(struct useful_buf_c in)
+{
+ return UsefulBuf_IsNULLC(in);
+}
+
+
+static inline int useful_buf_is_null(struct useful_buf in)
+{
+ return UsefulBuf_IsNULL(in);
+}
+
+
+static inline int useful_buf_c_is_empty(struct useful_buf_c in)
+{
+ return UsefulBuf_IsEmptyC(in);
+}
+
+static inline int useful_buf_is_empty(struct useful_buf in)
+{
+ return UsefulBuf_IsEmpty(in);
+}
+
+
+static inline int useful_buf_is_null_or_empty(struct useful_buf in)
+{
+ return UsefulBuf_IsNULLOrEmpty(in);
+}
+
+
+static inline int useful_buf_c_is_null_or_empty(struct useful_buf_c in)
+{
+ return UsefulBuf_IsNULLOrEmptyC(in);
+}
+
+
+static inline struct useful_buf useful_buf_unconst(struct useful_buf_c in)
+{
+ return UsefulBuf_Unconst(in);
+}
+
+#define USEFUL_BUF_FROM_SZ_LITERAL UsefulBuf_FROM_SZ_LITERAL
+
+#define USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL UsefulBuf_FROM_BYTE_ARRAY_LITERAL
+
+#define USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB
+
+#define USEFUL_BUF_FROM_BYTE_ARRAY UsefulBuf_FROM_BYTE_ARRAY
+
+
+static inline struct useful_buf_c useful_buf_from_sz(const char *string)
+{
+ return UsefulBuf_FromSZ(string);
+}
+
+static inline struct
+useful_buf_c useful_buf_copy_offset(struct useful_buf dest,
+ size_t offset,
+ struct useful_buf_c src)
+{
+ return UsefulBuf_CopyOffset(dest, offset, src);
+}
+
+
+
+static inline struct useful_buf_c useful_buf_copy(struct useful_buf dest,
+ struct useful_buf_c src)
+{
+ return UsefulBuf_Copy(dest, src);
+}
+
+
+static inline struct useful_buf_c useful_buf_set(struct useful_buf dest,
+ uint8_t value)
+{
+ return UsefulBuf_Set(dest, value);
+}
+
+
+static inline struct useful_buf_c useful_buf_copy_ptr(struct useful_buf dest,
+ const void *ptr,
+ size_t len)
+{
+ return UsefulBuf_CopyPtr(dest, ptr, len);
+}
+
+
+static inline struct useful_buf_c useful_buf_head(struct useful_buf_c buf,
+ size_t amount)
+{
+ return UsefulBuf_Head(buf, amount);
+}
+
+static inline struct useful_buf_c useful_buf_tail(struct useful_buf_c buf,
+ size_t amount)
+{
+ return UsefulBuf_Tail(buf, amount);
+}
+
+static inline int useful_buf_compare(const struct useful_buf_c buf1,
+ const struct useful_buf_c buf2)
+{
+ return UsefulBuf_Compare(buf1, buf2);
+}
+
+static inline size_t
+useful_buf_find_bytes(const struct useful_buf_c bytes_to_search,
+ const struct useful_buf_c bytes_to_find)
+{
+ return UsefulBuf_FindBytes(bytes_to_search, bytes_to_find);
+}
+
+
+#endif /* __USEFUL_BUF_H__ */
diff --git a/lib/ext/qcbor/src/UsefulBuf.c b/lib/ext/qcbor/src/UsefulBuf.c
new file mode 100644
index 0000000..5a7d37b
--- /dev/null
+++ b/lib/ext/qcbor/src/UsefulBuf.c
@@ -0,0 +1,329 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+/*===================================================================================
+ FILE: UsefulBuf.c
+
+ DESCRIPTION: General purpose input and output buffers
+
+ EDIT HISTORY FOR FILE:
+
+ This section contains comments describing changes made to the module.
+ Notice that changes are listed in reverse chronological order.
+
+ when who what, where, why
+ -------- ---- ---------------------------------------------------
+ 09/07/17 llundbla Fix critical bug in UsefulBuf_Find() -- a read off
+ the end of memory when the bytes to find is longer
+ than the bytes to search.
+ 06/27/17 llundbla Fix UsefulBuf_Compare() bug. Only affected comparison
+ for < or > for unequal length buffers. Added
+ UsefulBuf_Set() function.
+ 05/30/17 llundbla Functions for NULL UsefulBufs and const / unconst
+ 11/13/16 llundbla Initial Version.
+
+ =====================================================================================*/
+
+#include "UsefulBuf.h"
+
+#define USEFUL_OUT_BUF_MAGIC (0x0B0F) // used to catch use of uninitialized or corrupted UOBs
+
+
+/*
+ Public function -- see UsefulBuf.h
+ */
+UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src)
+{
+ // Do this with subtraction so it doesn't give erroneous result if uOffset + Src.len overflows
+ if(uOffset > Dest.len || Src.len > Dest.len - uOffset) { // uOffset + Src.len > Dest.len
+ return NULLUsefulBufC;
+ }
+
+ memcpy((uint8_t *)Dest.ptr + uOffset, Src.ptr, Src.len);
+
+ return (UsefulBufC){Dest.ptr, Src.len + uOffset};
+}
+
+
+/*
+ Public function -- see UsefulBuf.h
+ */
+int UsefulBuf_Compare(const UsefulBufC UB1, const UsefulBufC UB2)
+{
+ // use the comparisons rather than subtracting lengths to
+ // return an int instead of a size_t
+ if(UB1.len < UB2.len) {
+ return -1;
+ } else if (UB1.len > UB2.len) {
+ return 1;
+ } // else UB1.len == UB2.len
+
+ return memcmp(UB1.ptr, UB2.ptr, UB1.len);
+}
+
+
+
+/*
+ Public function -- see UsefulBuf.h
+ */
+size_t UsefulBuf_FindBytes(UsefulBufC BytesToSearch, UsefulBufC BytesToFind)
+{
+ if(BytesToSearch.len < BytesToFind.len) {
+ return SIZE_MAX;
+ }
+
+ for(size_t uPos = 0; uPos <= BytesToSearch.len - BytesToFind.len; uPos++) {
+ if(!UsefulBuf_Compare((UsefulBufC){((uint8_t *)BytesToSearch.ptr) + uPos, BytesToFind.len}, BytesToFind)) {
+ return uPos;
+ }
+ }
+
+ return SIZE_MAX;
+}
+
+
+/*
+ Public function -- see UsefulBuf.h
+
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+void UsefulOutBuf_Init(UsefulOutBuf *me, UsefulBuf Storage)
+{
+ me->magic = USEFUL_OUT_BUF_MAGIC;
+ UsefulOutBuf_Reset(me);
+ me->UB = Storage;
+
+#if 0
+ // This check is off by default.
+
+ // The following check fails on ThreadX
+
+ // Sanity check on the pointer and size to be sure we are not
+ // passed a buffer that goes off the end of the address space.
+ // Given this test, we know that all unsigned lengths less than
+ // me->size are valid and won't wrap in any pointer additions
+ // based off of pStorage in the rest of this code.
+ const uintptr_t ptrM = UINTPTR_MAX - Storage.len;
+ if(Storage.ptr && (uintptr_t)Storage.ptr > ptrM) // Check #0
+ me->err = 1;
+#endif
+}
+
+
+
+/*
+ Public function -- see UsefulBuf.h
+
+ The core of UsefulOutBuf -- put some bytes in the buffer without writing off the end of it.
+
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+
+ This function inserts the source buffer, NewData, into the destination buffer, me->UB.ptr.
+
+ Destination is represented as:
+ me->UB.ptr -- start of the buffer
+ me->UB.len -- size of the buffer UB.ptr
+ me->data_len -- length of value data in UB
+
+ Source is data:
+ NewData.ptr -- start of source buffer
+ NewData.len -- length of source buffer
+
+ Insertion point:
+ uInsertionPos.
+
+ Steps:
+
+ 0. Corruption checks on UsefulOutBuf
+
+ 1. Figure out if the new data will fit or not
+
+ 2. Is insertion position in the range of valid data?
+
+ 3. If insertion point is not at the end, slide data to the right of the insertion point to the right
+
+ 4. Put the new data in at the insertion position.
+
+ */
+void UsefulOutBuf_InsertUsefulBuf(UsefulOutBuf *me, UsefulBufC NewData, size_t uInsertionPos)
+{
+ if(me->err) {
+ // Already in error state.
+ return;
+ }
+
+ /* 0. Sanity check the UsefulOutBuf structure */
+ // A "counter measure". If magic number is not the right number it
+ // probably means me was not initialized or it was corrupted. Attackers
+ // can defeat this, but it is a hurdle and does good with very
+ // little code.
+ if(me->magic != USEFUL_OUT_BUF_MAGIC) {
+ me->err = 1;
+ return; // Magic number is wrong due to uninitalization or corrption
+ }
+
+ // Make sure valid data is less than buffer size. This would only occur
+ // if there was corruption of me, but it is also part of the checks to
+ // be sure there is no pointer arithmatic under/overflow.
+ if(me->data_len > me->UB.len) { // Check #1
+ me->err = 1;
+ return; // Offset of valid data is off the end of the UsefulOutBuf due to uninitialization or corruption
+ }
+
+ /* 1. Will it fit? */
+ // WillItFit() is the same as: NewData.len <= (me->size - me->data_len)
+ // Check #1 makes sure subtraction in RoomLeft will not wrap around
+ if(! UsefulOutBuf_WillItFit(me, NewData.len)) { // Check #2
+ // The new data will not fit into the the buffer.
+ me->err = 1;
+ return;
+ }
+
+ /* 2. Check the Insertion Position */
+ // This, with Check #1, also confirms that uInsertionPos <= me->data_len
+ if(uInsertionPos > me->data_len) { // Check #3
+ // Off the end of the valid data in the buffer.
+ me->err = 1;
+ return;
+ }
+
+ /* 3. Slide existing data to the right */
+ uint8_t *pSourceOfMove = ((uint8_t *)me->UB.ptr) + uInsertionPos; // PtrMath #1
+ size_t uNumBytesToMove = me->data_len - uInsertionPos; // PtrMath #2
+ uint8_t *pDestinationOfMove = pSourceOfMove + NewData.len; // PtrMath #3
+
+ if(uNumBytesToMove && me->UB.ptr) {
+ // To know memmove won't go off end of destination, see PtrMath #4
+ memmove(pDestinationOfMove, pSourceOfMove, uNumBytesToMove);
+ }
+
+ /* 4. Put the new data in */
+ uint8_t *pInsertionPoint = ((uint8_t *)me->UB.ptr) + uInsertionPos; // PtrMath #5
+ if(me->UB.ptr) {
+ // To know memmove won't go off end of destination, see PtrMath #6
+ memmove(pInsertionPoint, NewData.ptr, NewData.len);
+ }
+ me->data_len += NewData.len ;
+}
+
+
+/*
+ Rationale that describes why the above pointer math is safe
+
+ PtrMath #1 will never wrap around over because
+ Check #0 in UsefulOutBuf_Init makes sure me->UB.ptr + me->UB.len doesn't wrap
+ Check #1 makes sure me->data_len is less than me->UB.len
+ Check #3 makes sure uInsertionPos is less than me->data_len
+
+ PtrMath #2 will never wrap around under because
+ Check #3 makes sure uInsertionPos is less than me->data_len
+
+ PtrMath #3 will never wrap around over because todo
+ PtrMath #1 is checked resulting in pSourceOfMove being between me->UB.ptr and a maximum valid ptr
+ Check #2 that NewData.len will fit
+
+ PtrMath #4 will never wrap under because
+ Calculation for extent or memmove is uRoomInDestination = me->UB.len - (uInsertionPos + NewData.len)
+ Check #3 makes sure uInsertionPos is less than me->data_len
+ Check #3 allows Check #2 to be refactored as NewData.Len > (me->size - uInsertionPos)
+ This algebraically rearranges to me->size > uInsertionPos + NewData.len
+
+ PtrMath #5 is exactly the same as PtrMath #1
+
+ PtrMath #6 will never wrap under because
+ Calculation for extent of memove is uRoomInDestination = me->UB.len - uInsertionPos;
+ Check #1 makes sure me->data_len is less than me->size
+ Check #3 makes sure uInsertionPos is less than me->data_len
+ */
+
+
+/*
+ Public function -- see UsefulBuf.h
+ */
+UsefulBufC UsefulOutBuf_OutUBuf(UsefulOutBuf *me)
+{
+ if(me->err) {
+ return NULLUsefulBufC;
+ }
+
+ if(me->magic != USEFUL_OUT_BUF_MAGIC) {
+ me->err = 1;
+ return NULLUsefulBufC;
+ }
+
+ return (UsefulBufC){me->UB.ptr,me->data_len};
+}
+
+
+/*
+ Public function -- see UsefulBuf.h
+
+ Copy out the data accumulated in to the output buffer.
+ */
+UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *me, UsefulBuf pDest)
+{
+ const UsefulBufC Tmp = UsefulOutBuf_OutUBuf(me);
+ if(UsefulBuf_IsNULLC(Tmp)) {
+ return NULLUsefulBufC;
+ }
+ return UsefulBuf_Copy(pDest, Tmp);
+}
+
+
+
+
+/*
+ Public function -- see UsefulBuf.h
+
+ The core of UsefulInputBuf -- consume some bytes without going off the end of the buffer.
+
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+const void * UsefulInputBuf_GetBytes(UsefulInputBuf *me, size_t uAmount)
+{
+ // Already in error state. Do nothing.
+ if(me->err) {
+ return NULL;
+ }
+
+ if(!UsefulInputBuf_BytesAvailable(me, uAmount)) {
+ // The number of bytes asked for at current position are more than available
+ me->err = 1;
+ return NULL;
+ }
+
+ // This is going to succeed
+ const void * const result = ((uint8_t *)me->UB.ptr) + me->cursor;
+ me->cursor += uAmount; // this will not overflow because of check using UsefulInputBuf_BytesAvailable()
+ return result;
+}
+
diff --git a/lib/ext/qcbor/src/ieee754.c b/lib/ext/qcbor/src/ieee754.c
new file mode 100644
index 0000000..6fdfda8
--- /dev/null
+++ b/lib/ext/qcbor/src/ieee754.c
@@ -0,0 +1,497 @@
+/*==============================================================================
+ ieee754.c -- floating point conversion between half, double and single precision
+
+ Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created on 7/23/18
+ ==============================================================================*/
+
+#include "ieee754.h"
+#include <string.h> // For memcpy()
+
+
+/*
+ This code is written for clarity and verifiability, not for size, on the assumption
+ that the optimizer will do a good job. The LLVM optimizer, -Os, does seem to do the
+ job and the resulting object code is smaller from combining code for the many different
+ cases (normal, subnormal, infinity, zero...) for the conversions.
+
+ Dead stripping is also really helpful to get code size down when floating point
+ encoding is not needed.
+
+ This code works solely using shifts and masks and thus has no dependency on
+ any math libraries. It can even work if the CPU doesn't have any floating
+ point support, though that isn't the most useful thing to do.
+
+ The memcpy() dependency is only for CopyFloatToUint32() and friends which only
+ is needed to avoid type punning when converting the actual float bits to
+ an unsigned value so the bit shifts and masks can work.
+ */
+
+/*
+ The references used to write this code:
+
+ - IEEE 754-2008, particularly section 3.6 and 6.2.1
+
+ - https://en.wikipedia.org/wiki/IEEE_754 and subordinate pages
+
+ - https://stackoverflow.com/questions/19800415/why-does-ieee-754-reserve-so-many-nan-values
+ */
+
+
+// ----- Half Precsion -----------
+#define HALF_NUM_SIGNIFICAND_BITS (10)
+#define HALF_NUM_EXPONENT_BITS (5)
+#define HALF_NUM_SIGN_BITS (1)
+
+#define HALF_SIGNIFICAND_SHIFT (0)
+#define HALF_EXPONENT_SHIFT (HALF_NUM_SIGNIFICAND_BITS)
+#define HALF_SIGN_SHIFT (HALF_NUM_SIGNIFICAND_BITS + HALF_NUM_EXPONENT_BITS)
+
+#define HALF_SIGNIFICAND_MASK (0x3ff) // The lower 10 bits // 0x03ff
+#define HALF_EXPONENT_MASK (0x1f << HALF_EXPONENT_SHIFT) // 0x7c00 5 bits of exponent
+#define HALF_SIGN_MASK (0x01 << HALF_SIGN_SHIFT) // // 0x80001 bit of sign
+#define HALF_QUIET_NAN_BIT (0x01 << (HALF_NUM_SIGNIFICAND_BITS-1)) // 0x0200
+
+/* Biased Biased Unbiased Use
+ 0x00 0 -15 0 and subnormal
+ 0x01 1 -14 Smallest normal exponent
+ 0x1e 30 15 Largest normal exponent
+ 0x1F 31 16 NaN and Infinity */
+#define HALF_EXPONENT_BIAS (15)
+#define HALF_EXPONENT_MAX (HALF_EXPONENT_BIAS) // 15 Unbiased
+#define HALF_EXPONENT_MIN (-HALF_EXPONENT_BIAS+1) // -14 Unbiased
+#define HALF_EXPONENT_ZERO (-HALF_EXPONENT_BIAS) // -15 Unbiased
+#define HALF_EXPONENT_INF_OR_NAN (HALF_EXPONENT_BIAS+1) // 16 Unbiased
+
+
+// ------ Single Precision --------
+#define SINGLE_NUM_SIGNIFICAND_BITS (23)
+#define SINGLE_NUM_EXPONENT_BITS (8)
+#define SINGLE_NUM_SIGN_BITS (1)
+
+#define SINGLE_SIGNIFICAND_SHIFT (0)
+#define SINGLE_EXPONENT_SHIFT (SINGLE_NUM_SIGNIFICAND_BITS)
+#define SINGLE_SIGN_SHIFT (SINGLE_NUM_SIGNIFICAND_BITS + SINGLE_NUM_EXPONENT_BITS)
+
+#define SINGLE_SIGNIFICAND_MASK (0x7fffffUL) // The lower 23 bits
+#define SINGLE_EXPONENT_MASK (0xffUL << SINGLE_EXPONENT_SHIFT) // 8 bits of exponent
+#define SINGLE_SIGN_MASK (0x01UL << SINGLE_SIGN_SHIFT) // 1 bit of sign
+#define SINGLE_QUIET_NAN_BIT (0x01UL << (SINGLE_NUM_SIGNIFICAND_BITS-1))
+
+/* Biased Biased Unbiased Use
+ 0x0000 0 -127 0 and subnormal
+ 0x0001 1 -126 Smallest normal exponent
+ 0x7f 127 0 1
+ 0xfe 254 127 Largest normal exponent
+ 0xff 255 128 NaN and Infinity */
+#define SINGLE_EXPONENT_BIAS (127)
+#define SINGLE_EXPONENT_MAX (SINGLE_EXPONENT_BIAS) // 127 unbiased
+#define SINGLE_EXPONENT_MIN (-SINGLE_EXPONENT_BIAS+1) // -126 unbiased
+#define SINGLE_EXPONENT_ZERO (-SINGLE_EXPONENT_BIAS) // -127 unbiased
+#define SINGLE_EXPONENT_INF_OR_NAN (SINGLE_EXPONENT_BIAS+1) // 128 unbiased
+
+
+// --------- Double Precision ----------
+#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
+#define DOUBLE_NUM_EXPONENT_BITS (11)
+#define DOUBLE_NUM_SIGN_BITS (1)
+
+#define DOUBLE_SIGNIFICAND_SHIFT (0)
+#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
+#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
+
+#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
+#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
+#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
+#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
+
+
+/* Biased Biased Unbiased Use
+ 0x00000000 0 -1023 0 and subnormal
+ 0x00000001 1 -1022 Smallest normal exponent
+ 0x000007fe 2046 1023 Largest normal exponent
+ 0x000007ff 2047 1024 NaN and Infinity */
+#define DOUBLE_EXPONENT_BIAS (1023)
+#define DOUBLE_EXPONENT_MAX (DOUBLE_EXPONENT_BIAS) // unbiased
+#define DOUBLE_EXPONENT_MIN (-DOUBLE_EXPONENT_BIAS+1) // unbiased
+#define DOUBLE_EXPONENT_ZERO (-DOUBLE_EXPONENT_BIAS) // unbiased
+#define DOUBLE_EXPONENT_INF_OR_NAN (DOUBLE_EXPONENT_BIAS+1) // unbiased
+
+
+
+/*
+ Convenient functions to avoid type punning, compiler warnings and such
+ The optimizer reduces them to a simple assignment.
+ This is a crusty corner of C. It shouldn't be this hard.
+
+ These are also in UsefulBuf.h under a different name. They are copied
+ here to avoid a dependency on UsefulBuf.h. There is no
+ object code size impact because these always optimze down to a
+ simple assignment.
+ */
+static inline uint32_t CopyFloatToUint32(float f)
+{
+ uint32_t u32;
+ memcpy(&u32, &f, sizeof(uint32_t));
+ return u32;
+}
+
+static inline uint64_t CopyDoubleToUint64(double d)
+{
+ uint64_t u64;
+ memcpy(&u64, &d, sizeof(uint64_t));
+ return u64;
+}
+
+static inline float CopyUint32ToFloat(uint32_t u32)
+{
+ float f;
+ memcpy(&f, &u32, sizeof(uint32_t));
+ return f;
+}
+
+static inline double CopyUint64ToDouble(uint64_t u64)
+{
+ double d;
+ memcpy(&d, &u64, sizeof(uint64_t));
+ return d;
+}
+
+
+// Public function; see ieee754.h
+uint16_t IEEE754_FloatToHalf(float f)
+{
+ // Pull the three parts out of the single-precision float
+ const uint32_t uSingle = CopyFloatToUint32(f);
+ const int32_t nSingleUnbiasedExponent = ((uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT) - SINGLE_EXPONENT_BIAS;
+ const uint32_t uSingleSign = (uSingle & SINGLE_SIGN_MASK) >> SINGLE_SIGN_SHIFT;
+ const uint32_t uSingleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
+
+
+ // Now convert the three parts to half-precision.
+ uint16_t uHalfSign, uHalfSignificand, uHalfBiasedExponent;
+ if(nSingleUnbiasedExponent == SINGLE_EXPONENT_INF_OR_NAN) {
+ // +/- Infinity and NaNs -- single biased exponent is 0xff
+ uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
+ if(!uSingleSignificand) {
+ // Infinity
+ uHalfSignificand = 0;
+ } else {
+ // Copy the LBSs of the NaN payload that will fit from the single to the half
+ uHalfSignificand = uSingleSignificand & (HALF_SIGNIFICAND_MASK & ~HALF_QUIET_NAN_BIT);
+ if(uSingleSignificand & SINGLE_QUIET_NAN_BIT) {
+ // It's a qNaN; copy the qNaN bit
+ uHalfSignificand |= HALF_QUIET_NAN_BIT;
+ } else {
+ // It's a sNaN; make sure the significand is not zero so it stays a NaN
+ // This is needed because not all significand bits are copied from single
+ if(!uHalfSignificand) {
+ // Set the LSB. This is what wikipedia shows for sNAN.
+ uHalfSignificand |= 0x01;
+ }
+ }
+ }
+ } else if(nSingleUnbiasedExponent == SINGLE_EXPONENT_ZERO) {
+ // 0 or a subnormal number -- singled biased exponent is 0
+ uHalfBiasedExponent = 0;
+ uHalfSignificand = 0; // Any subnormal single will be too small to express as a half precision
+ } else if(nSingleUnbiasedExponent > HALF_EXPONENT_MAX) {
+ // Exponent is too large to express in half-precision; round up to infinity
+ uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
+ uHalfSignificand = 0;
+ } else if(nSingleUnbiasedExponent < HALF_EXPONENT_MIN) {
+ // Exponent is too small to express in half-precision normal; make it a half-precision subnormal
+ uHalfBiasedExponent = (uint16_t)(HALF_EXPONENT_ZERO + HALF_EXPONENT_BIAS);
+ // Difference between single normal exponent and the base exponent of a half subnormal
+ const uint32_t nExpDiff = -(nSingleUnbiasedExponent - HALF_EXPONENT_MIN);
+ // Also have to shift the significand by the difference in number of bits between a single and a half significand
+ const int32_t nSignificandBitsDiff = SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS;
+ // Add in the 1 that is implied in the significand of a normal number; it needs to be present in a subnormal
+ const uint32_t uSingleSignificandSubnormal = uSingleSignificand + (0x01L << SINGLE_NUM_SIGNIFICAND_BITS);
+ uHalfSignificand = uSingleSignificandSubnormal >> (nExpDiff + nSignificandBitsDiff);
+ } else {
+ // The normal case
+ uHalfBiasedExponent = nSingleUnbiasedExponent + HALF_EXPONENT_BIAS;
+ uHalfSignificand = uSingleSignificand >> (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ }
+ uHalfSign = uSingleSign;
+
+ // Put the 3 values in the right place for a half precision
+ const uint16_t uHalfPrecision = uHalfSignificand |
+ (uHalfBiasedExponent << HALF_EXPONENT_SHIFT) |
+ (uHalfSign << HALF_SIGN_SHIFT);
+ return uHalfPrecision;
+}
+
+
+// Public function; see ieee754.h
+uint16_t IEEE754_DoubleToHalf(double d)
+{
+ // Pull the three parts out of the double-precision float
+ const uint64_t uDouble = CopyDoubleToUint64(d);
+ const int64_t nDoubleUnbiasedExponent = ((uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT) - DOUBLE_EXPONENT_BIAS;
+ const uint64_t uDoubleSign = (uDouble & DOUBLE_SIGN_MASK) >> DOUBLE_SIGN_SHIFT;
+ const uint64_t uDoubleSignificand = uDouble & DOUBLE_SIGNIFICAND_MASK;
+
+
+ // Now convert the three parts to half-precision.
+ uint16_t uHalfSign, uHalfSignificand, uHalfBiasedExponent;
+ if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_INF_OR_NAN) {
+ // +/- Infinity and NaNs -- single biased exponent is 0xff
+ uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
+ if(!uDoubleSignificand) {
+ // Infinity
+ uHalfSignificand = 0;
+ } else {
+ // Copy the LBSs of the NaN payload that will fit from the double to the half
+ uHalfSignificand = uDoubleSignificand & (HALF_SIGNIFICAND_MASK & ~HALF_QUIET_NAN_BIT);
+ if(uDoubleSignificand & DOUBLE_QUIET_NAN_BIT) {
+ // It's a qNaN; copy the qNaN bit
+ uHalfSignificand |= HALF_QUIET_NAN_BIT;
+ } else {
+ // It's an sNaN; make sure the significand is not zero so it stays a NaN
+ // This is needed because not all significand bits are copied from single
+ if(!uHalfSignificand) {
+ // Set the LSB. This is what wikipedia shows for sNAN.
+ uHalfSignificand |= 0x01;
+ }
+ }
+ }
+ } else if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_ZERO) {
+ // 0 or a subnormal number -- double biased exponent is 0
+ uHalfBiasedExponent = 0;
+ uHalfSignificand = 0; // Any subnormal single will be too small to express as a half precision; TODO, is this really true?
+ } else if(nDoubleUnbiasedExponent > HALF_EXPONENT_MAX) {
+ // Exponent is too large to express in half-precision; round up to infinity; TODO, is this really true?
+ uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
+ uHalfSignificand = 0;
+ } else if(nDoubleUnbiasedExponent < HALF_EXPONENT_MIN) {
+ // Exponent is too small to express in half-precision; round down to zero
+ uHalfBiasedExponent = (uint16_t)(HALF_EXPONENT_ZERO + HALF_EXPONENT_BIAS);
+ // Difference between double normal exponent and the base exponent of a half subnormal
+ const uint64_t nExpDiff = -(nDoubleUnbiasedExponent - HALF_EXPONENT_MIN);
+ // Also have to shift the significand by the difference in number of bits between a double and a half significand
+ const int64_t nSignificandBitsDiff = DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS;
+ // Add in the 1 that is implied in the significand of a normal number; it needs to be present in a subnormal
+ const uint64_t uDoubleSignificandSubnormal = uDoubleSignificand + (0x01ULL << DOUBLE_NUM_SIGNIFICAND_BITS);
+ uHalfSignificand = uDoubleSignificandSubnormal >> (nExpDiff + nSignificandBitsDiff);
+ } else {
+ // The normal case
+ uHalfBiasedExponent = nDoubleUnbiasedExponent + HALF_EXPONENT_BIAS;
+ uHalfSignificand = uDoubleSignificand >> (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ }
+ uHalfSign = uDoubleSign;
+
+
+ // Put the 3 values in the right place for a half precision
+ const uint16_t uHalfPrecision = uHalfSignificand |
+ (uHalfBiasedExponent << HALF_EXPONENT_SHIFT) |
+ (uHalfSign << HALF_SIGN_SHIFT);
+ return uHalfPrecision;
+}
+
+
+// Public function; see ieee754.h
+float IEEE754_HalfToFloat(uint16_t uHalfPrecision)
+{
+ // Pull out the three parts of the half-precision float
+ const uint16_t uHalfSignificand = uHalfPrecision & HALF_SIGNIFICAND_MASK;
+ const int16_t nHalfUnBiasedExponent = ((uHalfPrecision & HALF_EXPONENT_MASK) >> HALF_EXPONENT_SHIFT) - HALF_EXPONENT_BIAS;
+ const uint16_t uHalfSign = (uHalfPrecision & HALF_SIGN_MASK) >> HALF_SIGN_SHIFT;
+
+
+ // Make the three parts of the single-precision number
+ uint32_t uSingleSignificand, uSingleSign, uSingleBiasedExponent;
+ if(nHalfUnBiasedExponent == HALF_EXPONENT_ZERO) {
+ // 0 or subnormal
+ if(uHalfSignificand) {
+ // Subnormal case
+ uSingleBiasedExponent = -HALF_EXPONENT_BIAS + SINGLE_EXPONENT_BIAS +1;
+ // A half-precision subnormal can always be converted to a normal single-precision float because the ranges line up
+ uSingleSignificand = uHalfSignificand;
+ // Shift bits from right of the decimal to left, reducing the exponent by 1 each time
+ do {
+ uSingleSignificand <<= 1;
+ uSingleBiasedExponent--;
+ } while ((uSingleSignificand & 0x400) == 0);
+ uSingleSignificand &= HALF_SIGNIFICAND_MASK;
+ uSingleSignificand <<= (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ } else {
+ // Just zero
+ uSingleBiasedExponent = SINGLE_EXPONENT_ZERO + SINGLE_EXPONENT_BIAS;
+ uSingleSignificand = 0;
+ }
+ } else if(nHalfUnBiasedExponent == HALF_EXPONENT_INF_OR_NAN) {
+ // NaN or Inifinity
+ uSingleBiasedExponent = SINGLE_EXPONENT_INF_OR_NAN + SINGLE_EXPONENT_BIAS;
+ if(uHalfSignificand) {
+ // NaN
+ // First preserve the NaN payload from half to single
+ uSingleSignificand = uHalfSignificand & ~HALF_QUIET_NAN_BIT;
+ if(uHalfSignificand & HALF_QUIET_NAN_BIT) {
+ // Next, set qNaN if needed since half qNaN bit is not copied above
+ uSingleSignificand |= SINGLE_QUIET_NAN_BIT;
+ }
+ } else {
+ // Infinity
+ uSingleSignificand = 0;
+ }
+ } else {
+ // Normal number
+ uSingleBiasedExponent = nHalfUnBiasedExponent + SINGLE_EXPONENT_BIAS;
+ uSingleSignificand = uHalfSignificand << (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ }
+ uSingleSign = uHalfSign;
+
+
+ // Shift the three parts of the single precision into place
+ const uint32_t uSinglePrecision = uSingleSignificand |
+ (uSingleBiasedExponent << SINGLE_EXPONENT_SHIFT) |
+ (uSingleSign << SINGLE_SIGN_SHIFT);
+
+ return CopyUint32ToFloat(uSinglePrecision);
+}
+
+
+// Public function; see ieee754.h
+double IEEE754_HalfToDouble(uint16_t uHalfPrecision)
+{
+ // Pull out the three parts of the half-precision float
+ const uint16_t uHalfSignificand = uHalfPrecision & HALF_SIGNIFICAND_MASK;
+ const int16_t nHalfUnBiasedExponent = ((uHalfPrecision & HALF_EXPONENT_MASK) >> HALF_EXPONENT_SHIFT) - HALF_EXPONENT_BIAS;
+ const uint16_t uHalfSign = (uHalfPrecision & HALF_SIGN_MASK) >> HALF_SIGN_SHIFT;
+
+
+ // Make the three parts of hte single-precision number
+ uint64_t uDoubleSignificand, uDoubleSign, uDoubleBiasedExponent;
+ if(nHalfUnBiasedExponent == HALF_EXPONENT_ZERO) {
+ // 0 or subnormal
+ uDoubleBiasedExponent = DOUBLE_EXPONENT_ZERO + DOUBLE_EXPONENT_BIAS;
+ if(uHalfSignificand) {
+ // Subnormal case
+ uDoubleBiasedExponent = -HALF_EXPONENT_BIAS + DOUBLE_EXPONENT_BIAS +1;
+ // A half-precision subnormal can always be converted to a normal double-precision float because the ranges line up
+ uDoubleSignificand = uHalfSignificand;
+ // Shift bits from right of the decimal to left, reducing the exponent by 1 each time
+ do {
+ uDoubleSignificand <<= 1;
+ uDoubleBiasedExponent--;
+ } while ((uDoubleSignificand & 0x400) == 0);
+ uDoubleSignificand &= HALF_SIGNIFICAND_MASK;
+ uDoubleSignificand <<= (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ } else {
+ // Just zero
+ uDoubleSignificand = 0;
+ }
+ } else if(nHalfUnBiasedExponent == HALF_EXPONENT_INF_OR_NAN) {
+ // NaN or Inifinity
+ uDoubleBiasedExponent = DOUBLE_EXPONENT_INF_OR_NAN + DOUBLE_EXPONENT_BIAS;
+ if(uHalfSignificand) {
+ // NaN
+ // First preserve the NaN payload from half to single
+ uDoubleSignificand = uHalfSignificand & ~HALF_QUIET_NAN_BIT;
+ if(uHalfSignificand & HALF_QUIET_NAN_BIT) {
+ // Next, set qNaN if needed since half qNaN bit is not copied above
+ uDoubleSignificand |= DOUBLE_QUIET_NAN_BIT;
+ }
+ } else {
+ // Infinity
+ uDoubleSignificand = 0;
+ }
+ } else {
+ // Normal number
+ uDoubleBiasedExponent = nHalfUnBiasedExponent + DOUBLE_EXPONENT_BIAS;
+ uDoubleSignificand = (uint64_t)uHalfSignificand << (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ }
+ uDoubleSign = uHalfSign;
+
+
+ // Shift the 3 parts into place as a double-precision
+ const uint64_t uDouble = uDoubleSignificand |
+ (uDoubleBiasedExponent << DOUBLE_EXPONENT_SHIFT) |
+ (uDoubleSign << DOUBLE_SIGN_SHIFT);
+ return CopyUint64ToDouble(uDouble);
+}
+
+
+// Public function; see ieee754.h
+IEEE754_union IEEE754_FloatToSmallest(float f)
+{
+ IEEE754_union result;
+
+ // Pull the neeed two parts out of the single-precision float
+ const uint32_t uSingle = CopyFloatToUint32(f);
+ const int32_t nSingleExponent = ((uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT) - SINGLE_EXPONENT_BIAS;
+ const uint32_t uSingleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
+
+ // Bit mask that is the significand bits that would be lost when converting
+ // from single-precision to half-precision
+ const uint64_t uDroppedSingleBits = SINGLE_SIGNIFICAND_MASK >> HALF_NUM_SIGNIFICAND_BITS;
+
+ // Optimizer will re organize so there is only one call to IEEE754_FloatToHalf()
+ if(uSingle == 0) {
+ // Value is 0.0000, not a a subnormal
+ result.uSize = IEEE754_UNION_IS_HALF;
+ result.uValue = IEEE754_FloatToHalf(f);
+ } else if(nSingleExponent == SINGLE_EXPONENT_INF_OR_NAN) {
+ // NaN, +/- infinity
+ result.uSize = IEEE754_UNION_IS_HALF;
+ result.uValue = IEEE754_FloatToHalf(f);
+ } else if((nSingleExponent >= HALF_EXPONENT_MIN) && nSingleExponent <= HALF_EXPONENT_MAX && (!(uSingleSignificand & uDroppedSingleBits))) {
+ // Normal number in exponent range and precision won't be lost
+ result.uSize = IEEE754_UNION_IS_HALF;
+ result.uValue = IEEE754_FloatToHalf(f);
+ } else {
+ // Subnormal, exponent out of range, or precision will be lost
+ result.uSize = IEEE754_UNION_IS_SINGLE;
+ result.uValue = uSingle;
+ }
+
+ return result;
+}
+
+// Public function; see ieee754.h
+IEEE754_union IEEE754_DoubleToSmallestInternal(double d, int bAllowHalfPrecision)
+{
+ IEEE754_union result;
+
+ // Pull the needed two parts out of the double-precision float
+ const uint64_t uDouble = CopyDoubleToUint64(d);
+ const int64_t nDoubleExponent = ((uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT) - DOUBLE_EXPONENT_BIAS;
+ const uint64_t uDoubleSignificand = uDouble & DOUBLE_SIGNIFICAND_MASK;
+
+ // Masks to check whether dropped significand bits are zero or not
+ const uint64_t uDroppedDoubleBits = DOUBLE_SIGNIFICAND_MASK >> HALF_NUM_SIGNIFICAND_BITS;
+ const uint64_t uDroppedSingleBits = DOUBLE_SIGNIFICAND_MASK >> SINGLE_NUM_SIGNIFICAND_BITS;
+
+ // The various cases
+ if(d == 0.0) { // Take care of positive and negative zero
+ // Value is 0.0000, not a a subnormal
+ result.uSize = IEEE754_UNION_IS_HALF;
+ result.uValue = IEEE754_DoubleToHalf(d);
+ } else if(nDoubleExponent == DOUBLE_EXPONENT_INF_OR_NAN) {
+ // NaN, +/- infinity
+ result.uSize = IEEE754_UNION_IS_HALF;
+ result.uValue = IEEE754_DoubleToHalf(d);
+ } else if(bAllowHalfPrecision && (nDoubleExponent >= HALF_EXPONENT_MIN) && nDoubleExponent <= HALF_EXPONENT_MAX && (!(uDoubleSignificand & uDroppedDoubleBits))) {
+ // Can convert to half without precision loss
+ result.uSize = IEEE754_UNION_IS_HALF;
+ result.uValue = IEEE754_DoubleToHalf(d);
+ } else if((nDoubleExponent >= SINGLE_EXPONENT_MIN) && nDoubleExponent <= SINGLE_EXPONENT_MAX && (!(uDoubleSignificand & uDroppedSingleBits))) {
+ // Can convert to single without precision loss
+ result.uSize = IEEE754_UNION_IS_SINGLE;
+ result.uValue = CopyFloatToUint32((float)d);
+ } else {
+ // Can't convert without precision loss
+ result.uSize = IEEE754_UNION_IS_DOUBLE;
+ result.uValue = uDouble;
+ }
+
+ return result;
+}
+
diff --git a/lib/ext/qcbor/src/ieee754.h b/lib/ext/qcbor/src/ieee754.h
new file mode 100644
index 0000000..2530f98
--- /dev/null
+++ b/lib/ext/qcbor/src/ieee754.h
@@ -0,0 +1,168 @@
+/*==============================================================================
+ ieee754.c -- floating point conversion between half, double and single precision
+
+ Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created on 7/23/18
+ ==============================================================================*/
+
+#ifndef ieee754_h
+#define ieee754_h
+
+#include <stdint.h>
+
+
+
+/*
+ General comments
+
+ This is a complete in that it handles all conversion cases
+ including +/- infinity, +/- zero, subnormal numbers, qNaN, sNaN
+ and NaN payloads.
+
+ This confirms to IEEE 754-2008, but note that this doesn't
+ specify conversions, just the encodings.
+
+ NaN payloads are preserved with alignment on the LSB. The
+ qNaN bit is handled differently and explicity copied. It
+ is always the MSB of the significand. The NaN payload MSBs
+ (except the qNaN bit) are truncated when going from
+ double or single to half.
+
+ TODO: what does the C cast do with NaN payloads from
+ double to single?
+
+
+
+ */
+
+/*
+ Most simply just explicilty encode the type you want, single or double.
+ This works easily everywhere since standard C supports both
+ these types and so does qcbor. This encoder also supports
+ half precision and there's a few ways to use it to encode
+ floating point numbers in less space.
+
+ Without losing precision, you can encode a single or double
+ such that the special values of 0, NaN and Infinity encode
+ as half-precision. This CBOR decodoer and most others
+ should handle this properly.
+
+ If you don't mind losing precision, then you can use half-precision.
+ One way to do this is to set up your environment to use
+ ___fp_16. Some compilers and CPUs support it even though it is not
+ standard C. What is nice about this is that your program
+ will use less memory and floating point operations like
+ multiplying, adding and such will be faster.
+
+ Another way to make use of half-precision is to represent
+ the values in your program as single or double, but encode
+ them in CBOR as half-precision. This cuts the size
+ of the encoded messages by 2 or 4, but doesn't reduce
+ memory needs or speed because you are still using
+ single or double in your code.
+
+
+ encode:
+ - float as float
+ - double as double
+ - half as half
+ - float as half_precision, for environments that don't support a half-precision type
+ - double as half_precision, for environments that don't support a half-precision type
+ - float with NaN, Infinity and 0 as half
+ - double with NaN, Infinity and 0 as half
+
+
+
+
+ */
+
+
+
+/*
+ Convert single precision float to half-precision float.
+ Precision and NaN payload bits will be lost. Too large
+ values will round up to infinity and too small to zero.
+ */
+uint16_t IEEE754_FloatToHalf(float f);
+
+
+/*
+ Convert half precision float to single precision float.
+ This is a loss-less conversion.
+ */
+float IEEE754_HalfToFloat(uint16_t uHalfPrecision);
+
+
+/*
+ Convert double precision float to half-precision float.
+ Precision and NaN payload bits will be lost. Too large
+ values will round up to infinity and too small to zero.
+ */
+uint16_t IEEE754_DoubleToHalf(double d);
+
+
+/*
+ Convert half precision float to double precision float.
+ This is a loss-less conversion.
+ */
+double IEEE754_HalfToDouble(uint16_t uHalfPrecision);
+
+
+
+// Both tags the value and gives the size
+#define IEEE754_UNION_IS_HALF 2
+#define IEEE754_UNION_IS_SINGLE 4
+#define IEEE754_UNION_IS_DOUBLE 8
+
+typedef struct {
+ uint8_t uSize; // One of IEEE754_IS_xxxx
+ uint64_t uValue;
+} IEEE754_union;
+
+
+/*
+ Converts double-precision to single-precision or half-precision if possible without
+ loss of precisions. If not, leaves it as a double. Only converts to single-precision
+ unless bAllowHalfPrecision is set.
+ */
+IEEE754_union IEEE754_DoubleToSmallestInternal(double d, int bAllowHalfPrecision);
+
+/*
+ Converts double-precision to single-precision if possible without
+ loss of precision. If not, leaves it as a double.
+ */
+static inline IEEE754_union IEEE754_DoubleToSmall(double d)
+{
+ return IEEE754_DoubleToSmallestInternal(d, 0);
+}
+
+
+/*
+ Converts double-precision to single-precision or half-precision if possible without
+ loss of precisions. If not, leaves it as a double.
+ */
+static inline IEEE754_union IEEE754_DoubleToSmallest(double d)
+{
+ return IEEE754_DoubleToSmallestInternal(d, 1);
+}
+
+/*
+ Converts single-precision to half-precision if possible without
+ loss of precision. If not leaves as single-precision.
+ */
+IEEE754_union IEEE754_FloatToSmallest(float f);
+
+
+#endif /* ieee754_h */
+
+
+
+
+
+
+
diff --git a/lib/ext/qcbor/src/qcbor_decode.c b/lib/ext/qcbor/src/qcbor_decode.c
new file mode 100644
index 0000000..2286038
--- /dev/null
+++ b/lib/ext/qcbor/src/qcbor_decode.c
@@ -0,0 +1,1319 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+/*===================================================================================
+ FILE: qcbor_decode.c
+
+ DESCRIPTION: This file contains the implementation of QCBOR.
+
+ EDIT HISTORY FOR FILE:
+
+ This section contains comments describing changes made to the module.
+ Notice that changes are listed in reverse chronological order.
+
+ when who what, where, why
+ -------- ---- ---------------------------------------------------
+ 01/10/19 llundblade Clever type and argument decoder is 250 bytes smaller
+ 11/9/18 llundblade Error codes are now enums.
+ 11/2/18 llundblade Simplify float decoding and align with preferred
+ float encoding
+ 10/31/18 llundblade Switch to one license that is almost BSD-3.
+ 10/28/18 llundblade Reworked tag decoding
+ 10/15/18 llundblade Indefinite length maps and arrays supported
+ 10/8/18 llundblade Indefinite length strings supported
+ 02/04/17 llundbla Work on CPUs that don's require pointer alignment
+ by making use of changes in UsefulBuf
+ 03/01/17 llundbla More data types; decoding improvements and fixes
+ 11/13/16 llundbla Integrate most TZ changes back into github version.
+ 09/30/16 gkanike Porting to TZ.
+ 03/15/16 llundbla Initial Version.
+
+ =====================================================================================*/
+
+#include "qcbor.h"
+#include "ieee754.h"
+
+
+/*
+ This casts away the const-ness of a pointer, usually so it can be
+ freed or realloced.
+ */
+#define UNCONST_POINTER(ptr) ((void *)(ptr))
+
+
+/*
+ Collection of functions to track the map/array nesting for decoding
+ */
+
+inline static int IsMapOrArray(uint8_t uDataType)
+{
+ return uDataType == QCBOR_TYPE_MAP || uDataType == QCBOR_TYPE_ARRAY;
+}
+
+inline static int DecodeNesting_IsNested(const QCBORDecodeNesting *pNesting)
+{
+ return pNesting->pCurrent != &(pNesting->pMapsAndArrays[0]);
+}
+
+inline static int DecodeNesting_IsIndefiniteLength(const QCBORDecodeNesting *pNesting)
+{
+ return pNesting->pCurrent->uCount == UINT16_MAX;
+}
+
+inline static uint8_t DecodeNesting_GetLevel(QCBORDecodeNesting *pNesting)
+{
+ return pNesting->pCurrent - &(pNesting->pMapsAndArrays[0]);
+}
+
+inline static int DecodeNesting_TypeIsMap(const QCBORDecodeNesting *pNesting)
+{
+ if(!DecodeNesting_IsNested(pNesting)) {
+ return 0;
+ }
+
+ return CBOR_MAJOR_TYPE_MAP == pNesting->pCurrent->uMajorType;
+}
+
+// Process a break. This will either ascend the nesting or error out
+inline static QCBORError DecodeNesting_BreakAscend(QCBORDecodeNesting *pNesting)
+{
+ // breaks must always occur when there is nesting
+ if(!DecodeNesting_IsNested(pNesting)) {
+ return QCBOR_ERR_BAD_BREAK;
+ }
+
+ // breaks can only occur when the map/array is indefinite length
+ if(!DecodeNesting_IsIndefiniteLength(pNesting)) {
+ return QCBOR_ERR_BAD_BREAK;
+ }
+
+ // if all OK, the break reduces the level of nesting
+ pNesting->pCurrent--;
+
+ return QCBOR_SUCCESS;
+}
+
+// Called on every single item except breaks including the opening of a map/array
+inline static void DecodeNesting_DecrementCount(QCBORDecodeNesting *pNesting)
+{
+ if(!DecodeNesting_IsNested(pNesting)) {
+ // at top level where there is no tracking
+ return;
+ }
+
+ if(DecodeNesting_IsIndefiniteLength(pNesting)) {
+ // There is no count for indefinite length arrays/maps
+ return;
+ }
+
+ // Decrement the count of items in this array/map
+ pNesting->pCurrent->uCount--;
+
+ // Pop up nesting levels if the counts at the levels are zero
+ while(DecodeNesting_IsNested(pNesting) && 0 == pNesting->pCurrent->uCount) {
+ pNesting->pCurrent--;
+ if(!DecodeNesting_IsIndefiniteLength(pNesting)) {
+ pNesting->pCurrent->uCount--;
+ }
+ }
+}
+
+// Called on every map/array
+inline static QCBORError DecodeNesting_Descend(QCBORDecodeNesting *pNesting, QCBORItem *pItem)
+{
+ QCBORError nReturn = QCBOR_SUCCESS;
+
+ if(pItem->val.uCount == 0) {
+ // Nothing to do for empty definite lenth arrays. They are just are
+ // effectively the same as an item that is not a map or array
+ goto Done;
+ // Empty indefinite length maps and arrays are handled elsewhere
+ }
+
+ // Error out if arrays is too long to handle
+ if(pItem->val.uCount != UINT16_MAX && pItem->val.uCount > QCBOR_MAX_ITEMS_IN_ARRAY) {
+ nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
+ goto Done;
+ }
+
+ // Error out if nesting is too deep
+ if(pNesting->pCurrent >= &(pNesting->pMapsAndArrays[QCBOR_MAX_ARRAY_NESTING])) {
+ nReturn = QCBOR_ERR_ARRAY_NESTING_TOO_DEEP;
+ goto Done;
+ }
+
+ // The actual descend
+ pNesting->pCurrent++;
+
+ // Record a few details for this nesting level
+ pNesting->pCurrent->uMajorType = pItem->uDataType;
+ pNesting->pCurrent->uCount = pItem->val.uCount;
+
+Done:
+ return nReturn;;
+}
+
+inline static void DecodeNesting_Init(QCBORDecodeNesting *pNesting)
+{
+ pNesting->pCurrent = &(pNesting->pMapsAndArrays[0]);
+}
+
+
+
+/*
+ This list of built-in tags. Only add tags here that are
+ clearly established and useful. Once a tag is added here
+ it can't be taken out as that would break backwards compatibility.
+ There are only 48 slots available forever.
+ */
+static const uint16_t spBuiltInTagMap[] = {
+ CBOR_TAG_DATE_STRING, // See TAG_MAPPER_FIRST_FOUR
+ CBOR_TAG_DATE_EPOCH, // See TAG_MAPPER_FIRST_FOUR
+ CBOR_TAG_POS_BIGNUM, // See TAG_MAPPER_FIRST_FOUR
+ CBOR_TAG_NEG_BIGNUM, // See TAG_MAPPER_FIRST_FOUR
+ CBOR_TAG_FRACTION,
+ CBOR_TAG_BIGFLOAT,
+ CBOR_TAG_COSE_ENCRYPTO,
+ CBOR_TAG_COSE_MAC0,
+ CBOR_TAG_COSE_SIGN1,
+ CBOR_TAG_ENC_AS_B64URL,
+ CBOR_TAG_ENC_AS_B64,
+ CBOR_TAG_ENC_AS_B16,
+ CBOR_TAG_CBOR,
+ CBOR_TAG_URI,
+ CBOR_TAG_B64URL,
+ CBOR_TAG_B64,
+ CBOR_TAG_REGEX,
+ CBOR_TAG_MIME,
+ CBOR_TAG_BIN_UUID,
+ CBOR_TAG_CWT,
+ CBOR_TAG_ENCRYPT,
+ CBOR_TAG_MAC,
+ CBOR_TAG_SIGN,
+ CBOR_TAG_GEO_COORD,
+ CBOR_TAG_CBOR_MAGIC
+};
+
+// This is used in a bit of cleverness in GetNext_TaggedItem() to
+// keep code size down and switch for the internal processing of
+// these types. This will break if the first four items in
+// spBuiltInTagMap don't have values 0,1,2,3. That is the
+// mapping is 0 to 0, 1 to 1, 2 to 2 and 3 to 3.
+#define QCBOR_TAGFLAG_DATE_STRING (0x01LL << CBOR_TAG_DATE_STRING)
+#define QCBOR_TAGFLAG_DATE_EPOCH (0x01LL << CBOR_TAG_DATE_EPOCH)
+#define QCBOR_TAGFLAG_POS_BIGNUM (0x01LL << CBOR_TAG_POS_BIGNUM)
+#define QCBOR_TAGFLAG_NEG_BIGNUM (0x01LL << CBOR_TAG_NEG_BIGNUM)
+
+#define TAG_MAPPER_FIRST_FOUR (QCBOR_TAGFLAG_DATE_STRING |\
+ QCBOR_TAGFLAG_DATE_EPOCH |\
+ QCBOR_TAGFLAG_POS_BIGNUM |\
+ QCBOR_TAGFLAG_NEG_BIGNUM)
+
+#define TAG_MAPPER_TOTAL_TAG_BITS 64 // Number of bits in a uint64_t
+#define TAG_MAPPER_CUSTOM_TAGS_BASE_INDEX (TAG_MAPPER_TOTAL_TAG_BITS - QCBOR_MAX_CUSTOM_TAGS) // 48
+#define TAG_MAPPER_MAX_SIZE_BUILT_IN_TAGS (TAG_MAPPER_TOTAL_TAG_BITS - QCBOR_MAX_CUSTOM_TAGS ) // 48
+
+static inline int TagMapper_LookupBuiltIn(uint64_t uTag)
+{
+ if(sizeof(spBuiltInTagMap)/sizeof(uint16_t) > TAG_MAPPER_MAX_SIZE_BUILT_IN_TAGS) {
+ // This is a cross-check to make sure the above array doesn't
+ // accidentally get made too big.
+ // In normal conditions the above test should optimize out
+ // as all the values are known at compile time.
+ return -1;
+ }
+
+ if(uTag > UINT16_MAX) {
+ // This tag map works only on 16-bit tags
+ return -1;
+ }
+
+ for(int nTagBitIndex = 0; nTagBitIndex < (int)(sizeof(spBuiltInTagMap)/sizeof(uint16_t)); nTagBitIndex++) {
+ if(spBuiltInTagMap[nTagBitIndex] == uTag) {
+ return nTagBitIndex;
+ }
+ }
+ return -1; // Indicates no match
+}
+
+static inline int TagMapper_LookupCallerConfigured(const QCBORTagListIn *pCallerConfiguredTagMap, uint64_t uTag)
+{
+ for(int nTagBitIndex = 0; nTagBitIndex < pCallerConfiguredTagMap->uNumTags; nTagBitIndex++) {
+ if(pCallerConfiguredTagMap->puTags[nTagBitIndex] == uTag) {
+ return nTagBitIndex + TAG_MAPPER_CUSTOM_TAGS_BASE_INDEX;
+ }
+ }
+
+ return -1; // Indicates no match
+}
+
+/*
+ Find the tag bit index for a given tag value, or error out
+
+ This and the above functions could probably be optimized and made
+ clearer and neater.
+ */
+static QCBORError TagMapper_Lookup(const QCBORTagListIn *pCallerConfiguredTagMap, uint64_t uTag, uint8_t *puTagBitIndex)
+{
+ int nTagBitIndex = TagMapper_LookupBuiltIn(uTag);
+ if(nTagBitIndex >= 0) {
+ // Cast is safe because TagMapper_LookupBuiltIn never returns > 47
+ *puTagBitIndex = (uint8_t)nTagBitIndex;
+ return QCBOR_SUCCESS;
+ }
+
+ if(pCallerConfiguredTagMap) {
+ if(pCallerConfiguredTagMap->uNumTags > QCBOR_MAX_CUSTOM_TAGS) {
+ return QCBOR_ERR_TOO_MANY_TAGS;
+ }
+ nTagBitIndex = TagMapper_LookupCallerConfigured(pCallerConfiguredTagMap, uTag);
+ if(nTagBitIndex >= 0) {
+ // Cast is safe because TagMapper_LookupBuiltIn never returns > 63
+
+ *puTagBitIndex = (uint8_t)nTagBitIndex;
+ return QCBOR_SUCCESS;
+ }
+ }
+
+ return QCBOR_ERR_BAD_OPT_TAG;
+}
+
+
+
+
+/*
+ Public function, see header file
+ */
+void QCBORDecode_Init(QCBORDecodeContext *me, UsefulBufC EncodedCBOR, QCBORDecodeMode nDecodeMode)
+{
+ memset(me, 0, sizeof(QCBORDecodeContext));
+ UsefulInputBuf_Init(&(me->InBuf), EncodedCBOR);
+ // Don't bother with error check on decode mode. If a bad value is passed it will just act as
+ // if the default normal mode of 0 was set.
+ me->uDecodeMode = nDecodeMode;
+ DecodeNesting_Init(&(me->nesting));
+}
+
+
+/*
+ Public function, see header file
+ */
+void QCBORDecode_SetUpAllocator(QCBORDecodeContext *pCtx, const QCBORStringAllocator *pAllocator, bool bAllocAll)
+{
+ pCtx->pStringAllocator = (void *)pAllocator;
+ pCtx->bStringAllocateAll = bAllocAll;
+}
+
+void QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext *me, const QCBORTagListIn *pTagList)
+{
+ me->pCallerConfiguredTagList = pTagList;
+}
+
+
+/*
+ This decodes the fundamental part of a CBOR data item, the type and number
+
+ This is the Counterpart to InsertEncodedTypeAndNumber().
+
+ This does the network->host byte order conversion. The conversion here
+ also results in the conversion for floats in addition to that for
+ lengths, tags and integer values.
+
+ This returns:
+ pnMajorType -- the major type for the item
+ puNumber -- the "number" which is used a the value for integers, tags and floats and length for strings and arrays
+ puAdditionalInfo -- Pass this along to know what kind of float or if length is indefinite
+
+ */
+inline static QCBORError DecodeTypeAndNumber(UsefulInputBuf *pUInBuf,
+ int *pnMajorType,
+ uint64_t *puArgument,
+ uint8_t *puAdditionalInfo)
+{
+ QCBORError nReturn;
+
+ // Get the initial byte that every CBOR data item has
+ const uint8_t uInitialByte = UsefulInputBuf_GetByte(pUInBuf);
+
+ // Break down the initial byte
+ const uint8_t uTmpMajorType = uInitialByte >> 5;
+ const uint8_t uAdditionalInfo = uInitialByte & 0x1f;
+
+ // Where the number or argument accumulates
+ uint64_t uArgument;
+
+ if(uAdditionalInfo >= LEN_IS_ONE_BYTE && uAdditionalInfo <= LEN_IS_EIGHT_BYTES) {
+ // Need to get 1,2,4 or 8 additional argument bytes
+ // Map LEN_IS_ONE_BYTE.. LEN_IS_EIGHT_BYTES to actual length
+ static const uint8_t aIterate[] = {1,2,4,8};
+
+ // Loop getting all the bytes in the argument
+ uArgument = 0;
+ for(int i = aIterate[uAdditionalInfo - LEN_IS_ONE_BYTE]; i; i--) {
+ // This shift and add gives the endian conversion
+ uArgument = (uArgument << 8) + UsefulInputBuf_GetByte(pUInBuf);
+ }
+ } else if(uAdditionalInfo >= ADDINFO_RESERVED1 && uAdditionalInfo <= ADDINFO_RESERVED3) {
+ // The reserved and thus-far unused additional info values
+ nReturn = QCBOR_ERR_UNSUPPORTED;
+ goto Done;
+ } else {
+ // Less than 24, additional info is argument or 31, an indefinite length
+ // No more bytes to get
+ uArgument = uAdditionalInfo;
+ }
+
+ if(UsefulInputBuf_GetError(pUInBuf)) {
+ nReturn = QCBOR_ERR_HIT_END;
+ goto Done;
+ }
+
+ // All successful if we got here.
+ nReturn = QCBOR_SUCCESS;
+ *pnMajorType = uTmpMajorType;
+ *puArgument = uArgument;
+ *puAdditionalInfo = uAdditionalInfo;
+
+Done:
+ return nReturn;
+}
+
+/*
+ CBOR doesn't explicitly specify two's compliment for integers but all CPUs
+ use it these days and the test vectors in the RFC are so. All integers in the CBOR
+ structure are positive and the major type indicates positive or negative.
+ CBOR can express positive integers up to 2^x - 1 where x is the number of bits
+ and negative integers down to 2^x. Note that negative numbers can be one
+ more away from zero than positive.
+ Stdint, as far as I can tell, uses two's compliment to represent
+ negative integers.
+
+ See http://www.unix.org/whitepapers/64bit.html for reasons int isn't
+ used here in any way including in the interface
+ */
+inline static QCBORError DecodeInteger(int nMajorType, uint64_t uNumber, QCBORItem *pDecodedItem)
+{
+ // Stack usage: int/ptr 1 -- 8
+ QCBORError nReturn = QCBOR_SUCCESS;
+
+ if(nMajorType == CBOR_MAJOR_TYPE_POSITIVE_INT) {
+ if (uNumber <= INT64_MAX) {
+ pDecodedItem->val.int64 = (int64_t)uNumber;
+ pDecodedItem->uDataType = QCBOR_TYPE_INT64;
+
+ } else {
+ pDecodedItem->val.uint64 = uNumber;
+ pDecodedItem->uDataType = QCBOR_TYPE_UINT64;
+
+ }
+ } else {
+ if(uNumber <= INT64_MAX) {
+ pDecodedItem->val.int64 = -uNumber-1;
+ pDecodedItem->uDataType = QCBOR_TYPE_INT64;
+
+ } else {
+ // C can't represent a negative integer in this range
+ // so it is an error. todo -- test this condition
+ nReturn = QCBOR_ERR_INT_OVERFLOW;
+ }
+ }
+
+ return nReturn;
+}
+
+// Make sure #define value line up as DecodeSimple counts on this.
+#if QCBOR_TYPE_FALSE != CBOR_SIMPLEV_FALSE
+#error QCBOR_TYPE_FALSE macro value wrong
+#endif
+
+#if QCBOR_TYPE_TRUE != CBOR_SIMPLEV_TRUE
+#error QCBOR_TYPE_TRUE macro value wrong
+#endif
+
+#if QCBOR_TYPE_NULL != CBOR_SIMPLEV_NULL
+#error QCBOR_TYPE_NULL macro value wrong
+#endif
+
+#if QCBOR_TYPE_UNDEF != CBOR_SIMPLEV_UNDEF
+#error QCBOR_TYPE_UNDEF macro value wrong
+#endif
+
+#if QCBOR_TYPE_BREAK != CBOR_SIMPLE_BREAK
+#error QCBOR_TYPE_BREAK macro value wrong
+#endif
+
+#if QCBOR_TYPE_DOUBLE != DOUBLE_PREC_FLOAT
+#error QCBOR_TYPE_DOUBLE macro value wrong
+#endif
+
+#if QCBOR_TYPE_FLOAT != SINGLE_PREC_FLOAT
+#error QCBOR_TYPE_FLOAT macro value wrong
+#endif
+
+/*
+ Decode true, false, floats, break...
+ */
+
+inline static QCBORError DecodeSimple(uint8_t uAdditionalInfo, uint64_t uNumber, QCBORItem *pDecodedItem)
+{
+ // Stack usage: 0
+ QCBORError nReturn = QCBOR_SUCCESS;
+
+ // uAdditionalInfo is 5 bits from the initial byte
+ // compile time checks above make sure uAdditionalInfo values line up with uDataType values
+ pDecodedItem->uDataType = uAdditionalInfo;
+
+ switch(uAdditionalInfo) {
+ case ADDINFO_RESERVED1: // 28
+ case ADDINFO_RESERVED2: // 29
+ case ADDINFO_RESERVED3: // 30
+ nReturn = QCBOR_ERR_UNSUPPORTED;
+ break;
+
+ case HALF_PREC_FLOAT:
+ pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uNumber);
+ pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+ break;
+ case SINGLE_PREC_FLOAT:
+ pDecodedItem->val.dfnum = (double)UsefulBufUtil_CopyUint32ToFloat((uint32_t)uNumber);
+ pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+ break;
+ case DOUBLE_PREC_FLOAT:
+ pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uNumber);
+ pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+ break;
+
+ case CBOR_SIMPLEV_FALSE: // 20
+ case CBOR_SIMPLEV_TRUE: // 21
+ case CBOR_SIMPLEV_NULL: // 22
+ case CBOR_SIMPLEV_UNDEF: // 23
+ case CBOR_SIMPLE_BREAK: // 31
+ break; // nothing to do
+
+ case CBOR_SIMPLEV_ONEBYTE: // 24
+ if(uNumber <= CBOR_SIMPLE_BREAK) {
+ // This takes out f8 00 ... f8 1f which should be encoded as e0 … f7
+ nReturn = QCBOR_ERR_INVALID_CBOR;
+ goto Done;
+ }
+ /* FALLTHROUGH */
+ // fall through intentionally
+
+ default: // 0-19
+ pDecodedItem->uDataType = QCBOR_TYPE_UKNOWN_SIMPLE;
+ // DecodeTypeAndNumber will make uNumber equal to uAdditionalInfo when uAdditionalInfo is < 24
+ // This cast is safe because the 2, 4 and 8 byte lengths of uNumber are in the double/float cases above
+ pDecodedItem->val.uSimple = (uint8_t)uNumber;
+ break;
+ }
+
+Done:
+ return nReturn;
+}
+
+
+
+/*
+ Decode text and byte strings. Call the string allocator if asked to.
+ */
+inline static QCBORError DecodeBytes(const QCBORStringAllocator *pAlloc, int nMajorType, uint64_t uStrLen, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
+{
+ // Stack usage: UsefulBuf 2, int/ptr 1 40
+ QCBORError nReturn = QCBOR_SUCCESS;
+
+ const UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(pUInBuf, uStrLen);
+ if(UsefulBuf_IsNULLC(Bytes)) {
+ // Failed to get the bytes for this string item
+ nReturn = QCBOR_ERR_HIT_END;
+ goto Done;
+ }
+
+ if(pAlloc) {
+ // We are asked to use string allocator to make a copy
+ UsefulBuf NewMem = pAlloc->fAllocate(pAlloc->pAllocaterContext, NULL, uStrLen);
+ if(UsefulBuf_IsNULL(NewMem)) {
+ nReturn = QCBOR_ERR_STRING_ALLOCATE;
+ goto Done;
+ }
+ pDecodedItem->val.string = UsefulBuf_Copy(NewMem, Bytes);
+ } else {
+ // Normal case with no string allocator
+ pDecodedItem->val.string = Bytes;
+ }
+ pDecodedItem->uDataType = (nMajorType == CBOR_MAJOR_TYPE_BYTE_STRING) ? QCBOR_TYPE_BYTE_STRING : QCBOR_TYPE_TEXT_STRING;
+
+Done:
+ return nReturn;
+}
+
+
+/*
+ Mostly just assign the right data type for the date string.
+ */
+inline static QCBORError DecodeDateString(QCBORItem *pDecodedItem)
+{
+ // Stack Use: UsefulBuf 1 16
+ if(pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return QCBOR_ERR_BAD_OPT_TAG;
+ }
+
+ const UsefulBufC Temp = pDecodedItem->val.string;
+ pDecodedItem->val.dateString = Temp;
+ pDecodedItem->uDataType = QCBOR_TYPE_DATE_STRING;
+ return QCBOR_SUCCESS;
+}
+
+
+/*
+ Mostly just assign the right data type for the bignum.
+ */
+inline static QCBORError DecodeBigNum(QCBORItem *pDecodedItem)
+{
+ // Stack Use: UsefulBuf 1 -- 16
+ if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return QCBOR_ERR_BAD_OPT_TAG;
+ }
+ const UsefulBufC Temp = pDecodedItem->val.string;
+ pDecodedItem->val.bigNum = Temp;
+ pDecodedItem->uDataType = pDecodedItem->uTagBits & QCBOR_TAGFLAG_POS_BIGNUM ? QCBOR_TYPE_POSBIGNUM : QCBOR_TYPE_NEGBIGNUM;
+ return QCBOR_SUCCESS;
+}
+
+
+/*
+ The epoch formatted date. Turns lots of different forms of encoding date into uniform one
+ */
+static int DecodeDateEpoch(QCBORItem *pDecodedItem)
+{
+ // Stack usage: 1
+ QCBORError nReturn = QCBOR_SUCCESS;
+
+ pDecodedItem->val.epochDate.fSecondsFraction = 0;
+
+ switch (pDecodedItem->uDataType) {
+
+ case QCBOR_TYPE_INT64:
+ pDecodedItem->val.epochDate.nSeconds = pDecodedItem->val.int64;
+ break;
+
+ case QCBOR_TYPE_UINT64:
+ if(pDecodedItem->val.uint64 > INT64_MAX) {
+ nReturn = QCBOR_ERR_DATE_OVERFLOW;
+ goto Done;
+ }
+ pDecodedItem->val.epochDate.nSeconds = pDecodedItem->val.uint64;
+ break;
+
+ case QCBOR_TYPE_DOUBLE:
+ {
+ const double d = pDecodedItem->val.dfnum;
+ if(d > INT64_MAX) {
+ nReturn = QCBOR_ERR_DATE_OVERFLOW;
+ goto Done;
+ }
+ pDecodedItem->val.epochDate.nSeconds = d; // Float to integer conversion happening here.
+ pDecodedItem->val.epochDate.fSecondsFraction = d - pDecodedItem->val.epochDate.nSeconds;
+ }
+ break;
+
+ default:
+ nReturn = QCBOR_ERR_BAD_OPT_TAG;
+ goto Done;
+ }
+ pDecodedItem->uDataType = QCBOR_TYPE_DATE_EPOCH;
+
+Done:
+ return nReturn;
+}
+
+
+
+
+// Make sure the constants align as this is assumed by the GetAnItem() implementation
+#if QCBOR_TYPE_ARRAY != CBOR_MAJOR_TYPE_ARRAY
+#error QCBOR_TYPE_ARRAY value not lined up with major type
+#endif
+#if QCBOR_TYPE_MAP != CBOR_MAJOR_TYPE_MAP
+#error QCBOR_TYPE_MAP value not lined up with major type
+#endif
+
+/*
+ This gets a single data item and decodes it including preceding optional tagging. This does not
+ deal with arrays and maps and nesting except to decode the data item introducing them. Arrays and
+ maps are handled at the next level up in GetNext().
+
+ Errors detected here include: an array that is too long to decode, hit end of buffer unexpectedly,
+ a few forms of invalid encoded CBOR
+ */
+static QCBORError GetNext_Item(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, const QCBORStringAllocator *pAlloc)
+{
+ // Stack usage: int/ptr 3 -- 24
+ QCBORError nReturn;
+
+ // Get the major type and the number. Number could be length of more bytes or the value depending on the major type
+ // nAdditionalInfo is an encoding of the length of the uNumber and is needed to decode floats and doubles
+ int uMajorType;
+ uint64_t uNumber;
+ uint8_t uAdditionalInfo;
+
+ nReturn = DecodeTypeAndNumber(pUInBuf, &uMajorType, &uNumber, &uAdditionalInfo);
+
+ // Error out here if we got into trouble on the type and number.
+ // The code after this will not work if the type and number is not good.
+ if(nReturn)
+ goto Done;
+
+ memset(pDecodedItem, 0, sizeof(QCBORItem));
+
+ // At this point the major type and the value are valid. We've got the type and the number that
+ // starts every CBOR data item.
+ switch (uMajorType) {
+ case CBOR_MAJOR_TYPE_POSITIVE_INT: // Major type 0
+ case CBOR_MAJOR_TYPE_NEGATIVE_INT: // Major type 1
+ nReturn = DecodeInteger(uMajorType, uNumber, pDecodedItem);
+ break;
+
+ case CBOR_MAJOR_TYPE_BYTE_STRING: // Major type 2
+ case CBOR_MAJOR_TYPE_TEXT_STRING: // Major type 3
+ if(uAdditionalInfo == LEN_IS_INDEFINITE) {
+ pDecodedItem->uDataType = (uMajorType == CBOR_MAJOR_TYPE_BYTE_STRING) ? QCBOR_TYPE_BYTE_STRING : QCBOR_TYPE_TEXT_STRING;
+ pDecodedItem->val.string = (UsefulBufC){NULL, SIZE_MAX};
+ } else {
+ nReturn = DecodeBytes(pAlloc, uMajorType, uNumber, pUInBuf, pDecodedItem);
+ }
+ break;
+
+ case CBOR_MAJOR_TYPE_ARRAY: // Major type 4
+ case CBOR_MAJOR_TYPE_MAP: // Major type 5
+ // Record the number of items in the array or map
+ if(uNumber > QCBOR_MAX_ITEMS_IN_ARRAY) {
+ nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
+ goto Done;
+ }
+ if(uAdditionalInfo == LEN_IS_INDEFINITE) {
+ pDecodedItem->val.uCount = UINT16_MAX; // Indicate indefinite length
+ } else {
+ pDecodedItem->val.uCount = (uint16_t)uNumber; // type conversion OK because of check above
+ }
+ pDecodedItem->uDataType = uMajorType; // C preproc #if above makes sure constants align
+ break;
+
+ case CBOR_MAJOR_TYPE_OPTIONAL: // Major type 6, optional prepended tags
+ pDecodedItem->val.uTagV = uNumber;
+ pDecodedItem->uDataType = QCBOR_TYPE_OPTTAG;
+ break;
+
+ case CBOR_MAJOR_TYPE_SIMPLE: // Major type 7, float, double, true, false, null...
+ nReturn = DecodeSimple(uAdditionalInfo, uNumber, pDecodedItem);
+ break;
+
+ default: // Should never happen because DecodeTypeAndNumber() should never return > 7
+ nReturn = QCBOR_ERR_UNSUPPORTED;
+ break;
+ }
+
+Done:
+ return nReturn;
+}
+
+
+
+/*
+ This layer deals with indefinite length strings. It pulls all the
+ individual chunk items together into one QCBORItem using the
+ string allocator.
+
+ Code Reviewers: THIS FUNCTION DOES A LITTLE POINTER MATH
+ */
+static inline QCBORError GetNext_FullItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+ // Stack usage; int/ptr 2 UsefulBuf 2 QCBORItem -- 96
+ QCBORError nReturn;
+ QCBORStringAllocator *pAlloc = (QCBORStringAllocator *)me->pStringAllocator;
+ UsefulBufC FullString = NULLUsefulBufC;
+
+ nReturn = GetNext_Item(&(me->InBuf), pDecodedItem, me->bStringAllocateAll ? pAlloc: NULL);
+ if(nReturn) {
+ goto Done;
+ }
+
+ // To reduce code size by removing support for indefinite length strings, the
+ // code in this function from here down can be eliminated. Run tests, except
+ // indefinite length string tests, to be sure all is OK if this is removed.
+
+ // Only do indefinite length processing on strings
+ if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING && pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+ goto Done; // no need to do any work here on non-string types
+ }
+
+ // Is this a string with an indefinite length?
+ if(pDecodedItem->val.string.len != SIZE_MAX) {
+ goto Done; // length is not indefinite, so no work to do here
+ }
+
+ // Can't do indefinite length strings without a string allocator
+ if(pAlloc == NULL) {
+ nReturn = QCBOR_ERR_NO_STRING_ALLOCATOR;
+ goto Done;
+ }
+
+ // There is an indefinite length string to work on...
+ // Track which type of string it is
+ const uint8_t uStringType = pDecodedItem->uDataType;
+
+ // Loop getting chunk of indefinite string
+ for(;;) {
+ // Get item for next chunk
+ QCBORItem StringChunkItem;
+ // NULL passed to never string alloc chunk of indefinite length strings
+ nReturn = GetNext_Item(&(me->InBuf), &StringChunkItem, NULL);
+ if(nReturn) {
+ break; // Error getting the next chunk
+ }
+
+ // See if it is a marker at end of indefinite length string
+ if(StringChunkItem.uDataType == QCBOR_TYPE_BREAK) {
+ // String is complete
+ pDecodedItem->val.string = FullString;
+ pDecodedItem->uDataAlloc = 1;
+ break;
+ }
+
+ // Match data type of chunk to type at beginning.
+ // Also catches error of other non-string types that don't belong.
+ if(StringChunkItem.uDataType != uStringType) {
+ nReturn = QCBOR_ERR_INDEFINITE_STRING_CHUNK;
+ break;
+ }
+
+ // Alloc new buffer or expand previously allocated buffer so it can fit
+ UsefulBuf NewMem = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext,
+ UNCONST_POINTER(FullString.ptr),
+ FullString.len + StringChunkItem.val.string.len);
+ if(UsefulBuf_IsNULL(NewMem)) {
+ // Allocation of memory for the string failed
+ nReturn = QCBOR_ERR_STRING_ALLOCATE;
+ break;
+ }
+
+ // Copy new string chunk at the end of string so far.
+ FullString = UsefulBuf_CopyOffset(NewMem, FullString.len, StringChunkItem.val.string);
+ }
+
+Done:
+ if(pAlloc && nReturn && !UsefulBuf_IsNULLC(FullString)) {
+ // Getting item failed, clean up the allocated memory
+ (pAlloc->fFree)(pAlloc->pAllocaterContext, UNCONST_POINTER(FullString.ptr));
+ }
+
+ return nReturn;
+}
+
+
+/*
+ Returns an error if there was something wrong with the optional item or it couldn't
+ be handled.
+ */
+static QCBORError GetNext_TaggedItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem, QCBORTagListOut *pTags)
+{
+ // Stack usage: int/ptr: 3 -- 24
+ QCBORError nReturn;
+ uint64_t uTagBits = 0;
+ if(pTags) {
+ pTags->uNumUsed = 0;
+ }
+
+ for(;;) {
+ nReturn = GetNext_FullItem(me, pDecodedItem);
+ if(nReturn) {
+ goto Done; // Error out of the loop
+ }
+
+ if(pDecodedItem->uDataType != QCBOR_TYPE_OPTTAG) {
+ // Successful exit from loop; maybe got some tags, maybe not
+ pDecodedItem->uTagBits = uTagBits;
+ break;
+ }
+
+ uint8_t uTagBitIndex;
+ // Tag was mapped, tag was not mapped, error with tag list
+ switch(TagMapper_Lookup(me->pCallerConfiguredTagList, pDecodedItem->val.uTagV, &uTagBitIndex)) {
+
+ case QCBOR_SUCCESS:
+ // Successfully mapped the tag
+ uTagBits |= 0x01ULL << uTagBitIndex;
+ break;
+
+ case QCBOR_ERR_BAD_OPT_TAG:
+ // Tag is not recognized. Do nothing
+ break;
+
+ default:
+ // Error Condition
+ goto Done;
+ }
+
+ if(pTags) {
+ // Caller wants all tags recorded in the provided buffer
+ if(pTags->uNumUsed >= pTags->uNumAllocated) {
+ nReturn = QCBOR_ERR_TOO_MANY_TAGS;
+ goto Done;
+ }
+ pTags->puTags[pTags->uNumUsed] = pDecodedItem->val.uTagV;
+ pTags->uNumUsed++;
+ }
+ }
+
+ switch(pDecodedItem->uTagBits & TAG_MAPPER_FIRST_FOUR) {
+ case 0:
+ // No tags at all or none we know about. Nothing to do.
+ // This is part of the pass-through path of this function
+ // that will mostly be taken when decoding any item.
+ break;
+
+ case QCBOR_TAGFLAG_DATE_STRING:
+ nReturn = DecodeDateString(pDecodedItem);
+ break;
+
+ case QCBOR_TAGFLAG_DATE_EPOCH:
+ nReturn = DecodeDateEpoch(pDecodedItem);
+ break;
+
+ case QCBOR_TAGFLAG_POS_BIGNUM:
+ case QCBOR_TAGFLAG_NEG_BIGNUM:
+ nReturn = DecodeBigNum(pDecodedItem);
+ break;
+
+ default:
+ // Encountering some mixed up CBOR like something that
+ // is tagged as both a string and integer date.
+ nReturn = QCBOR_ERR_BAD_OPT_TAG;
+ }
+
+Done:
+ return nReturn;
+}
+
+
+/*
+ This layer takes care of map entries. It combines the label and data items into one QCBORItem.
+ */
+static inline QCBORError GetNext_MapEntry(QCBORDecodeContext *me, QCBORItem *pDecodedItem, QCBORTagListOut *pTags)
+{
+ // Stack use: int/ptr 1, QCBORItem -- 56
+ QCBORError nReturn = GetNext_TaggedItem(me, pDecodedItem, pTags);
+ if(nReturn)
+ goto Done;
+
+ if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+ // Break can't be a map entry
+ goto Done;
+ }
+
+ if(me->uDecodeMode != QCBOR_DECODE_MODE_MAP_AS_ARRAY) {
+ // In a map and caller wants maps decoded, not treated as arrays
+
+ if(DecodeNesting_TypeIsMap(&(me->nesting))) {
+ // If in a map and the right decoding mode, get the label
+
+ // Get the next item which will be the real data; Item will be the label
+ QCBORItem LabelItem = *pDecodedItem;
+ nReturn = GetNext_TaggedItem(me, pDecodedItem, pTags);
+ if(nReturn)
+ goto Done;
+
+ pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
+
+ if(LabelItem.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ // strings are always good labels
+ pDecodedItem->label.string = LabelItem.val.string;
+ pDecodedItem->uLabelType = QCBOR_TYPE_TEXT_STRING;
+ } else if (QCBOR_DECODE_MODE_MAP_STRINGS_ONLY == me->uDecodeMode) {
+ // It's not a string and we only want strings, probably for easy translation to JSON
+ nReturn = QCBOR_ERR_MAP_LABEL_TYPE;
+ goto Done;
+ } else if(LabelItem.uDataType == QCBOR_TYPE_INT64) {
+ pDecodedItem->label.int64 = LabelItem.val.int64;
+ pDecodedItem->uLabelType = QCBOR_TYPE_INT64;
+ } else if(LabelItem.uDataType == QCBOR_TYPE_UINT64) {
+ pDecodedItem->label.uint64 = LabelItem.val.uint64;
+ pDecodedItem->uLabelType = QCBOR_TYPE_UINT64;
+ } else if(LabelItem.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ pDecodedItem->label.string = LabelItem.val.string;
+ pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
+ pDecodedItem->uLabelType = QCBOR_TYPE_BYTE_STRING;
+ } else {
+ // label is not an int or a string. It is an arrray
+ // or a float or such and this implementation doesn't handle that.
+ // Also, tags on labels are ignored.
+ nReturn = QCBOR_ERR_MAP_LABEL_TYPE;
+ goto Done;
+ }
+ }
+ } else {
+ if(pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
+ // Decoding a map as an array
+ pDecodedItem->uDataType = QCBOR_TYPE_MAP_AS_ARRAY;
+ pDecodedItem->val.uCount *= 2;
+ }
+ }
+
+Done:
+ return nReturn;
+}
+
+
+/*
+ Public function, see header qcbor.h file
+ */
+QCBORError QCBORDecode_GetNextWithTags(QCBORDecodeContext *me, QCBORItem *pDecodedItem, QCBORTagListOut *pTags)
+{
+ // Stack ptr/int: 2, QCBORItem : 64
+
+ // The public entry point for fetching and parsing the next QCBORItem.
+ // All the CBOR parsing work is here and in subordinate calls.
+ QCBORError nReturn;
+
+ nReturn = GetNext_MapEntry(me, pDecodedItem, pTags);
+ if(nReturn) {
+ goto Done;
+ }
+
+ // Break ending arrays/maps are always processed at the end of this function.
+ // They should never show up here.
+ if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+ nReturn = QCBOR_ERR_BAD_BREAK;
+ goto Done;
+ }
+
+ // Record the nesting level for this data item before processing any of
+ // decrementing and descending.
+ pDecodedItem->uNestingLevel = DecodeNesting_GetLevel(&(me->nesting));
+
+ // Process the item just received for descent or decrement, and
+ // ascent if decrements are enough to close out a definite length array/map
+ if(IsMapOrArray(pDecodedItem->uDataType)) {
+ // If the new item is array or map, the nesting level descends
+ nReturn = DecodeNesting_Descend(&(me->nesting), pDecodedItem);
+ // Maps and arrays do count in as items in the map/array that encloses
+ // them so a decrement needs to be done for them too, but that is done
+ // only when all the items in them have been processed, not when they
+ // are opened.
+ } else {
+ // Decrement the count of items in the enclosing map/array
+ // If the count in the enclosing map/array goes to zero, that
+ // triggers a decrement in the map/array above that and
+ // an ascend in nesting level.
+ DecodeNesting_DecrementCount(&(me->nesting));
+ }
+ if(nReturn) {
+ goto Done;
+ }
+
+ // For indefinite length maps/arrays, looking at any and
+ // all breaks that might terminate them. The equivalent
+ // for definite length maps/arrays happens in
+ // DecodeNesting_DecrementCount().
+ if(DecodeNesting_IsNested(&(me->nesting)) && DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
+ while(UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
+ // Peek forward one item to see if it is a break.
+ QCBORItem Peek;
+ size_t uPeek = UsefulInputBuf_Tell(&(me->InBuf));
+ nReturn = GetNext_Item(&(me->InBuf), &Peek, NULL);
+ if(nReturn) {
+ goto Done;
+ }
+ if(Peek.uDataType != QCBOR_TYPE_BREAK) {
+ // It is not a break, rewind so it can be processed normally.
+ UsefulInputBuf_Seek(&(me->InBuf), uPeek);
+ break;
+ }
+ // It is a break. Ascend one nesting level.
+ // The break is consumed.
+ nReturn = DecodeNesting_BreakAscend(&(me->nesting));
+ if(nReturn) {
+ // break occured outside of an indefinite length array/map
+ goto Done;
+ }
+ }
+ }
+
+ // Tell the caller what level is next. This tells them what maps/arrays
+ // were closed out and makes it possible for them to reconstruct
+ // the tree with just the information returned by GetNext
+ pDecodedItem->uNextNestLevel = DecodeNesting_GetLevel(&(me->nesting));
+
+Done:
+ return nReturn;
+}
+
+
+QCBORError QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+ return QCBORDecode_GetNextWithTags(me, pDecodedItem, NULL);
+}
+
+
+/*
+ Decoding items is done in 5 layered functions, one calling the
+ next one down. If a layer has no work to do for a particular item
+ it returns quickly.
+
+ - QCBORDecode_GetNext -- The top layer manages the beginnings and
+ ends of maps and arrays. It tracks descending into and ascending
+ out of maps/arrays. It processes all breaks that terminate
+ maps and arrays.
+
+ - GetNext_MapEntry -- This handles the combining of two
+ items, the label and the data, that make up a map entry.
+ It only does work on maps. It combines the label and data
+ items into one labeled item.
+
+ - GetNext_TaggedItem -- This handles the type 6 tagged items.
+ It accumulates all the tags and combines them with the following
+ non-tagged item. If the tagged item is something that is understood
+ like a date, the decoding of that item is invoked.
+
+ - GetNext_FullItem -- This assembles the sub items that make up
+ an indefinte length string into one string item. It uses the
+ string allocater to create contiguous space for the item. It
+ processes all breaks that are part of indefinite length strings.
+
+ - GetNext_Item -- This gets and decodes the most atomic
+ item in CBOR, the thing with an initial byte containing
+ the major type.
+
+ Roughly this takes 300 bytes of stack for vars. Need to
+ evaluate this more carefully and correctly.
+
+ */
+
+
+/*
+ Public function, see header qcbor.h file
+ */
+int QCBORDecode_IsTagged(QCBORDecodeContext *me, const QCBORItem *pItem, uint64_t uTag)
+{
+ const QCBORTagListIn *pCallerConfiguredTagMap = me->pCallerConfiguredTagList;
+
+ uint8_t uTagBitIndex;
+ // Do not care about errors in pCallerConfiguredTagMap here. They are
+ // caught during GetNext() before this is called.
+ if(TagMapper_Lookup(pCallerConfiguredTagMap, uTag, &uTagBitIndex)) {
+ return 0;
+ }
+
+ const uint64_t uTagBit = 0x01ULL << uTagBitIndex;
+ return (uTagBit & pItem->uTagBits) != 0;
+}
+
+
+/*
+ Public function, see header qcbor.h file
+ */
+QCBORError QCBORDecode_Finish(QCBORDecodeContext *me)
+{
+ int nReturn = QCBOR_SUCCESS;
+
+ // Error out if all the maps/arrays are not closed out
+ if(DecodeNesting_IsNested(&(me->nesting))) {
+ nReturn = QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN;
+ goto Done;
+ }
+
+ // Error out if not all the bytes are consumed
+ if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
+ nReturn = QCBOR_ERR_EXTRA_BYTES;
+ }
+
+Done:
+ // Call the destructor for the string allocator if there is one.
+ // Always called, even if there are errors; always have to clean up
+ if(me->pStringAllocator) {
+ QCBORStringAllocator *pAllocator = (QCBORStringAllocator *)me->pStringAllocator;
+ if(pAllocator->fDestructor) {
+ (pAllocator->fDestructor)(pAllocator->pAllocaterContext);
+ }
+ }
+
+ return nReturn;
+}
+
+
+
+/*
+
+Decoder errors handled in this file
+
+ - Hit end of input before it was expected while decoding type and number QCBOR_ERR_HIT_END
+
+ - negative integer that is too large for C QCBOR_ERR_INT_OVERFLOW
+
+ - Hit end of input while decoding a text or byte string QCBOR_ERR_HIT_END
+
+ - Encountered conflicting tags -- e.g., an item is tagged both a date string and an epoch date QCBOR_ERR_UNSUPPORTED
+
+ - Encontered an array or mapp that has too many items QCBOR_ERR_ARRAY_TOO_LONG
+
+ - Encountered array/map nesting that is too deep QCBOR_ERR_ARRAY_NESTING_TOO_DEEP
+
+ - An epoch date > INT64_MAX or < INT64_MIN was encountered QCBOR_ERR_DATE_OVERFLOW
+
+ - The type of a map label is not a string or int QCBOR_ERR_MAP_LABEL_TYPE
+
+ - Hit end with arrays or maps still open -- QCBOR_ERR_EXTRA_BYTES
+
+ */
+
+
+
+
+/*
+ This is a very primitive memory allocator. It does not track individual
+ allocations, only a high-water mark. A free or reallotcation must be of
+ the last chunk allocated.
+
+ All of this following code will get dead-stripped if QCBORDecode_SetMemPool()
+ is not called.
+ */
+
+typedef struct {
+ QCBORStringAllocator StringAllocator;
+ uint8_t *pStart; // First byte that can be allocated
+ uint8_t *pEnd; // One past the last byte that can be allocated
+ uint8_t *pFree; // Where the next free chunk is
+} MemPool;
+
+
+/*
+ Internal function for an allocation
+
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+static UsefulBuf MemPool_Alloc(void *ctx, void *pMem, size_t uNewSize)
+{
+ MemPool *me = (MemPool *)ctx;
+ void *pReturn = NULL;
+
+ if(pMem) {
+ // Realloc case
+ // This check will work even if uNewSize is a super-large value like UINT64_MAX
+ if((uNewSize <= (size_t)(me->pEnd - (uint8_t *)pMem)) && ((uint8_t *)pMem >= me->pStart)) {
+ me->pFree = (uint8_t *)pMem + uNewSize;
+ pReturn = pMem;
+ }
+ } else {
+ // New chunk case
+ // This check will work even if uNewSize is a super large value like UINT64_MAX
+ if(uNewSize <= (size_t)(me->pEnd - me->pFree)) {
+ pReturn = me->pFree;
+ me->pFree += uNewSize;
+ }
+ }
+
+ return (UsefulBuf){pReturn, uNewSize};
+}
+
+/*
+ Internal function to free memory
+ */
+static void MemPool_Free(void *ctx, void *pOldMem)
+{
+ MemPool *me = (MemPool *)ctx;
+ me->pFree = pOldMem;
+}
+
+/*
+ Public function, see header qcbor.h file
+ */
+QCBORError QCBORDecode_SetMemPool(QCBORDecodeContext *me, UsefulBuf Pool, bool bAllStrings)
+{
+ // The idea behind QCBOR_MIN_MEM_POOL_SIZE is
+ // that the caller knows exactly what size to
+ // allocate and that the tests can run conclusively
+ // no matter what size MemPool is
+ // even though it wastes some memory. MemPool
+ // will vary depending on pointer size of the
+ // the machine. QCBOR_MIN_MEM_POOL_SIZE is
+ // set for pointers up to 64-bits. This
+ // wastes about 50 bytes on a 32-bit machine.
+ // This check makes sure things don't go
+ // horribly wrong. It should optimize out
+ // when there is no problem as the sizes are
+ // known at compile time.
+ if(sizeof(MemPool) > QCBOR_DECODE_MIN_MEM_POOL_SIZE) {
+ return QCBOR_ERR_MEM_POOL_INTERNAL;
+ }
+
+ // The first bytes of the Pool passed in are used
+ // as the context (vtable of sorts) for the memory pool
+ // allocator.
+ if(Pool.len < QCBOR_DECODE_MIN_MEM_POOL_SIZE) {
+ return QCBOR_ERR_BUFFER_TOO_SMALL;
+ }
+ MemPool *pMP = (MemPool *)Pool.ptr;
+
+ // Fill in the "vtable"
+ pMP->StringAllocator.fAllocate = MemPool_Alloc;
+ pMP->StringAllocator.fFree = MemPool_Free;
+ pMP->StringAllocator.fDestructor = NULL;
+
+ // Set up the pointers to the memory to be allocated
+ pMP->pStart = (uint8_t *)Pool.ptr + QCBOR_DECODE_MIN_MEM_POOL_SIZE;
+ pMP->pFree = pMP->pStart;
+ pMP->pEnd = (uint8_t *)Pool.ptr + Pool.len;
+
+ // More book keeping of context
+ pMP->StringAllocator.pAllocaterContext = pMP;
+ me->pStringAllocator = pMP;
+
+ // The flag indicating when to use the allocator
+ me->bStringAllocateAll = bAllStrings;
+
+ return QCBOR_SUCCESS;
+}
+
+
+/*
+ Extra little hook to make MemPool testing work right
+ without adding any code size or overhead to non-test
+ uses. This will get dead-stripped for non-test use.
+
+ This is not a public function.
+ */
+size_t MemPoolTestHook_GetPoolSize(void *ctx)
+{
+ MemPool *me = (MemPool *)ctx;
+
+ return me->pEnd - me->pStart;
+}
+
diff --git a/lib/ext/qcbor/src/qcbor_encode.c b/lib/ext/qcbor/src/qcbor_encode.c
new file mode 100644
index 0000000..460dd85
--- /dev/null
+++ b/lib/ext/qcbor/src/qcbor_encode.c
@@ -0,0 +1,650 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+/*===================================================================================
+ FILE: qcbor_encode.c
+
+ DESCRIPTION: This file contains the implementation of QCBOR.
+
+ EDIT HISTORY FOR FILE:
+
+ This section contains comments describing changes made to the module.
+ Notice that changes are listed in reverse chronological order.
+
+ when who what, where, why
+ -------- ---- ---------------------------------------------------
+ 12/30/18 llundblade Small efficient clever encode of type & argument.
+ 11/29/18 llundblade Rework to simpler handling of tags and labels.
+ 11/9/18 llundblade Error codes are now enums.
+ 11/1/18 llundblade Floating support.
+ 10/31/18 llundblade Switch to one license that is almost BSD-3.
+ 09/28/18 llundblade Added bstr wrapping feature for COSE implementation.
+ 02/05/18 llundbla Works on CPUs which require integer alignment.
+ Requires new version of UsefulBuf.
+ 07/05/17 llundbla Add bstr wrapping of maps/arrays for COSE
+ 03/01/17 llundbla More data types
+ 11/13/16 llundbla Integrate most TZ changes back into github version.
+ 09/30/16 gkanike Porting to TZ.
+ 03/15/16 llundbla Initial Version.
+
+ =====================================================================================*/
+
+#include "qcbor.h"
+#include "ieee754.h"
+
+
+/*...... This is a ruler that is 80 characters long...........................*/
+
+
+/*
+ CBOR's two nesting types, arrays and maps, are tracked here. There is a
+ limit of QCBOR_MAX_ARRAY_NESTING to the number of arrays and maps
+ that can be nested in one encoding so the encoding context stays
+ small enough to fit on the stack.
+
+ When an array / map is opened, pCurrentNesting points to the element
+ in pArrays that records the type, start position and accumluates a
+ count of the number of items added. When closed the start position is
+ used to go back and fill in the type and number of items in the array
+ / map.
+
+ Encoded output be just items like ints and strings that are
+ not part of any array / map. That is, the first thing encoded
+ does not have to be an array or a map.
+ */
+inline static void Nesting_Init(QCBORTrackNesting *pNesting)
+{
+ // assumes pNesting has been zeroed
+ pNesting->pCurrentNesting = &pNesting->pArrays[0];
+ // Implied CBOR array at the top nesting level. This is never returned,
+ // but makes the item count work correctly.
+ pNesting->pCurrentNesting->uMajorType = CBOR_MAJOR_TYPE_ARRAY;
+}
+
+inline static QCBORError Nesting_Increase(QCBORTrackNesting *pNesting,
+ uint8_t uMajorType,
+ uint32_t uPos)
+{
+ QCBORError nReturn = QCBOR_SUCCESS;
+
+ if(pNesting->pCurrentNesting == &pNesting->pArrays[QCBOR_MAX_ARRAY_NESTING]) {
+ // trying to open one too many
+ nReturn = QCBOR_ERR_ARRAY_NESTING_TOO_DEEP;
+ } else {
+ pNesting->pCurrentNesting++;
+ pNesting->pCurrentNesting->uCount = 0;
+ pNesting->pCurrentNesting->uStart = uPos;
+ pNesting->pCurrentNesting->uMajorType = uMajorType;
+ }
+ return nReturn;
+}
+
+inline static void Nesting_Decrease(QCBORTrackNesting *pNesting)
+{
+ pNesting->pCurrentNesting--;
+}
+
+inline static QCBORError Nesting_Increment(QCBORTrackNesting *pNesting)
+{
+ if(1 >= QCBOR_MAX_ITEMS_IN_ARRAY - pNesting->pCurrentNesting->uCount) {
+ return QCBOR_ERR_ARRAY_TOO_LONG;
+ }
+
+ pNesting->pCurrentNesting->uCount += 1;
+
+ return QCBOR_SUCCESS;
+}
+
+inline static uint16_t Nesting_GetCount(QCBORTrackNesting *pNesting)
+{
+ // The nesting count recorded is always the actual number of individiual
+ // data items in the array or map. For arrays CBOR uses the actual item
+ // count. For maps, CBOR uses the number of pairs. This function returns
+ // the number needed for the CBOR encoding, so it divides the number of
+ // items by two for maps to get the number of pairs. This implementation
+ // takes advantage of the map major type being one larger the array major
+ // type, hence uDivisor is either 1 or 2.
+ const uint16_t uDivisor = pNesting->pCurrentNesting->uMajorType - CBOR_MAJOR_TYPE_ARRAY+1;
+
+ return pNesting->pCurrentNesting->uCount / uDivisor;
+}
+
+inline static uint32_t Nesting_GetStartPos(QCBORTrackNesting *pNesting)
+{
+ return pNesting->pCurrentNesting->uStart;
+}
+
+inline static uint8_t Nesting_GetMajorType(QCBORTrackNesting *pNesting)
+{
+ return pNesting->pCurrentNesting->uMajorType;
+}
+
+inline static int Nesting_IsInNest(QCBORTrackNesting *pNesting)
+{
+ return pNesting->pCurrentNesting == &pNesting->pArrays[0] ? 0 : 1;
+}
+
+
+
+
+/*
+ Error tracking plan -- Errors are tracked internally and not returned
+ until Finish is called. The CBOR errors are in me->uError.
+ UsefulOutBuf also tracks whether the buffer is full or not in its
+ context. Once either of these errors is set they are never
+ cleared. Only QCBOREncode_Init() resets them. Or said another way, they must
+ never be cleared or we'll tell the caller all is good when it is not.
+
+ Only one error code is reported by QCBOREncode_Finish() even if there are
+ multiple errors. The last one set wins. The caller might have to fix
+ one error to reveal the next one they have to fix. This is OK.
+
+ The buffer full error tracked by UsefulBuf is only pulled out of
+ UsefulBuf in Finish() so it is the one that usually wins. UsefulBuf
+ will never go off the end of the buffer even if it is called again
+ and again when full.
+
+ It is really tempting to not check for overflow on the count in the
+ number of items in an array. It would save a lot of code, it is
+ extremely unlikely that any one will every put 65,000 items in an
+ array, and the only bad thing that would happen is the CBOR would be
+ bogus.
+
+ Since this does not parse any input, you could in theory remove all
+ error checks in this code if you knew the caller called it
+ correctly. Maybe someday CDDL or some such language will be able to
+ generate the code to call this and the calling code would always be
+ correct. This could also automatically size some of the data
+ structures like array/map nesting resulting in some stack memory
+ savings.
+
+ Errors returned here fall into two categories:
+
+ Sizes
+ QCBOR_ERR_BUFFER_TOO_LARGE -- Encoded output exceeded UINT32_MAX
+ QCBOR_ERR_BUFFER_TOO_SMALL -- output buffer too small
+
+ QCBOR_ERR_ARRAY_NESTING_TOO_DEEP -- Array/map nesting > QCBOR_MAX_ARRAY_NESTING1
+ QCBOR_ERR_ARRAY_TOO_LONG -- Too many things added to an array/map
+
+ Nesting constructed incorrectly
+ QCBOR_ERR_TOO_MANY_CLOSES -- more close calls than opens
+ QCBOR_ERR_CLOSE_MISMATCH -- Type of close does not match open
+ QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN -- Finish called without enough closes
+ */
+
+
+
+
+/*
+ Public function for initialization. See header qcbor.h
+ */
+void QCBOREncode_Init(QCBOREncodeContext *me, UsefulBuf Storage)
+{
+ memset(me, 0, sizeof(QCBOREncodeContext));
+ UsefulOutBuf_Init(&(me->OutBuf), Storage);
+ Nesting_Init(&(me->nesting));
+}
+
+
+
+
+/*
+ All CBOR data items have a type and an "argument". The argument is
+ either the value of the item for integer types, the length of the
+ content for string, byte, array and map types, a tag for major type
+ 6, and has several uses for major type 7.
+
+ This function encodes the type and the argument. There are several
+ encodings for the argument depending on how large it is and how it is
+ used.
+
+ Every encoding of the type and argument has at least one byte, the
+ "initial byte".
+
+ The top three bits of the initial byte are the major type for the
+ CBOR data item. The eight major types defined by the standard are
+ defined as CBOR_MAJOR_TYPE_xxxx in qcbor.h.
+
+ The remaining five bits, known as "additional information", and
+ possibly more bytes encode the argument. If the argument is less than
+ 24, then it is encoded entirely in the five bits. This is neat
+ because it allows you to encode an entire CBOR data item in 1 byte
+ for many values and types (integers 0-23, true, false, and tags).
+
+ If the argument is larger than 24, then it is encoded in 1,2,4 or 8
+ additional bytes, with the number of these bytes indicated by the
+ values of the 5 bits 24, 25, 25 and 27.
+
+ It is possible to encode a particular argument in many ways with this
+ representation. This implementation always uses the smallest
+ possible representation. This conforms with CBOR preferred encoding.
+
+ This function inserts them into the output buffer at the specified
+ position. AppendEncodedTypeAndNumber() appends to the end.
+
+ This function takes care of converting to network byte order.
+
+ This function is also used to insert floats and doubles. Before this
+ function is called the float or double must be copied into a
+ uint64_t. That is how they are passed in. They are then converted to
+ network byte order correctly. The uMinLen param makes sure that even
+
+ if all the digits of a half, float or double are 0 it is still
+ correctly encoded in 2, 4 or 8 bytes.
+ */
+
+static void InsertEncodedTypeAndNumber(QCBOREncodeContext *me,
+ uint8_t uMajorType,
+ int nMinLen,
+ uint64_t uNumber,
+ size_t uPos)
+{
+ /*
+ This code does endian conversion without hton or knowing the
+ endianness of the machine using masks and shifts. This avoids the
+ dependency on hton and the mess of figuring out how to find the
+ machine's endianness.
+
+ This is a good efficient implementation on little-endian machines.
+ A faster and small implementation is possible on big-endian
+ machines because CBOR/network byte order is big endian. However
+ big endian machines are uncommon.
+
+ On x86, it is about 200 bytes instead of 500 bytes for the more
+ formal unoptimized code.
+
+ This also does the CBOR preferred shortest encoding for integers
+ and is called to do endian conversion for floats.
+
+ It works backwards from the LSB to the MSB as needed.
+
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+ // Holds up to 9 bytes of type and argument
+ // plus one extra so pointer always points to
+ // valid bytes.
+ uint8_t bytes[sizeof(uint64_t)+2];
+ // Point to the last bytes and work backwards
+ uint8_t *pByte = &bytes[sizeof(bytes)-1];
+ // This is the 5 bits in the initial byte that is not the major type
+ uint8_t uAdditionalInfo;
+
+ if(uNumber < CBOR_TWENTY_FOUR && nMinLen == 0) {
+ // Simple case where argument is < 24
+ uAdditionalInfo = uNumber;
+ } else {
+ /*
+ Encode argument in 1,2,4 or 8 bytes. Outer loop
+ runs once for 1 byte and 4 times for 8 bytes.
+ Inner loop runs 1, 2 or 4 times depending on
+ outer loop counter. This works backwards taking
+ 8 bits off the argument being encoded at a time
+ until all bits from uNumber have been encoded
+ and the minimum encoding size is reached.
+ Minimum encoding size is for floating point
+ numbers with zero bytes.
+ */
+ static const uint8_t aIterate[] = {1,1,2,4};
+ uint8_t i;
+ for(i = 0; uNumber || nMinLen > 0; i++) {
+ const uint8_t uIterations = aIterate[i];
+ for(int j = 0; j < uIterations; j++) {
+ *--pByte = uNumber & 0xff;
+ uNumber = uNumber >> 8;
+ }
+ nMinLen -= uIterations;
+ }
+ // Additional info is the encoding of the
+ // number of additional bytes to encode
+ // argument.
+ uAdditionalInfo = LEN_IS_ONE_BYTE-1 + i;
+ }
+ *--pByte = (uMajorType << 5) + uAdditionalInfo;
+
+ UsefulOutBuf_InsertData(&(me->OutBuf), pByte, &bytes[sizeof(bytes)-1] - pByte, uPos);
+}
+
+
+/*
+ Append the type and number info to the end of the buffer.
+
+ See InsertEncodedTypeAndNumber() function above for details
+*/
+inline static void AppendEncodedTypeAndNumber(QCBOREncodeContext *me,
+ uint8_t uMajorType,
+ uint64_t uNumber)
+{
+ // An append is an insert at the end.
+ InsertEncodedTypeAndNumber(me,
+ uMajorType,
+ 0,
+ uNumber,
+ UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
+}
+
+
+
+
+/*
+ Public functions for closing arrays and maps. See header qcbor.h
+ */
+void QCBOREncode_AddUInt64(QCBOREncodeContext *me, uint64_t uValue)
+{
+ if(me->uError == QCBOR_SUCCESS) {
+ AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_POSITIVE_INT, uValue);
+ me->uError = Nesting_Increment(&(me->nesting));
+ }
+}
+
+
+/*
+ Public functions for closing arrays and maps. See header qcbor.h
+ */
+void QCBOREncode_AddInt64(QCBOREncodeContext *me, int64_t nNum)
+{
+ if(me->uError == QCBOR_SUCCESS) {
+ uint8_t uMajorType;
+ uint64_t uValue;
+
+ if(nNum < 0) {
+ // In CBOR -1 encodes as 0x00 with major type negative int.
+ uValue = (uint64_t)(-nNum - 1);
+ uMajorType = CBOR_MAJOR_TYPE_NEGATIVE_INT;
+ } else {
+ uValue = (uint64_t)nNum;
+ uMajorType = CBOR_MAJOR_TYPE_POSITIVE_INT;
+ }
+
+ AppendEncodedTypeAndNumber(me, uMajorType, uValue);
+ me->uError = Nesting_Increment(&(me->nesting));
+ }
+}
+
+
+/*
+ Semi-private function. It is exposed to user of the interface,
+ but they will usually call one of the inline wrappers rather than this.
+
+ See header qcbor.h
+
+ Does the work of adding some bytes to the CBOR output. Works for a
+ byte and text strings, which are the same in in CBOR though they have
+ different major types. This is also used to insert raw
+ pre-encoded CBOR.
+ */
+void QCBOREncode_AddBuffer(QCBOREncodeContext *me, uint8_t uMajorType, UsefulBufC Bytes)
+{
+ if(me->uError == QCBOR_SUCCESS) {
+ // If it is not Raw CBOR, add the type and the length
+ if(uMajorType != CBOR_MAJOR_NONE_TYPE_RAW) {
+ AppendEncodedTypeAndNumber(me, uMajorType, Bytes.len);
+ }
+
+ // Actually add the bytes
+ UsefulOutBuf_AppendUsefulBuf(&(me->OutBuf), Bytes);
+
+ // Update the array counting if there is any nesting at all
+ me->uError = Nesting_Increment(&(me->nesting));
+ }
+}
+
+
+/*
+ Public functions for closing arrays and maps. See header qcbor.h
+ */
+void QCBOREncode_AddTag(QCBOREncodeContext *me, uint64_t uTag)
+{
+ AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_OPTIONAL, uTag);
+}
+
+
+/*
+ Semi-private function. It is exposed to user of the interface,
+ but they will usually call one of the inline wrappers rather than this.
+
+ See header qcbor.h
+ */
+void QCBOREncode_AddType7(QCBOREncodeContext *me, size_t uSize, uint64_t uNum)
+{
+ if(me->uError == QCBOR_SUCCESS) {
+ // This function call takes care of endian swapping for the float / double
+ InsertEncodedTypeAndNumber(me,
+ // The major type for floats and doubles
+ CBOR_MAJOR_TYPE_SIMPLE,
+ // size makes sure floats with zeros encode correctly
+ (int)uSize,
+ // Bytes of the floating point number as a uint
+ uNum,
+ // end position because this is append
+ UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
+
+ me->uError = Nesting_Increment(&(me->nesting));
+ }
+}
+
+
+/*
+ Public functions for closing arrays and maps. See header qcbor.h
+ */
+void QCBOREncode_AddDouble(QCBOREncodeContext *me, double dNum)
+{
+ const IEEE754_union uNum = IEEE754_DoubleToSmallest(dNum);
+
+ QCBOREncode_AddType7(me, uNum.uSize, uNum.uValue);
+}
+
+
+/*
+ Semi-public function. It is exposed to user of the interface,
+ but they will usually call one of the inline wrappers rather than this.
+
+ See header qcbor.h
+*/
+void QCBOREncode_OpenMapOrArray(QCBOREncodeContext *me, uint8_t uMajorType)
+{
+ // Add one item to the nesting level we are in for the new map or array
+ me->uError = Nesting_Increment(&(me->nesting));
+ if(me->uError == QCBOR_SUCCESS) {
+ // The offset where the length of an array or map will get written
+ // is stored in a uint32_t, not a size_t to keep stack usage smaller. This
+ // checks to be sure there is no wrap around when recording the offset.
+ // Note that on 64-bit machines CBOR larger than 4GB can be encoded as long as no
+ // array / map offsets occur past the 4GB mark, but the public interface
+ // says that the maximum is 4GB to keep the discussion simpler.
+ size_t uEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
+
+ // QCBOR_MAX_ARRAY_OFFSET is slightly less than UINT32_MAX so this
+ // code can run on a 32-bit machine and tests can pass on a 32-bit
+ // machine. If it was exactly UINT32_MAX, then this code would
+ // not compile or run on a 32-bit machine and an #ifdef or some
+ // machine size detection would be needed reducing portability.
+ if(uEndPosition >= QCBOR_MAX_ARRAY_OFFSET) {
+ me->uError = QCBOR_ERR_BUFFER_TOO_LARGE;
+
+ } else {
+ // Increase nesting level because this is a map or array.
+ // Cast from size_t to uin32_t is safe because of check above
+ me->uError = Nesting_Increase(&(me->nesting), uMajorType, (uint32_t)uEndPosition);
+ }
+ }
+}
+
+
+/*
+ Public functions for closing arrays and maps. See header qcbor.h
+ */
+void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *me,
+ uint8_t uMajorType,
+ UsefulBufC *pWrappedCBOR)
+{
+ if(me->uError == QCBOR_SUCCESS) {
+ if(!Nesting_IsInNest(&(me->nesting))) {
+ me->uError = QCBOR_ERR_TOO_MANY_CLOSES;
+ } else if(Nesting_GetMajorType(&(me->nesting)) != uMajorType) {
+ me->uError = QCBOR_ERR_CLOSE_MISMATCH;
+ } else {
+ // When the array, map or bstr wrap was started, nothing was done
+ // except note the position of the start of it. This code goes back
+ // and inserts the actual CBOR array, map or bstr and its length.
+ // That means all the data that is in the array, map or wrapped
+ // needs to be slid to the right. This is done by UsefulOutBuf's
+ // insert function that is called from inside
+ // InsertEncodedTypeAndNumber()
+ const size_t uInsertPosition = Nesting_GetStartPos(&(me->nesting));
+ const size_t uEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
+ // This can't go negative because the UsefulOutBuf always only grows
+ // and never shrinks. UsefulOutBut itself also has defenses such that
+ // it won't write were it should not even if given hostile input lengths
+ const size_t uLenOfEncodedMapOrArray = uEndPosition - uInsertPosition;
+
+ // Length is number of bytes for a bstr and number of items a for map & array
+ const size_t uLength = uMajorType == CBOR_MAJOR_TYPE_BYTE_STRING ?
+ uLenOfEncodedMapOrArray : Nesting_GetCount(&(me->nesting));
+
+ // Actually insert
+ InsertEncodedTypeAndNumber(me,
+ uMajorType, // major type bstr, array or map
+ 0, // no minimum length for encoding
+ uLength, // either len of bstr or num map / array items
+ uInsertPosition); // position in out buffer
+
+ // Return pointer and length to the enclosed encoded CBOR. The intended
+ // use is for it to be hashed (e.g., SHA-256) in a COSE implementation.
+ // This must be used right away, as the pointer and length go invalid
+ // on any subsequent calls to this function because of the
+ // InsertEncodedTypeAndNumber() call that slides data to the right.
+ if(pWrappedCBOR) {
+ const UsefulBufC PartialResult = UsefulOutBuf_OutUBuf(&(me->OutBuf));
+ const size_t uBstrLen = UsefulOutBuf_GetEndPosition(&(me->OutBuf)) - uEndPosition;
+ *pWrappedCBOR = UsefulBuf_Tail(PartialResult, uInsertPosition+uBstrLen);
+ }
+ Nesting_Decrease(&(me->nesting));
+ }
+ }
+}
+
+
+
+
+/*
+ Public functions to finish and get the encoded result. See header qcbor.h
+ */
+QCBORError QCBOREncode_Finish(QCBOREncodeContext *me, UsefulBufC *pEncodedCBOR)
+{
+ QCBORError uReturn = me->uError;
+
+ if(uReturn != QCBOR_SUCCESS) {
+ goto Done;
+ }
+
+ if (Nesting_IsInNest(&(me->nesting))) {
+ uReturn = QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN;
+ goto Done;
+ }
+
+ if(UsefulOutBuf_GetError(&(me->OutBuf))) {
+ // Stuff didn't fit in the buffer.
+ // This check catches this condition for all the appends and inserts
+ // so checks aren't needed when the appends and inserts are performed.
+ // And of course UsefulBuf will never overrun the input buffer given
+ // to it. No complex analysis of the error handling in this file is
+ // needed to know that is true. Just read the UsefulBuf code.
+ uReturn = QCBOR_ERR_BUFFER_TOO_SMALL;
+ goto Done;
+ }
+
+ *pEncodedCBOR = UsefulOutBuf_OutUBuf(&(me->OutBuf));
+
+Done:
+ return uReturn;
+}
+
+
+/*
+ Public functions to finish and get the encoded result. See header qcbor.h
+ */
+QCBORError QCBOREncode_FinishGetSize(QCBOREncodeContext *me, size_t *puEncodedLen)
+{
+ UsefulBufC Enc;
+
+ QCBORError nReturn = QCBOREncode_Finish(me, &Enc);
+
+ if(nReturn == QCBOR_SUCCESS) {
+ *puEncodedLen = Enc.len;
+ }
+
+ return nReturn;
+}
+
+
+
+
+/*
+ Notes on the code
+
+ CBOR Major Type Public Function
+ 0 QCBOREncode_AddUInt64
+ 0, 1 QCBOREncode_AddUInt64, QCBOREncode_AddInt64
+ 2, 3 QCBOREncode_AddBuffer, Also QCBOREncode_OpenMapOrArray
+ 4, 5 QCBOREncode_OpenMapOrArray
+ 6 QCBOREncode_AddTag
+ 7 QCBOREncode_AddDouble, QCBOREncode_AddType7
+
+ Object code sizes on X86 with LLVM compiler and -Os (Dec 30, 2018)
+
+ _QCBOREncode_Init 69
+ _QCBOREncode_AddUInt64 76
+ _QCBOREncode_AddInt64 87
+ _QCBOREncode_AddBuffer 113
+ _QCBOREncode_AddTag 27
+ _QCBOREncode_AddType7 87
+ _QCBOREncode_AddDouble 36
+ _QCBOREncode_OpenMapOrArray 103
+ _QCBOREncode_CloseMapOrArray 181
+ _InsertEncodedTypeAndNumber 190
+ _QCBOREncode_Finish 72
+ _QCBOREncode_FinishGetSize 70
+
+ Total is about 1.1KB
+
+ _QCBOREncode_CloseMapOrArray is larger because it has a lot
+ of nesting tracking to do and much of Nesting_ inlines
+ into it. It probably can't be reduced much.
+
+ If the error returned by Nesting_Increment() can be ignored
+ because the limit is so high and the consequence of exceeding
+ is proved to be inconsequential, then a lot of if(me->uError)
+ instance can be removed, saving some code.
+
+ */
+
diff --git a/lib/ext/qcbor/test/UsefulBuf_Tests.c b/lib/ext/qcbor/test/UsefulBuf_Tests.c
new file mode 100644
index 0000000..cfa1262
--- /dev/null
+++ b/lib/ext/qcbor/test/UsefulBuf_Tests.c
@@ -0,0 +1,700 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+#include "UsefulBuf.h"
+
+
+/* Basic exercise...
+
+ Call all the main public functions.
+
+ Binary compare the result to the expected.
+
+ There is nothing adversarial in this test
+ */
+const char * UOBTest_NonAdversarial()
+{
+ const char *szReturn = NULL;
+
+ UsefulBuf_MAKE_STACK_UB(outbuf,50);
+
+ UsefulOutBuf UOB;
+
+ UsefulOutBuf_Init(&UOB, outbuf);
+
+ if(!UsefulOutBuf_AtStart(&UOB)) {
+ szReturn = "Not at start";
+ goto Done;
+ }
+
+ // Put 7 bytes at beginning of buf
+ UsefulOutBuf_AppendData(&UOB, "bluster", 7);
+
+ if(UsefulOutBuf_AtStart(&UOB)) {
+ szReturn = "At start";
+ goto Done;
+ }
+
+ // add a space to end
+ UsefulOutBuf_AppendByte(&UOB, ' ');
+
+ // Add 5 bytes to the end
+ UsefulBufC UBC = {"hunny", 5};
+ UsefulOutBuf_AppendUsefulBuf(&UOB, UBC);
+
+ // Insert 9 bytes at the beginning, slide the previous stuff right
+ UsefulOutBuf_InsertData(&UOB, "heffalump", 9, 0);
+ UsefulOutBuf_InsertByte(&UOB, ' ', 9);
+
+ // Put 9 bytes in at position 10 -- just after "heffalump "
+ UsefulBufC UBC2 = {"unbounce ", 9};
+ UsefulOutBuf_InsertUsefulBuf(&UOB, UBC2, 10);
+
+ // Make it a null terminated string (because all the appends and inserts above not strcpy !)
+ UsefulOutBuf_AppendByte(&UOB, '\0');
+
+
+ UsefulBufC U = UsefulOutBuf_OutUBuf(&UOB);
+
+ const char *expected = "heffalump unbounce bluster hunny";
+
+ if(UsefulBuf_IsNULLC(U) || U.len-1 != strlen(expected) || strcmp(expected, U.ptr) || UsefulOutBuf_GetError(&UOB)) {
+ szReturn = "OutUBuf";
+ }
+
+ UsefulBuf_MAKE_STACK_UB(buf, 50);
+ UsefulBufC Out = UsefulOutBuf_CopyOut(&UOB, buf);
+
+ if(UsefulBuf_IsNULLC(Out) || Out.len-1 != strlen(expected) || strcmp(expected, Out.ptr)) {
+ szReturn = "CopyOut";
+ }
+
+Done:
+ return szReturn;
+}
+
+
+/*
+ Append test utility.
+ pUOB is the buffer to append too
+ num is the amount to append
+ expected is the expected return code, 0 or 1
+
+ returns 0 if test passed
+
+ */
+static int AppendTest(UsefulOutBuf *pUOB, size_t num, int expected)
+{
+ //reset
+ UsefulOutBuf_Reset(pUOB);
+
+ // check status first
+ if(UsefulOutBuf_GetError(pUOB))
+ return 1;
+
+ // append the bytes
+ UsefulOutBuf_AppendData(pUOB, (const uint8_t *)"bluster", num);
+
+ // check error status after
+ if(UsefulOutBuf_GetError(pUOB) != expected)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ Same as append, but takes a position param too
+ */
+static int InsertTest(UsefulOutBuf *pUOB, size_t num, size_t pos, int expected)
+{
+ // reset
+ UsefulOutBuf_Reset(pUOB);
+
+ // check
+ if(UsefulOutBuf_GetError(pUOB))
+ return 1;
+
+ UsefulOutBuf_InsertData(pUOB, (const uint8_t *)"bluster", num, pos);
+
+ if(UsefulOutBuf_GetError(pUOB) != expected)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ Boundary conditions to test
+ - around 0
+ - around the buffer size
+ - around MAX size_t
+
+
+ Test these for the buffer size and the cursor, the insert amount, the append amount and the insert position
+
+ */
+
+const char *UOBTest_BoundaryConditionsTest()
+{
+ UsefulBuf_MAKE_STACK_UB(outbuf,2);
+
+ UsefulOutBuf UOB;
+
+ UsefulOutBuf_Init(&UOB, outbuf);
+
+ // append 0 byte to a 2 byte buffer --> success
+ if(AppendTest(&UOB, 0, 0))
+ return "Append 0 bytes failed";
+
+ // append 1 byte to a 2 byte buffer --> success
+ if(AppendTest(&UOB, 1, 0))
+ return "Append of 1 byte failed";
+
+ // append 2 byte to a 2 byte buffer --> success
+ if(AppendTest(&UOB, 2, 0))
+ return "Append to fill buffer failed";
+
+ // append 3 bytes to a 2 byte buffer --> failure
+ if(AppendTest(&UOB, 3, 1))
+ return "Overflow of buffer not caught";
+
+ // append max size_t to a 2 byte buffer --> failure
+ if(AppendTest(&UOB, SIZE_MAX, 1))
+ return "Append of SIZE_MAX error not caught";
+
+ if(InsertTest(&UOB, 1, 0, 0))
+ return "Insert 1 byte at start failed";
+
+ if(InsertTest(&UOB, 2, 0, 0))
+ return "Insert 2 bytes at start failed";
+
+ if(InsertTest(&UOB, 3, 0, 1))
+ return "Insert overflow not caught";
+
+ if(InsertTest(&UOB, 1, 1, 1))
+ return "Bad insertion point not caught";
+
+
+ UsefulBuf_MAKE_STACK_UB(outBuf2,10);
+
+ UsefulOutBuf_Init(&UOB, outBuf2);
+
+ UsefulOutBuf_Reset(&UOB);
+ // put data in the buffer
+ UsefulOutBuf_AppendString(&UOB, "abc123");
+
+ UsefulOutBuf_InsertString(&UOB, "xyz*&^", 0);
+
+ if(!UsefulOutBuf_GetError(&UOB)) {
+ return "insert with data should have failed";
+ }
+
+
+ UsefulOutBuf_Init(&UOB, (UsefulBuf){NULL, SIZE_MAX - 5});
+ UsefulOutBuf_AppendData(&UOB, "123456789", SIZE_MAX -6);
+ if(UsefulOutBuf_GetError(&UOB)) {
+ return "insert in huge should have succeeded";
+ }
+
+ UsefulOutBuf_Init(&UOB, (UsefulBuf){NULL, SIZE_MAX - 5});
+ UsefulOutBuf_AppendData(&UOB, "123456789", SIZE_MAX -5);
+ if(UsefulOutBuf_GetError(&UOB)) {
+ return "insert in huge should have succeeded";
+ }
+
+ UsefulOutBuf_Init(&UOB, (UsefulBuf){NULL, SIZE_MAX - 5});
+ UsefulOutBuf_AppendData(&UOB, "123456789", SIZE_MAX - 4);
+ if(!UsefulOutBuf_GetError(&UOB)) {
+ return "lengths near max size";
+ }
+
+ return NULL;
+}
+
+
+
+
+
+// Test function to get size and magic number check
+
+const char *TestBasicSanity()
+{
+ UsefulBuf_MAKE_STACK_UB(outbuf,10);
+
+ UsefulOutBuf UOB;
+
+ // First -- make sure that the room left function returns the right amount
+ UsefulOutBuf_Init(&UOB, outbuf);
+
+ if(UsefulOutBuf_RoomLeft(&UOB) != 10)
+ return "room left failed";
+
+ if(!UsefulOutBuf_WillItFit(&UOB, 9)) {
+ return "it did not fit";
+ }
+
+ if(UsefulOutBuf_WillItFit(&UOB, 11)) {
+ return "it should have not fit";
+ }
+
+
+ // Next -- make sure that the magic number checking is working right
+ UOB.magic = 8888; // make magic bogus
+
+ UsefulOutBuf_AppendData(&UOB, (const uint8_t *)"bluster", 7);
+
+ if(!UsefulOutBuf_GetError(&UOB))
+ return "magic corruption check failed";
+
+
+
+ // Next make sure that the valid data length check is working right
+ UsefulOutBuf_Init(&UOB, outbuf);
+
+ UOB.data_len = UOB.UB.len+1; // make size bogus
+
+ UsefulOutBuf_AppendData(&UOB, (const uint8_t *)"bluster", 7);
+ if(!UsefulOutBuf_GetError(&UOB))
+ return "valid data check failed";
+
+ return NULL;
+}
+
+
+
+const char *UBMacroConversionsTest()
+{
+ char *szFoo = "foo";
+
+ UsefulBufC Foo = UsefulBuf_FromSZ(szFoo);
+ if(Foo.len != 3 || strncmp(Foo.ptr, szFoo, 3))
+ return "SZToUsefulBufC failed";
+
+ UsefulBufC Too = UsefulBuf_FROM_SZ_LITERAL("Toooo");
+ if(Too.len != 5 || strncmp(Too.ptr, "Toooo", 5))
+ return "UsefulBuf_FROM_SZ_LITERAL failed";
+
+ uint8_t pB[] = {0x42, 0x6f, 0x6f};
+ UsefulBufC Boo = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pB);
+ if(Boo.len != 3 || strncmp(Boo.ptr, "Boo", 3))
+ return "UsefulBuf_FROM_BYTE_ARRAY_LITERAL failed";
+
+ char *sz = "not const"; // some data for the test
+ UsefulBuf B = (UsefulBuf){sz, sizeof(sz)};
+ UsefulBufC BC = UsefulBuf_Const(B);
+ if(BC.len != sizeof(sz) || BC.ptr != sz)
+ return "UsefulBufConst failed";
+
+ return NULL;
+}
+
+
+const char *UBUtilTests()
+{
+ UsefulBuf UB = NULLUsefulBuf;
+
+ if(!UsefulBuf_IsNULL(UB)){
+ return "IsNull failed";
+ }
+
+ if(!UsefulBuf_IsEmpty(UB)){
+ return "IsEmpty failed";
+ }
+
+ if(!UsefulBuf_IsNULLOrEmpty(UB)) {
+ return "IsNULLOrEmpty failed";
+ }
+
+ const UsefulBufC UBC = UsefulBuf_Const(UB);
+
+ if(!UsefulBuf_IsNULLC(UBC)){
+ return "IsNull const failed";
+ }
+
+ if(!UsefulBuf_IsEmptyC(UBC)){
+ return "IsEmptyC failed";
+ }
+
+ if(!UsefulBuf_IsNULLOrEmptyC(UBC)){
+ return "IsNULLOrEmptyC failed";
+ }
+
+ const UsefulBuf UB2 = UsefulBuf_Unconst(UBC);
+ if(!UsefulBuf_IsEmpty(UB2)) {
+ return "Back to UB is Empty failed";
+ }
+
+ UB.ptr = "x"; // just some valid pointer
+
+ if(UsefulBuf_IsNULL(UB)){
+ return "IsNull failed";
+ }
+
+ if(!UsefulBuf_IsEmptyC(UBC)){
+ return "IsEmpty failed";
+ }
+
+ // test the Unconst.
+ if(UsefulBuf_Unconst(UBC).ptr != NULL) {
+ return "Unconst failed";
+ }
+
+ // Set 100 bytes of '+'; validated a few tests later
+ UsefulBuf_MAKE_STACK_UB(Temp, 100);
+ const UsefulBufC TempC = UsefulBuf_Set(Temp, '+');
+
+ // Try to copy into a buf that is too small and see failure
+ UsefulBuf_MAKE_STACK_UB(Temp2, 99);
+ if(!UsefulBuf_IsNULLC(UsefulBuf_Copy(Temp2, TempC))) {
+ return "Copy should have failed";
+ }
+
+ if(UsefulBuf_IsNULLC(UsefulBuf_CopyPtr(Temp2, "xx", 2))) {
+ return "CopyPtr failed";
+ }
+
+ UsefulBufC xxyy = UsefulBuf_CopyOffset(Temp2, 2, UsefulBuf_FROM_SZ_LITERAL("yy"));
+ if(UsefulBuf_IsNULLC(xxyy)) {
+ return "CopyOffset Failed";
+ }
+
+ if(UsefulBuf_Compare(UsefulBuf_Head(xxyy, 3), UsefulBuf_FROM_SZ_LITERAL("xxy"))) {
+ return "head failed";
+ }
+
+ if(UsefulBuf_Compare(UsefulBuf_Tail(xxyy, 1), UsefulBuf_FROM_SZ_LITERAL("xyy"))) {
+ return "tail failed";
+ }
+
+ if(!UsefulBuf_IsNULLC(UsefulBuf_Head(xxyy, 5))) {
+ return "head should have failed";
+ }
+
+ if(!UsefulBuf_IsNULLC(UsefulBuf_Tail(xxyy, 5))) {
+ return "tail should have failed";
+ }
+
+ if(!UsefulBuf_IsNULLC(UsefulBuf_Tail(NULLUsefulBufC, 0))) {
+ return "tail of NULLUsefulBufC is not NULLUsefulBufC";
+ }
+
+ const UsefulBufC TailResult = UsefulBuf_Tail((UsefulBufC){NULL, 100}, 99);
+ if(TailResult.ptr != NULL || TailResult.len != 1) {
+ return "tail of NULL and length incorrect";
+ }
+
+ if(!UsefulBuf_IsNULLC(UsefulBuf_CopyOffset(Temp2, 100, UsefulBuf_FROM_SZ_LITERAL("yy")))) {
+ return "Copy Offset should have failed";
+ }
+
+ // Try to copy into a NULL/empty buf and see failure
+ const UsefulBuf UBNull = NULLUsefulBuf;
+ if(!UsefulBuf_IsNULLC(UsefulBuf_Copy(UBNull, TempC))) {
+ return "Copy to NULL should have failed";
+ }
+
+
+ // Try to set a NULL/empty buf; nothing should happen
+ UsefulBuf_Set(UBNull, '+'); // This will crash on failure
+
+ // Copy successfully to a buffer
+ UsefulBuf_MAKE_STACK_UB(Temp3, 101);
+ if(UsefulBuf_IsNULLC(UsefulBuf_Copy(Temp3, TempC))) {
+ return "Copy should not have failed";
+ }
+
+ static const uint8_t pExpected[] = {
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ };
+ UsefulBufC Expected = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpected);
+ // This validates comparison for equality and the UsefulBuf_Set
+ if(UsefulBuf_Compare(Expected, TempC)) {
+ return "Set / Copy / Compare failed";
+ }
+
+ // Compare two empties and expect success
+ if(UsefulBuf_Compare(NULLUsefulBufC, NULLUsefulBufC)){
+ return "Compare Empties failed";
+ }
+
+ // Compare with empty and expect the first to be larger
+ if(UsefulBuf_Compare(Expected, NULLUsefulBufC) <= 0){
+ return "Compare with empty failed";
+ }
+
+
+ static const uint8_t pExpectedBigger[] = {
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', ',',
+ };
+ const UsefulBufC ExpectedBigger = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedBigger);
+
+ // Expect -1 when the first arg is smaller
+ if(UsefulBuf_Compare(Expected, ExpectedBigger) >= 0){
+ return "Compare with bigger";
+ }
+
+
+ static const uint8_t pExpectedSmaller[] = {
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '*',
+ };
+ const UsefulBufC ExpectedSmaller = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedSmaller);
+ // Expect +1 when the first arg is larger
+ if(UsefulBuf_Compare(Expected, ExpectedSmaller) <= 0){
+ return "Compare with smaller";
+ }
+
+
+ static const uint8_t pExpectedLonger[] = {
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+'
+ };
+ const UsefulBufC ExpectedLonger = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedLonger);
+
+ // Expect -1 when the first arg is smaller
+ if(UsefulBuf_Compare(Expected, ExpectedLonger) >= 0){
+ return "Compare with longer";
+ }
+
+
+ static const uint8_t pExpectedShorter[] = {
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ '+', '+', '+', '+', '+', '+', '+', '+', '+',
+ };
+ const UsefulBufC ExpectedShorter = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedShorter);
+ // Expect +1 with the first arg is larger
+ if(UsefulBuf_Compare(Expected, ExpectedShorter) <= 0){
+ return "Compare with shorter";
+ }
+
+
+ if(UsefulBuf_IsNULLC(UsefulBuf_Copy(Temp, NULLUsefulBufC))) {
+ return "Copy null/empty failed";
+ }
+
+ // Look for +++++... in +++++... and find it at the beginning
+ if(0 != UsefulBuf_FindBytes(ExpectedLonger, ExpectedShorter)){
+ return "Failed to find";
+ }
+
+ // look for ++* in ....++* and find it at the end
+ static const uint8_t pToFind[] = {'+', '+', '*'};
+ const UsefulBufC ToBeFound = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pToFind);
+
+ if(97 != UsefulBuf_FindBytes(ExpectedSmaller, ToBeFound)){
+ return "Failed to find 2";
+ }
+
+ // look for ++* in ....++, and find it near the end
+ if(SIZE_MAX != UsefulBuf_FindBytes(ExpectedBigger, ToBeFound)){
+ return "Failed to not find";
+ }
+
+ // Look for the whole buffer in itself and succeed.
+ if(0 != UsefulBuf_FindBytes(ExpectedLonger, ExpectedLonger)){
+ return "Failed to find 3";
+ }
+
+ return NULL;
+}
+
+
+const char * UIBTest_IntegerFormat()
+{
+ UsefulOutBuf_MakeOnStack(UOB,100);
+
+ const uint32_t u32 = 0x0A0B0C0D; // from https://en.wikipedia.org/wiki/Endianness
+ const uint64_t u64 = 1984738472938472;
+ const uint16_t u16 = 40000;
+ const uint8_t u8 = 9;
+ const float f = (float)314.15;
+ const double d = 2.1e10;
+
+
+ UsefulOutBuf_AppendUint32(&UOB, u32); // Also tests UsefulOutBuf_InsertUint64 and UsefulOutBuf_GetEndPosition
+ UsefulOutBuf_AppendUint64(&UOB, u64); // Also tests UsefulOutBuf_InsertUint32
+ UsefulOutBuf_AppendUint16(&UOB, u16); // Also tests UsefulOutBuf_InsertUint16
+ UsefulOutBuf_AppendByte(&UOB, u8);
+ UsefulOutBuf_AppendFloat(&UOB, f); // Also tests UsefulOutBuf_InsertFloat
+ UsefulOutBuf_AppendDouble(&UOB, d); // Also tests UsefulOutBuf_InsertDouble
+
+ const UsefulBufC O = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_IsNULLC(O))
+ return "Couldn't output integers";
+
+ // from https://en.wikipedia.org/wiki/Endianness
+ const uint8_t pExpectedNetworkOrder[4] = {0x0A, 0x0B, 0x0C, 0x0D};
+ if(memcmp(O.ptr, pExpectedNetworkOrder, 4)) {
+ return "not in network order";
+ }
+
+ UsefulInputBuf UIB;
+
+ UsefulInputBuf_Init(&UIB, O);
+
+ if(UsefulInputBuf_Tell(&UIB) != 0) {
+ return "UsefulInputBuf_Tell failed";
+ }
+
+ if(UsefulInputBuf_GetUint32(&UIB) != u32) {
+ return "u32 out then in failed";
+ }
+ if(UsefulInputBuf_GetUint64(&UIB) != u64) {
+ return "u64 out then in failed";
+ }
+ if(UsefulInputBuf_GetUint16(&UIB) != u16) {
+ return "u16 out then in failed";
+ }
+ if(UsefulInputBuf_GetByte(&UIB) != u8) {
+ return "u8 out then in failed";
+ }
+ if(UsefulInputBuf_GetFloat(&UIB) != f) {
+ return "float out then in failed";
+ }
+ if(UsefulInputBuf_GetDouble(&UIB) != d) {
+ return "double out then in failed";
+ }
+
+ // Reset and go again for a few more tests
+ UsefulInputBuf_Init(&UIB, O);
+
+ const UsefulBufC Four = UsefulInputBuf_GetUsefulBuf(&UIB, 4);
+ if(UsefulBuf_IsNULLC(Four)) {
+ return "Four is NULL";
+ }
+ if(UsefulBuf_Compare(Four, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedNetworkOrder))) {
+ return "Four compare failed";
+ }
+
+ if(UsefulInputBuf_BytesUnconsumed(&UIB) != 23){
+ return "Wrong number of unconsumed bytes";
+ }
+
+ if(!UsefulInputBuf_BytesAvailable(&UIB, 23)){
+ return "Wrong number of bytes available I";
+ }
+
+ if(UsefulInputBuf_BytesAvailable(&UIB, 24)){
+ return "Wrong number of bytes available II";
+ }
+
+ UsefulInputBuf_Seek(&UIB, 0);
+
+ if(UsefulInputBuf_GetError(&UIB)) {
+ return "unexpected error after seek";
+ }
+
+ const uint8_t *pGetBytes = (const uint8_t *)UsefulInputBuf_GetBytes(&UIB, 4);
+ if(pGetBytes == NULL) {
+ return "GetBytes returns NULL";
+ }
+
+ if(memcmp(pGetBytes, pExpectedNetworkOrder, 4)) {
+ return "Got wrong bytes";
+ }
+
+ UsefulInputBuf_Seek(&UIB, 28);
+
+ if(!UsefulInputBuf_GetError(&UIB)) {
+ return "expected error after seek";
+ }
+
+ return NULL;
+}
+
+
+const char *UBUTest_CopyUtil()
+{
+ if(UsefulBufUtil_CopyFloatToUint32(65536.0F) != 0x47800000) {
+ return "CopyFloatToUint32 failed";
+ }
+
+ if(UsefulBufUtil_CopyDoubleToUint64(4e-40F) != 0X37C16C2800000000ULL) {
+ return "CopyDoubleToUint64 failed";
+ }
+
+ if(UsefulBufUtil_CopyUint64ToDouble(0X37C16C2800000000ULL) != 4e-40F) {
+ return "CopyUint64ToDouble failed";
+ }
+
+ if(UsefulBufUtil_CopyUint32ToFloat(0x47800000) != 65536.0F) {
+ return "CopyUint32ToFloat failed";
+ }
+
+ return NULL;
+}
+
+
+
diff --git a/lib/ext/qcbor/test/UsefulBuf_Tests.h b/lib/ext/qcbor/test/UsefulBuf_Tests.h
new file mode 100644
index 0000000..976de62
--- /dev/null
+++ b/lib/ext/qcbor/test/UsefulBuf_Tests.h
@@ -0,0 +1,50 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+#ifndef UsefulBuf_UsefulBuf_Tests_h
+#define UsefulBuf_UsefulBuf_Tests_h
+
+const char * UOBTest_NonAdversarial(void);
+
+const char * TestBasicSanity(void);
+
+const char * UOBTest_BoundaryConditionsTest(void);
+
+const char * UBMacroConversionsTest(void);
+
+const char * UBUtilTests(void);
+
+const char * UIBTest_IntegerFormat(void);
+
+const char * UBUTest_CopyUtil(void);
+
+#endif
diff --git a/lib/ext/qcbor/test/float_tests.c b/lib/ext/qcbor/test/float_tests.c
new file mode 100644
index 0000000..eaf75aa
--- /dev/null
+++ b/lib/ext/qcbor/test/float_tests.c
@@ -0,0 +1,474 @@
+/*==============================================================================
+ float_tests.c -- tests for float and conversion to/from half-precision
+
+ Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created on 9/19/18
+ ==============================================================================*/
+
+#include "float_tests.h"
+#include "qcbor.h"
+#include "half_to_double_from_rfc7049.h"
+#include <math.h> // For INFINITY and NAN and isnan()
+
+
+
+static const uint8_t spExpectedHalf[] = {
+ 0xB1,
+ 0x64,
+ 0x7A, 0x65, 0x72, 0x6F,
+ 0xF9, 0x00, 0x00, // 0.000
+ 0x6A,
+ 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
+ 0xF9, 0x7C, 0x00, // Infinity
+ 0x73,
+ 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
+ 0xF9, 0xFC, 0x00, // -Inifinity
+ 0x63,
+ 0x4E, 0x61, 0x4E,
+ 0xF9, 0x7E, 0x00, // NaN
+ 0x63,
+ 0x6F, 0x6E, 0x65,
+ 0xF9, 0x3C, 0x00, // 1.0
+ 0x69,
+ 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
+ 0xF9, 0x35, 0x55, // 0.333251953125
+ 0x76,
+ 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
+ 0xF9, 0x7B, 0xFF, // 65504.0
+ 0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
+ 0xF9, 0x7C, 0x00, // Infinity
+ 0x72,
+ 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
+ 0xF9, 0x00, 0x01, // 0.000000059604
+ 0x6F,
+ 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
+ 0xF9, 0x03, 0xFF, // 0.0000609755516
+ 0x71,
+ 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
+ 0xF9, 0x04, 0x00, // 0.000061988
+ 0x70,
+ 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
+ 0xF9, 0x00, 0x00,
+ 0x03,
+ 0xF9, 0xC0, 0x00, // -2
+ 0x04,
+ 0xF9, 0x7E, 0x00, // qNaN
+ 0x05,
+ 0xF9, 0x7C, 0x01, // sNaN
+ 0x06,
+ 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
+ 0x07,
+ 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
+
+};
+
+
+int HalfPrecisionDecodeBasicTests()
+{
+ UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
+
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, HalfPrecision, 0);
+
+ QCBORItem Item;
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return -1;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
+ return -2;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
+ return -3;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
+ return -4;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
+ return -5;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
+ return -6;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
+ return -7;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
+ return -8;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
+ return -9;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item); // TODO: check this
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
+ return -10;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item); // TODO: check this
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
+ return -11;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item); // TODO check this
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
+ return -12;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
+ return -13;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
+ return -14;
+ }
+
+ // TODO: double check these four tests
+ QCBORDecode_GetNext(&DC, &Item); // qNaN
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
+ return -15;
+ }
+ QCBORDecode_GetNext(&DC, &Item); // sNaN
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
+ return -16;
+ }
+ QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
+ return -17;
+ }
+ QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
+ return -18;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -19;
+ }
+
+ return 0;
+}
+
+
+
+
+int HalfPrecisionAgainstRFCCodeTest()
+{
+ for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
+ unsigned char x[2];
+ x[1] = uHalfP & 0xff;
+ x[0] = uHalfP >> 8;
+ double d = decode_half(x);
+
+ // Contruct the CBOR for the half-precision float by hand
+ UsefulBuf_MAKE_STACK_UB(__xx, 3);
+ UsefulOutBuf UOB;
+ UsefulOutBuf_Init(&UOB, __xx);
+
+ const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
+ UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
+ UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
+
+ // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
+
+ QCBORItem Item;
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
+ return -1;
+ }
+
+ //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n", uHalfP,Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
+
+ if(isnan(d)) {
+ // The RFC code uses the native instructions which may or may not
+ // handle sNaN, qNaN and NaN payloads correctly. This test just
+ // makes sure it is a NaN and doesn't worry about the type of NaN
+ if(!isnan(Item.val.dfnum)) {
+ return -3;
+ }
+ } else {
+ if(Item.val.dfnum != d) {
+ return -2;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ {"zero": 0.0,
+ "negative zero": -0.0,
+ "infinitity": Infinity,
+ "negative infinitity": -Infinity,
+ "NaN": NaN,
+ "one": 1.0,
+ "one third": 0.333251953125,
+ "largest half-precision": 65504.0,
+ "largest half-precision point one": 65504.1,
+ "too-large half-precision": 65536.0,
+ "smallest subnormal": 5.96046448e-8,
+ "smallest normal": 0.00006103515261202119,
+ "biggest subnormal": 0.00006103515625,
+ "subnormal single": 4.00000646641519e-40,
+ 3: -2.0,
+ "large single exp": 2.5521177519070385e+38,
+ "too-large single exp": 5.104235503814077e+38,
+ "biggest single with prec": 16777216.0,
+ "first single with prec loss": 16777217.0,
+ 1: "fin"}
+ */
+static const uint8_t spExpectedSmallest[] = {
+ 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
+ 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
+ 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
+ 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
+ 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
+ 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
+ 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
+ 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
+ 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
+ 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
+ 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
+ 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
+ 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
+ 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
+ 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
+ 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
+ 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
+ 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
+ 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
+ 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
+ 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
+ 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
+ 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
+ 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
+ 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
+ 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
+ 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
+ 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
+ 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
+ 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
+ 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
+ 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
+ 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
+ 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
+ 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
+ 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
+ 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
+ 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
+ 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
+ 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
+};
+
+
+int DoubleAsSmallestTest()
+{
+ UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
+
+#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
+#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
+
+
+ QCBOREncodeContext EC;
+ QCBOREncode_Init(&EC, EncodedHalfsMem);
+ // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
+ QCBOREncode_OpenMap(&EC);
+ // 64 # text(4)
+ // 7A65726F # "zero"
+ // F9 0000 # primitive(0)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
+
+ // 64 # text(4)
+ // 7A65726F # "negative zero"
+ // F9 8000 # primitive(0)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
+
+ // 6A # text(10)
+ // 696E66696E6974697479 # "infinitity"
+ // F9 7C00 # primitive(31744)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
+
+ // 73 # text(19)
+ // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
+ // F9 FC00 # primitive(64512)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
+
+ // 63 # text(3)
+ // 4E614E # "NaN"
+ // F9 7E00 # primitive(32256)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
+
+ // TODO: test a few NaN variants
+
+ // 63 # text(3)
+ // 6F6E65 # "one"
+ // F9 3C00 # primitive(15360)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
+
+ // 69 # text(9)
+ // 6F6E65207468697264 # "one third"
+ // F9 3555 # primitive(13653)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
+
+ // 76 # text(22)
+ // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
+ // F9 7BFF # primitive(31743)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
+
+ // 76 # text(22)
+ // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
+ // F9 7BFF # primitive(31743)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
+
+ // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
+ // 78 18 # text(24)
+ // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
+ // FA 47800000 # primitive(31743)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
+
+ // The smallest possible half-precision subnormal, but digitis are lost converting
+ // to half, so this turns into a double
+ // 72 # text(18)
+ // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
+ // FB 3E700000001C5F68 # primitive(4499096027744984936)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
+
+ // The smallest possible half-precision snormal, but digitis are lost converting
+ // to half, so this turns into a single TODO: confirm this is right
+ // 6F # text(15)
+ // 736D616C6C657374206E6F726D616C # "smallest normal"
+ // FA 387FFFFF # primitive(947912703)
+ // in hex single is 0x387fffff, exponent -15, significand 7fffff
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
+
+ // 71 # text(17)
+ // 62696767657374207375626E6F726D616C # "biggest subnormal"
+ // F9 0400 # primitive(1024)
+ // in hex single is 0x38800000, exponent -14, significand 0
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
+
+ // 70 # text(16)
+ // 7375626E6F726D616C2073696E676C65 # "subnormal single"
+ // FB 37C16C2800000000 # primitive(4017611261645684736)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
+
+ // 03 # unsigned(3)
+ // F9 C000 # primitive(49152)
+ QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
+
+ // 70 # text(16)
+ // 6C617267652073696E676C6520657870 # "large single exp"
+ // FA 7F400000 # primitive(2134900736)
+ // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
+
+ // 74 # text(20)
+ // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
+ // FB 47F8000000000000 # primitive(5185894970917126144)
+ // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38); // Exponent too large for single
+
+ // 66 # text(6)
+ // 646664666465 # "dfdfde"
+ // FA 4B800000 # primitive(1266679808)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec",16777216); // Single with no precision loss
+
+ // 78 18 # text(24)
+ // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
+ // FA 4B800000 # primitive(1266679808)
+ QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss",16777217); // Double becuase of precision loss
+
+ // Just a convenient marker when cutting and pasting encoded CBOR
+ QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
+
+ QCBOREncode_CloseMap(&EC);
+
+ UsefulBufC EncodedHalfs;
+ int nReturn = QCBOREncode_Finish(&EC, &EncodedHalfs);
+ if(nReturn) {
+ return -1;
+ }
+
+ if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+#ifdef NAN_EXPERIMENT
+/*
+ Code for checking what the double to float cast does with
+ NaNs. Not run as part of tests. Keep it around to
+ be able to check various platforms and CPUs.
+ */
+
+#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
+#define DOUBLE_NUM_EXPONENT_BITS (11)
+#define DOUBLE_NUM_SIGN_BITS (1)
+
+#define DOUBLE_SIGNIFICAND_SHIFT (0)
+#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
+#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
+
+#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
+#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
+#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
+#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
+
+
+static int NaNExperiments() {
+ double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
+ double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
+ double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
+
+ float f1 = (float)dqNaN;
+ float f2 = (float)dsNaN;
+ float f3 = (float)dqNaNPayload;
+
+
+ uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
+ uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
+ uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
+
+ // Result of this on x86 is that every NaN is a qNaN. The intel
+ // CVTSD2SS instruction ignores the NaN payload and even converts
+ // a sNaN to a qNaN.
+
+ return 0;
+}
+#endif
+
+
+
diff --git a/lib/ext/qcbor/test/float_tests.h b/lib/ext/qcbor/test/float_tests.h
new file mode 100644
index 0000000..b7174c8
--- /dev/null
+++ b/lib/ext/qcbor/test/float_tests.h
@@ -0,0 +1,23 @@
+/*==============================================================================
+ float_tests.h -- tests for float and conversion to/from half-precision
+
+ Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created on 9/19/18
+ ==============================================================================*/
+
+#ifndef float_tests_h
+#define float_tests_h
+
+int HalfPrecisionDecodeBasicTests(void);
+
+int DoubleAsSmallestTest(void);
+
+int HalfPrecisionAgainstRFCCodeTest(void);
+
+
+#endif /* float_tests_h */
diff --git a/lib/ext/qcbor/test/half_to_double_from_rfc7049.c b/lib/ext/qcbor/test/half_to_double_from_rfc7049.c
new file mode 100644
index 0000000..6380e51
--- /dev/null
+++ b/lib/ext/qcbor/test/half_to_double_from_rfc7049.c
@@ -0,0 +1,45 @@
+/*
+
+ Copyright (c) 2013 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+ */
+
+/*
+ This code is from RFC 7049. It is not used in the main implementation
+ because:
+ a) it adds a dependency on <math.h> and ldexp().
+ b) the license may be an issue
+
+ QCBOR does support half-precision, but rather than using
+ floating point math like this, it does it with bit shifting
+ and masking.
+
+ This code is here to test that code.
+
+ */
+
+#include "half_to_double_from_rfc7049.h"
+
+#include <math.h>
+
+double decode_half(unsigned char *halfp) {
+ int half = (halfp[0] << 8) + halfp[1];
+ int exp = (half >> 10) & 0x1f;
+ int mant = half & 0x3ff;
+ double val;
+ if (exp == 0) val = ldexp(mant, -24);
+ else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
+ else val = mant == 0 ? INFINITY : NAN;
+ return half & 0x8000 ? -val : val;
+}
diff --git a/lib/ext/qcbor/test/half_to_double_from_rfc7049.h b/lib/ext/qcbor/test/half_to_double_from_rfc7049.h
new file mode 100644
index 0000000..9f69e35
--- /dev/null
+++ b/lib/ext/qcbor/test/half_to_double_from_rfc7049.h
@@ -0,0 +1,18 @@
+/*==============================================================================
+ half_to_double_from_rfc7049.h -- interface to IETF float conversion code.
+
+ Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created on 9/23/18
+ ==============================================================================*/
+
+#ifndef half_to_double_from_rfc7049_h
+#define half_to_double_from_rfc7049_h
+
+double decode_half(unsigned char *halfp);
+
+#endif /* half_to_double_from_rfc7049_h */
diff --git a/lib/ext/qcbor/test/qcbor_decode_tests.c b/lib/ext/qcbor/test/qcbor_decode_tests.c
new file mode 100644
index 0000000..38005c0
--- /dev/null
+++ b/lib/ext/qcbor/test/qcbor_decode_tests.c
@@ -0,0 +1,2836 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+#include "qcbor_decode_tests.h"
+#include "qcbor.h"
+#include <string.h>
+#include <math.h> // for fabs()
+
+
+#ifdef PRINT_FUNCTIONS_FOR_DEBUGGING
+#include <stdio.h>
+
+static void PrintUsefulBufC(const char *szLabel, UsefulBufC Buf)
+{
+ if(szLabel) {
+ printf("%s ", szLabel);
+ }
+
+ size_t i;
+ for(i = 0; i < Buf.len; i++) {
+ uint8_t Z = ((uint8_t *)Buf.ptr)[i];
+ printf("%02x ", Z);
+ }
+ printf("\n");
+
+ fflush(stdout);
+}
+
+/*static void printencoded(const char *szLabel, const uint8_t *pEncoded, size_t nLen)
+{
+ PrintUsefulBufC(szLabel, (UsefulBufC){pEncoded, nLen});
+}*/
+#endif
+
+
+static const uint8_t spExpectedEncodedInts[] = {
+ 0x98, 0x2f, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3b, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x3a, 0xff, 0xff, 0xff,
+ 0xff, 0x3a, 0xff, 0xff, 0xff, 0xfe, 0x3a, 0xff,
+ 0xff, 0xff, 0xfd, 0x3a, 0x7f, 0xff, 0xff, 0xff,
+ 0x3a, 0x7f, 0xff, 0xff, 0xfe, 0x3a, 0x00, 0x01,
+ 0x00, 0x01, 0x3a, 0x00, 0x01, 0x00, 0x00, 0x39,
+ 0xff, 0xff, 0x39, 0xff, 0xfe, 0x39, 0xff, 0xfd,
+ 0x39, 0x01, 0x00, 0x38, 0xff, 0x38, 0xfe, 0x38,
+ 0xfd, 0x38, 0x18, 0x37, 0x36, 0x20, 0x00, 0x00,
+ 0x01, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19, 0x18,
+ 0x1a, 0x18, 0xfe, 0x18, 0xff, 0x19, 0x01, 0x00,
+ 0x19, 0x01, 0x01, 0x19, 0xff, 0xfe, 0x19, 0xff,
+ 0xff, 0x1a, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00,
+ 0x01, 0x00, 0x01, 0x1a, 0x00, 0x01, 0x00, 0x02,
+ 0x1a, 0x7f, 0xff, 0xff, 0xff, 0x1a, 0x7f, 0xff,
+ 0xff, 0xff, 0x1a, 0x80, 0x00, 0x00, 0x00, 0x1a,
+ 0x80, 0x00, 0x00, 0x01, 0x1a, 0xff, 0xff, 0xff,
+ 0xfe, 0x1a, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1b,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff};
+
+
+// return CBOR error or -1 if type of value doesn't match
+
+static int IntegerValuesParseTestInternal(QCBORDecodeContext *pDCtx)
+{
+ QCBORItem Item;
+ int nCBORError;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_ARRAY)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 || // Todo; fix this for 32-bit machines
+ Item.val.int64 != -9223372036854775807LL - 1)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -4294967297)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -4294967296)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -4294967295)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -4294967294)
+ return -1;
+
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -2147483648)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -2147483647)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -65538)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -65537)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -65536)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -65535)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -65534)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -257)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -256)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -255)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -254)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -25)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -24)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -23)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -1)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 0)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 0)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 1)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 22)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 23)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 24)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 25)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 26)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 254)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 255)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 256)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 257)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 65534)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 65535)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 65536)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 65537)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 65538)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 2147483647)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 2147483647)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 2147483648)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 2147483649)
+ return -1;
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 4294967294)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 4294967295)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 4294967296)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 4294967297)
+ return -1;
+
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 9223372036854775807LL)
+ return -1;
+
+
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_UINT64 ||
+ Item.val.uint64 != 18446744073709551615ULL)
+ return -1;
+
+
+ if(QCBORDecode_Finish(pDCtx) != QCBOR_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ Tests the decoding of lots of different integers sizes
+ and values.
+ */
+
+int IntegerValuesParseTest()
+{
+ int n;
+ QCBORDecodeContext DCtx;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedEncodedInts), QCBOR_DECODE_MODE_NORMAL);
+
+ n = IntegerValuesParseTestInternal(&DCtx);
+
+ return(n);
+}
+
+
+/*
+ Creates a simple CBOR array and returns it in *pEncoded. The array is malloced
+ and needs to be freed. This is used by several tests.
+
+ Two of the inputs can be set. Two other items in the array are fixed.
+
+ */
+
+static uint8_t spSimpleArrayBuffer[50];
+
+static int CreateSimpleArray(int nInt1, int nInt2, uint8_t **pEncoded, size_t *pEncodedLen)
+{
+ QCBOREncodeContext ECtx;
+ int nReturn = -1;
+
+ *pEncoded = NULL;
+ *pEncodedLen = INT32_MAX;
+
+ // loop runs CBOR encoding twice. First with no buffer to
+ // calculate the length so buffer can be allocated correctly,
+ // and last with the buffer to do the actual encoding
+ do {
+ QCBOREncode_Init(&ECtx, (UsefulBuf){*pEncoded, *pEncodedLen});
+ QCBOREncode_OpenArray(&ECtx);
+ QCBOREncode_AddInt64(&ECtx, nInt1);
+ QCBOREncode_AddInt64(&ECtx, nInt2);
+ QCBOREncode_AddBytes(&ECtx, ((UsefulBufC) {"galactic", 8}));
+ QCBOREncode_AddBytes(&ECtx, ((UsefulBufC) {"haven token", 11}));
+ QCBOREncode_CloseArray(&ECtx);
+
+ if(QCBOREncode_FinishGetSize(&ECtx, pEncodedLen))
+ goto Done;
+
+ if(*pEncoded != NULL) {
+ nReturn = 0;
+ goto Done;
+ }
+
+ // Use static buffer to avoid dependency on malloc()
+ if(*pEncodedLen > sizeof(spSimpleArrayBuffer)) {
+ goto Done;
+ }
+ *pEncoded = spSimpleArrayBuffer;
+
+ } while(1);
+
+Done:
+ return nReturn;
+}
+
+
+/*
+ {"first integer": 42,
+ "an array of two strings": [
+ "string1", "string2"
+ ],
+ "map in a map": {
+ "bytes 1": h'78787878',
+ "bytes 2": h'79797979',
+ "another int": 98,
+ "text 2": "lies, damn lies and statistics"
+ }
+ }
+ */
+
+static uint8_t pValidMapEncoded[] = {
+ 0xa3, 0x6d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x69, 0x6e,
+ 0x74, 0x65, 0x67, 0x65, 0x72, 0x18, 0x2a, 0x77, 0x61, 0x6e,
+ 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x77, 0x6f, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x73, 0x82, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31,
+ 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x32, 0x6c, 0x6d,
+ 0x61, 0x70, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x6d, 0x61,
+ 0x70, 0xa4, 0x67, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x31,
+ 0x44, 0x78, 0x78, 0x78, 0x78, 0x67, 0x62, 0x79, 0x74, 0x65,
+ 0x73, 0x20, 0x32, 0x44, 0x79, 0x79, 0x79, 0x79, 0x6b, 0x61,
+ 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x74,
+ 0x18, 0x62, 0x66, 0x74, 0x65, 0x78, 0x74, 0x20, 0x32, 0x78,
+ 0x1e, 0x6c, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x6d,
+ 0x6e, 0x20, 0x6c, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63,
+ 0x73 } ;
+
+static int ParseOrderedArray(const uint8_t *pEncoded, size_t nLen, int64_t *pInt1, int64_t *pInt2, const uint8_t **pBuf3, size_t *pBuf3Len, const uint8_t **pBuf4, size_t *pBuf4Len)
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nReturn = -1; // assume error until success
+
+ QCBORDecode_Init(&DCtx, (UsefulBufC){pEncoded, nLen}, QCBOR_DECODE_MODE_NORMAL);
+
+ // Make sure the first thing is a map
+ if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_ARRAY)
+ goto Done;
+
+ // First integer
+ if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_INT64)
+ goto Done;
+ *pInt1 = Item.val.int64;
+
+ // Second integer
+ if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_INT64)
+ goto Done;
+ *pInt2 = Item.val.int64;
+
+ // First string
+ if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_BYTE_STRING)
+ goto Done;
+ *pBuf3 = Item.val.string.ptr;
+ *pBuf3Len = Item.val.string.len;
+
+ // Second string
+ if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_BYTE_STRING)
+ goto Done;
+ *pBuf4 = Item.val.string.ptr;
+ *pBuf4Len = Item.val.string.len;
+
+ nReturn = 0;
+
+Done:
+ return(nReturn);
+}
+
+
+
+
+int SimpleArrayTest()
+{
+ uint8_t *pEncoded;
+ size_t nEncodedLen;
+
+ int64_t i1=0, i2=0;
+ size_t i3=0, i4=0;
+ const uint8_t *s3= (uint8_t *)"";
+ const uint8_t *s4= (uint8_t *)"";
+
+
+ if(CreateSimpleArray(23, 6000, &pEncoded, &nEncodedLen) < 0) {
+ return(-1);
+ }
+
+ ParseOrderedArray(pEncoded, nEncodedLen, &i1, &i2, &s3, &i3, &s4, &i4);
+
+ if(i1 != 23 ||
+ i2 != 6000 ||
+ i3 != 8 ||
+ i4 != 11 ||
+ memcmp("galactic", s3, 8) !=0 ||
+ memcmp("haven token", s4, 11) !=0) {
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+
+static uint8_t spDeepArrays[] = {0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80};
+
+int ParseDeepArrayTest()
+{
+ QCBORDecodeContext DCtx;
+ int nReturn = 0;
+ int i;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spDeepArrays), QCBOR_DECODE_MODE_NORMAL);
+
+ for(i = 0; i < 10; i++) {
+ QCBORItem Item;
+
+ if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
+ Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.uNestingLevel != i) {
+ nReturn = -1;
+ break;
+ }
+ }
+
+ return(nReturn);
+}
+
+// Big enough to test nesting to the depth of 24
+static uint8_t spTooDeepArrays[] = {0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x80};
+
+int ParseTooDeepArrayTest()
+{
+ QCBORDecodeContext DCtx;
+ int nReturn = 0;
+ int i;
+ QCBORItem Item;
+
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTooDeepArrays), QCBOR_DECODE_MODE_NORMAL);
+
+ for(i = 0; i < QCBOR_MAX_ARRAY_NESTING1; i++) {
+
+ if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
+ Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.uNestingLevel != i) {
+ nReturn = -1;
+ break;
+ }
+ }
+
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_ARRAY_NESTING_TOO_DEEP)
+ nReturn = -1;
+
+ return(nReturn);
+}
+
+
+
+
+int ShortBufferParseTest()
+{
+ int nResult = 0;
+ QCBORDecodeContext DCtx;
+ int num;
+
+ for(num = sizeof(spExpectedEncodedInts)-1; num; num--) {
+ int n;
+
+ QCBORDecode_Init(&DCtx, (UsefulBufC){spExpectedEncodedInts, num}, QCBOR_DECODE_MODE_NORMAL);
+
+ n = IntegerValuesParseTestInternal(&DCtx);
+
+ //printf("Len %d, result: %d\n", num, n);
+
+ if(n != QCBOR_ERR_HIT_END) {
+ nResult = -1;
+ goto Done;
+ }
+ }
+Done:
+ return nResult;
+}
+
+
+
+int ShortBufferParseTest2()
+{
+ uint8_t *pEncoded;
+ int nReturn;
+ size_t nEncodedLen;
+
+ int64_t i1, i2;
+ size_t i3, i4;
+ const uint8_t *s3, *s4;
+
+ nReturn = 0;
+
+ if(CreateSimpleArray(23, 6000, &pEncoded, &nEncodedLen) < 0) {
+ return(-1);
+ }
+
+ for(nEncodedLen--; nEncodedLen; nEncodedLen--) {
+ int nResult = ParseOrderedArray(pEncoded, (uint32_t)nEncodedLen, &i1, &i2, &s3, &i3, &s4, &i4);
+ if(nResult == 0) {
+ nReturn = -1;
+ }
+ }
+
+ return(nReturn);
+}
+
+/*
+ Decode and thoroughly check a moderately complex
+ set of maps
+ */
+static int ParseMapTest1(QCBORDecodeMode nMode)
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+ QCBORDecode_Init(&DCtx, (UsefulBufC){pValidMapEncoded, sizeof(pValidMapEncoded)}, nMode);
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ Item.val.uCount != 3)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 42 ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("first integer"))) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("an array of two strings")) ||
+ Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.val.uCount != 2)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string1"))) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string2"))) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("map in a map")) ||
+ Item.uDataType != QCBOR_TYPE_MAP ||
+ Item.val.uCount != 4) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("bytes 1"))||
+ Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("xxxx"))) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("bytes 2")) ||
+ Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("yyyy"))) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("another int")) ||
+ Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 98)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("text 2"))||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("lies, damn lies and statistics"))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ Decode and thoroughly check a moderately complex
+ set of maps
+ */
+int ParseMapAsArrayTest()
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded), QCBOR_DECODE_MODE_MAP_AS_ARRAY);
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP_AS_ARRAY ||
+ Item.val.uCount != 6) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ Item.uLabelType != QCBOR_TYPE_NONE ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("first integer"))) {
+ return -2;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 42 ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc) {
+ return -3;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("an array of two strings")) ||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return -4;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.val.uCount != 2) {
+ return -5;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.val.string.len != 7 ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string1"))) {
+ return -6;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string2"))) {
+ return -7;
+ }
+
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("map in a map"))) {
+ return -8;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ Item.uDataType != QCBOR_TYPE_MAP_AS_ARRAY ||
+ Item.val.uCount != 8) {
+ return -9;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("bytes 1"))||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc) {
+ return -10;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("xxxx"))) {
+ return -11;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("bytes 2")) ||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc) {
+ return -12;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("yyyy"))) {
+ return -13;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("another int")) ||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return -14;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 98) {
+ return -15;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("text 2"))||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc) {
+ return -16;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_NONE ||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataAlloc ||
+ Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("lies, damn lies and statistics"))) {
+ return -17;
+ }
+
+ return 0;
+}
+
+
+/*
+ Fully or partially decode pValidMapEncoded. When
+ partially decoding check for the right error code.
+ How much partial decoding depends on nLevel.
+
+ The partial decodes test error conditions of
+ incomplete encoded input.
+
+ This could be combined with the above test
+ and made prettier and maybe a little more
+ thorough.
+ */
+static int ExtraBytesTest(int nLevel)
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+ QCBORDecode_Init(&DCtx, (UsefulBufC){pValidMapEncoded, sizeof(pValidMapEncoded)}, QCBOR_DECODE_MODE_NORMAL);
+
+ if(nLevel < 1) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_EXTRA_BYTES) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ Item.val.uCount != 3)
+ return -1;
+
+ if(nLevel < 2) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.uCount != 42 ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("first integer"))) {
+ return -1;
+ }
+
+ if(nLevel < 3) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("an array of two strings")) ||
+ Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.val.uCount != 2) {
+ return -1;
+ }
+
+
+ if(nLevel < 4) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string1"))) {
+ return -1;
+ }
+
+ if(nLevel < 5) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string2"))) {
+ return -1;
+ }
+
+ if(nLevel < 6) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("map in a map")) ||
+ Item.uDataType != QCBOR_TYPE_MAP ||
+ Item.val.uCount != 4)
+ return -1;
+
+ if(nLevel < 7) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("bytes 1")) ||
+ Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("xxxx"))) {
+ return -1;
+ }
+
+ if(nLevel < 8) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("bytes 2")) ||
+ Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("yyyy"))) {
+ return -1;
+ }
+
+ if(nLevel < 9) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("another int")) ||
+ Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != 98)
+ return -1;
+
+ if(nLevel < 10) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+ return nCBORError;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("text 2"))||
+ Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("lies, damn lies and statistics"))) {
+ return -1;
+ }
+
+ if(QCBORDecode_Finish(&DCtx)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+int ParseMapTest()
+{
+ // Parse a moderatly complex map structure very thoroughl
+ int n = ParseMapTest1(QCBOR_DECODE_MODE_NORMAL);
+
+ n = ParseMapTest1(QCBOR_DECODE_MODE_MAP_STRINGS_ONLY);
+
+ if(!n) {
+ for(int i = 0; i < 10; i++) {
+ n = ExtraBytesTest(i);
+ if(n) {
+ break;
+ }
+ }
+ }
+
+ return(n);
+}
+
+
+static uint8_t spSimpleValues[] = {0x8a, 0xf4, 0xf5, 0xf6, 0xf7, 0xff, 0xe0, 0xf3, 0xf8, 0x00, 0xf8, 0x13, 0xf8, 0x1f, 0xf8, 0x20, 0xf8, 0xff};
+
+int ParseSimpleTest()
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSimpleValues), QCBOR_DECODE_MODE_NORMAL);
+
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.val.uCount != 10)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_FALSE)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_TRUE)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_NULL)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_UNDEF)
+ return -1;
+
+ // A break
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_BAD_BREAK)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 0)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 19)
+ return -1;
+
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_INVALID_CBOR)
+ return -1;
+
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_INVALID_CBOR)
+ return -1;
+
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_INVALID_CBOR)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 32)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 255)
+ return -1;
+
+ return 0;
+
+}
+
+
+struct FailInput {
+ UsefulBufC Input;
+ int nError;
+};
+
+
+struct FailInput Failures[] = {
+ { {(uint8_t[]){0x18}, 1}, QCBOR_ERR_HIT_END }, // 1 byte integer missing the byte
+ { {(uint8_t[]){0x1c}, 1}, QCBOR_ERR_UNSUPPORTED }, // Reserved additional info = 28
+ { {(uint8_t[]){0x1d}, 1}, QCBOR_ERR_UNSUPPORTED }, // Reserved additional info = 29
+ { {(uint8_t[]){0x1e}, 1}, QCBOR_ERR_UNSUPPORTED }, // Reserved additional info = 30
+ { {(uint8_t[]){0x1f}, 1}, QCBOR_ERR_UNSUPPORTED }, // Indefinite length integer
+ { {(uint8_t[]){0x3c}, 1}, QCBOR_ERR_UNSUPPORTED }, // 1 byte integer missing the byte
+ { {(uint8_t[]){0x3d}, 1}, QCBOR_ERR_UNSUPPORTED }, // 1 byte integer missing the byte
+ { {(uint8_t[]){0x3e}, 1}, QCBOR_ERR_UNSUPPORTED }, // 1 byte integer missing the byte
+ { {(uint8_t[]){0x3f}, 1}, QCBOR_ERR_UNSUPPORTED }, // Indefinite length negative integer
+ { {(uint8_t[]){0x41}, 1}, QCBOR_ERR_HIT_END }, // Short byte string
+ { {(uint8_t[]){0x5c}, 1}, QCBOR_ERR_UNSUPPORTED }, // Reserved additional info = 28
+ { {(uint8_t[]){0x5f}, 1}, QCBOR_ERR_UNSUPPORTED }, // Indefinite length byte string
+ { {(uint8_t[]){0x61}, 1}, QCBOR_ERR_HIT_END }, // Short UTF-8 string
+ { {(uint8_t[]){0x7c}, 1}, QCBOR_ERR_UNSUPPORTED }, // Reserved additional info = 28
+ { {(uint8_t[]){0x7f}, 1}, QCBOR_ERR_UNSUPPORTED }, // Indefinite length UTF-8 string
+ { {(uint8_t[]){0xff}, 1}, QCBOR_ERR_UNSUPPORTED } , // break
+ { {(uint8_t[]){0xf8, 0x00}, 2}, QCBOR_ERR_INVALID_CBOR }, // An invalid encoding of a simple type
+ { {(uint8_t[]){0xf8, 0x1f}, 2}, QCBOR_ERR_INVALID_CBOR }, // An invalid encoding of a simple type
+ { {(uint8_t[]){0xc0, 0x00}, 2}, QCBOR_ERR_BAD_OPT_TAG }, // Text-based date, with an integer
+ { {(uint8_t[]){0xc1, 0x41, 0x33}, 3}, QCBOR_ERR_BAD_OPT_TAG }, // Epoch date, with an byte string
+ { {(uint8_t[]){0xc1, 0xc0, 0x00}, 3}, QCBOR_ERR_BAD_OPT_TAG }, // tagged as both epoch and string dates
+ { {(uint8_t[]){0xc2, 0x00}, 2}, QCBOR_ERR_BAD_OPT_TAG } // big num tagged an int, not a byte string
+
+};
+
+
+int FailureTests()
+{
+ int nResult = 0;
+
+ struct FailInput *pFEnd = &Failures[0] + sizeof(Failures)/sizeof(struct FailInput);
+
+ for(struct FailInput *pF = &Failures[0]; pF < pFEnd ;pF++) {
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+ QCBORDecode_Init(&DCtx, pF->Input, QCBOR_DECODE_MODE_NORMAL);
+
+ while(1) {
+ nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+ if(QCBOR_ERR_HIT_END == nCBORError) {
+ break;
+ }
+ if(nCBORError != pF->nError) {
+ nResult = 1;
+ break;
+ }
+ }
+ }
+
+ {
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSimpleValues), QCBOR_DECODE_MODE_NORMAL);
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.val.uCount != 10)
+ return -1;
+
+ DCtx.InBuf.magic = 0; // Corrupt the UsefulInputBuf
+
+ nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+ if(nCBORError != QCBOR_ERR_HIT_END)
+ return -1;
+ }
+
+
+ return nResult;
+}
+
+
+/* Try all 256 values of the byte at nLen including recursing for
+ each of the values to try values at nLen+1 ... up to nLenMax
+ */
+static void ComprehensiveInputRecurser(uint8_t *pBuf, int nLen, int nLenMax)
+{
+ if(nLen >= nLenMax) {
+ return;
+ }
+
+ for(int inputByte = 0; inputByte < 256; inputByte++) {
+ // Set up the input
+ pBuf[nLen] = inputByte;
+ const UsefulBufC Input = {pBuf, nLen+1};
+
+ // Get ready to parse
+ QCBORDecodeContext DCtx;
+ QCBORDecode_Init(&DCtx, Input, QCBOR_DECODE_MODE_NORMAL);
+
+ // Parse by getting the next item until an error occurs
+ // Just about every possible decoder error can occur here
+ // The goal of this test is not to check for the correct
+ // error since that is not really possible. It is to
+ // see that there is no crash on hostile input.
+ while(1) {
+ QCBORItem Item;
+ QCBORError nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+ if(nCBORError != QCBOR_SUCCESS) {
+ break;
+ }
+ }
+
+ ComprehensiveInputRecurser(pBuf, nLen+1, nLenMax);
+ }
+}
+
+
+/*
+ Public function for initialization. See header qcbor.h
+ */
+int ComprehensiveInputTest()
+{
+ // Size 2 tests 64K inputs and runs quickly
+ uint8_t pBuf[2];
+
+ ComprehensiveInputRecurser(pBuf, 0, sizeof(pBuf));
+
+ return 0;
+}
+
+
+/*
+ Public function for initialization. See header qcbor.h
+ */
+int BigComprehensiveInputTest()
+{
+ // size 3 tests 16 million inputs and runs OK
+ // in seconds on fast machines. Size 4 takes
+ // 10+ minutes and 5 half a day on fast
+ // machines. This test is kept separate from
+ // the others so as to no slow down the use
+ // of them as a very frequent regression.
+ uint8_t pBuf[3]; //
+
+ ComprehensiveInputRecurser(pBuf, 0, sizeof(pBuf));
+
+ return 0;
+}
+
+
+static uint8_t spDateTestInput[] = {
+ 0xc0, // tag for string date
+ 0x6a, '1','9','8','5','-','0','4','-','1','2', // Date string
+
+ 0xc1, // tag for epoch date
+ 0x1a, 0x53, 0x72, 0x4E, 0x00, // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
+
+ // CBOR_TAG_B64
+ 0xc1, 0xcf, 0xd8, 0x22, // 0xee, // Epoch date with extra tags TODO: fix this test
+ 0x1a, 0x53, 0x72, 0x4E, 0x01,
+
+ 0xc1, // tag for epoch date
+ 0x1b, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // Too large integer
+
+ 0xc1, // tag for epoch date
+ 0xfa, 0x3f, 0x8c, 0xcc, 0xcd, // double with value 1.1
+
+ 0xc1, // tag for epoch date
+ 0xfa, 0x7f, 0x7f, 0xff, 0xff // 3.4028234663852886e+38 too large
+
+};
+
+
+// have to check float expected only to within an epsilon
+int CHECK_EXPECTED_DOUBLE(double val, double expected) {
+
+ double diff = val - expected;
+
+ diff = fabs(diff);
+
+ return diff > 0.0000001;
+}
+
+
+int DateParseTest()
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spDateTestInput), QCBOR_DECODE_MODE_NORMAL);
+
+ const uint64_t uTags[] = {15};
+ QCBORTagListIn TagList = {1, uTags};
+
+ QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TagList);
+
+ // String date
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_DATE_STRING ||
+ UsefulBuf_Compare(Item.val.dateString, UsefulBuf_FromSZ("1985-04-12"))){
+ return -2;
+ }
+
+ // Epoch date
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -3;
+ if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
+ Item.val.epochDate.nSeconds != 1400000000 ||
+ Item.val.epochDate.fSecondsFraction != 0 ) {
+ return -4;
+ }
+
+ // Epoch date with extra CBOR_TAG_B64 tag that doesn't really mean anything
+ // but want to be sure extra tag doesn't cause a problem
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -5;
+ if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
+ Item.val.epochDate.nSeconds != 1400000001 ||
+ Item.val.epochDate.fSecondsFraction != 0 ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_B64)) {
+ return -6;
+ }
+
+ // Epoch date that is too large for our representation
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
+ return -7;
+ }
+
+ // Epoch date in float format with fractional seconds
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -8;
+ if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
+ Item.val.epochDate.nSeconds != 1 ||
+ CHECK_EXPECTED_DOUBLE(Item.val.epochDate.fSecondsFraction, 0.1 )) {
+ return -9;
+ }
+
+ // Epoch date float that is too large for our representation
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
+ return -10;
+ }
+
+ // TODO: could use a few more tests with float, double, and half precsion and negative (but coverage is still pretty good)
+
+ return 0;
+}
+
+// Really simple basic input for tagging test
+static uint8_t spOptTestInput[] = {
+ 0xd9, 0xd9, 0xf7, // CBOR magic number
+ 0x81, // Array of one
+ 0xd8, 0x04, // non-preferred serialization of tag 4
+ 0x82, 0x01, 0x03}; // fraction 1/3
+
+static uint8_t spEncodedLargeTag[] = {0xdb, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x80};
+
+// 0x9192939495969798, 0x88, 0x01, 0x04
+static uint8_t spLotsOfTags[] = {0xdb, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xd8, 0x88, 0xc5, 0xc4, 0x80};
+
+/*
+ The cbor.me parse of this.
+ 55799(55799(55799({6(7(-23)): 5859837686836516696(7({7(-20): 11({17(-18): 17(17(17("Organization"))),
+ 9(-17): 773("SSG"), -15: 4(5(6(7(8(9(10(11(12(13(14(15("Confusion")))))))))))), 17(-16): 17("San Diego"),
+ 17(-14): 17("US")}), 23(-19): 19({-11: 9({-9: -7}),
+ 90599561(90599561(90599561(-10))): 12(h'0102030405060708090A')})})),
+ 16(-22): 23({11(8(7(-5))): 8(-3)})})))
+ */
+static uint8_t spCSRWithTags[] = {
+ 0xd9, 0xd9, 0xf7, 0xd9, 0xd9, 0xf7, 0xd9, 0xd9, 0xf7, 0xa2,
+ 0xc6, 0xc7, 0x36,
+ 0xdb, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0xc7, 0xa2,
+ 0xda, 0x00, 0x00, 0x00, 0x07, 0x33,
+ 0xcb, 0xa5,
+ 0xd1, 0x31,
+ 0xd1, 0xd1, 0xd1, 0x6c,
+ 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0xc9, 0x30,
+ 0xd9, 0x03, 0x05, 0x63,
+ 0x53, 0x53, 0x47,
+ 0x2e,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0x69,
+ 0x43, 0x6f, 0x6e, 0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e,
+ 0xd1, 0x2f,
+ 0xd1, 0x69,
+ 0x53, 0x61, 0x6e, 0x20, 0x44, 0x69, 0x65, 0x67, 0x6f,
+ 0xd1, 0x2d,
+ 0xd1, 0x62,
+ 0x55, 0x53,
+ 0xd7, 0x32,
+ 0xd3, 0xa2,
+ 0x2a,
+ 0xc9, 0xa1,
+ 0x28,
+ 0x26,
+ 0xda, 0x05, 0x66, 0x70, 0x89, 0xda, 0x05, 0x66, 0x70, 0x89, 0xda, 0x05, 0x66, 0x70, 0x89, 0x29,
+ 0xcc, 0x4a,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,0x07, 0x08, 0x09, 0x0a,
+ 0xd0, 0x35,
+ 0xd7, 0xa1,
+ 0xcb, 0xc8, 0xc7, 0x24,
+ 0xc8, 0x22};
+
+static int CheckCSRMaps(QCBORDecodeContext *pDC);
+
+
+int OptTagParseTest()
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spOptTestInput), QCBOR_DECODE_MODE_NORMAL);
+
+ //-------------------------
+ // This text matches the magic number tag and the fraction tag
+ if(QCBORDecode_GetNext(&DCtx, &Item)) {
+ return -2;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_CBOR_MAGIC)) {
+ return -3;
+ }
+
+ if(QCBORDecode_GetNext(&DCtx, &Item)) {
+ return -4;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_FRACTION) ||
+ Item.val.uCount != 2) {
+ return -5;
+ }
+
+ // --------------------------------
+ // This test decodes the very large tag, but it is not in
+ // any list so it is ignored.
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEncodedLargeTag), QCBOR_DECODE_MODE_NORMAL);
+ if(QCBORDecode_GetNext(&DCtx, &Item)) {
+ return -6;
+ }
+ if(Item.uTagBits) {
+ return -7;
+ }
+
+ // ----------------------------------
+ // This test sets up a caller-config list that includes the very large tage and then matches it.
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEncodedLargeTag), QCBOR_DECODE_MODE_NORMAL);
+ const uint64_t puList[] = {0x9192939495969798, 257};
+ const QCBORTagListIn TL = {2, puList};
+ QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TL);
+
+ if(QCBORDecode_GetNext(&DCtx, &Item)) {
+ return -8;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, 0x9192939495969798) ||
+ QCBORDecode_IsTagged(&DCtx, &Item, 257) ||
+ QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_BIGFLOAT) ||
+ Item.val.uCount != 0) {
+ return -9;
+ }
+
+ //------------------------
+ // This test sets up a caller-configured list, and looks up something not in it
+ const uint64_t puLongList[17] = {1,2,1};
+ const QCBORTagListIn TLLong = {17, puLongList};
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEncodedLargeTag), QCBOR_DECODE_MODE_NORMAL);
+ QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TLLong);
+ if(QCBORDecode_GetNext(&DCtx, &Item)) {
+ return -11;
+ }
+
+ // -----------------------
+ // This tests retrievel of the full tag list
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spLotsOfTags), QCBOR_DECODE_MODE_NORMAL);
+ uint64_t puTags[16];
+ QCBORTagListOut Out = {0, 4, puTags};
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -12;
+ }
+ if(puTags[0] != 0x9192939495969798 ||
+ puTags[1] != 0x88 ||
+ puTags[2] != 0x05 ||
+ puTags[3] != 0x04) {
+ return -13;
+ }
+
+ // ----------------------
+ // This text if too small of an out list
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spLotsOfTags), QCBOR_DECODE_MODE_NORMAL);
+ QCBORTagListOut OutSmall = {0, 3, puTags};
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &OutSmall) != QCBOR_ERR_TOO_MANY_TAGS) {
+ return -14;
+ }
+
+ // ---------------
+ // Parse a version of the "CSR" that has had a ton of tags randomly inserted
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRWithTags), QCBOR_DECODE_MODE_NORMAL);
+ int n = CheckCSRMaps(&DCtx);
+ if(n) {
+ return n-2000;
+ }
+
+ Out = (QCBORTagListOut){0,16, puTags};
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRWithTags), QCBOR_DECODE_MODE_NORMAL);
+
+ const uint64_t puTagList[] = {773, 1, 90599561};
+ const QCBORTagListIn TagList = {3, puTagList};
+ QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TagList);
+
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -100;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_CBOR_MAGIC) ||
+ QCBORDecode_IsTagged(&DCtx, &Item, 90599561) ||
+ QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_DATE_EPOCH) ||
+ Item.val.uCount != 2 ||
+ puTags[0] != CBOR_TAG_CBOR_MAGIC ||
+ puTags[1] != CBOR_TAG_CBOR_MAGIC ||
+ puTags[2] != CBOR_TAG_CBOR_MAGIC ||
+ Out.uNumUsed != 3) {
+ return -101;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -102;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_CBOR_MAGIC) ||
+ QCBORDecode_IsTagged(&DCtx, &Item, 6) ||
+ QCBORDecode_IsTagged(&DCtx, &Item, 7) || // item is tagged 7, but 7 is not configured to be recognized
+ Item.val.uCount != 2 ||
+ puTags[0] != 5859837686836516696 ||
+ puTags[1] != 7 ||
+ Out.uNumUsed != 2) {
+ return -103;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -104;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ Item.uTagBits ||
+ Item.val.uCount != 5 ||
+ puTags[0] != 0x0b ||
+ Out.uNumUsed != 1) {
+ return -105;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -106;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_COSE_MAC0) ||
+ Item.val.string.len != 12 ||
+ puTags[0] != CBOR_TAG_COSE_MAC0 ||
+ puTags[1] != CBOR_TAG_COSE_MAC0 ||
+ puTags[2] != CBOR_TAG_COSE_MAC0 ||
+ Out.uNumUsed != 3) {
+ return -105;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -107;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, 773) ||
+ Item.val.string.len != 3 ||
+ puTags[0] != 773 ||
+ Out.uNumUsed != 1) {
+ return -108;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -109;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, 4) ||
+ Item.val.string.len != 9 ||
+ puTags[0] != 4 ||
+ puTags[11] != 0x0f ||
+ Out.uNumUsed != 12) {
+ return -110;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -111;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, 17) ||
+ Item.val.string.len != 9 ||
+ puTags[0] != 17 ||
+ Out.uNumUsed != 1) {
+ return -112;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -111;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, 17) ||
+ Item.val.string.len != 2 ||
+ puTags[0] != 17 ||
+ Out.uNumUsed != 1) {
+ return -112;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -113;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ QCBORDecode_IsTagged(&DCtx, &Item, 19) ||
+ Item.val.uCount != 2 ||
+ puTags[0] != 19 ||
+ Out.uNumUsed != 1) {
+ return -114;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -115;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ QCBORDecode_IsTagged(&DCtx, &Item, 9) ||
+ Item.uTagBits ||
+ Item.val.uCount != 1 ||
+ puTags[0] != 9 ||
+ Out.uNumUsed != 1) {
+ return -116;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -116;
+ }
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.val.int64 != -7 ||
+ Item.uTagBits ||
+ Out.uNumUsed != 0) {
+ return -117;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -118;
+ }
+ if(Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+ Item.val.string.len != 10 ||
+ Item.uTagBits ||
+ puTags[0] != 12 ||
+ Out.uNumUsed != 1) {
+ return -119;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -120;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP ||
+ !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_ENC_AS_B16) ||
+ Item.val.uCount != 1 ||
+ puTags[0] != 0x17 ||
+ Out.uNumUsed != 1) {
+ return -121;
+ }
+
+ if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+ return -122;
+ }
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ QCBORDecode_IsTagged(&DCtx, &Item, 8) ||
+ Item.val.int64 != -3 ||
+ puTags[0] != 8 ||
+ Out.uNumUsed != 1) {
+ return -123;
+ }
+
+ if(QCBORDecode_Finish(&DCtx)) {
+ return -124;
+ }
+
+ return 0;
+}
+
+
+
+
+static uint8_t spBigNumInput[] = {
+ 0x83,
+ 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xA4,
+ 0x63, 0x42, 0x4E, 0x2B,
+ 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x40,
+ 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x63, 0x42, 0x4E, 0x2D,
+ 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x3F,
+ 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+
+static uint8_t spBigNum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+
+int BignumParseTest()
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ int nCBORError;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNumInput), QCBOR_DECODE_MODE_NORMAL);
+
+
+ //
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -1;
+ }
+
+ //
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_POSBIGNUM ||
+ UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
+ return -1;
+ }
+
+ //
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_NEGBIGNUM ||
+ UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
+ return -1;
+ }
+
+ //
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_POSBIGNUM ||
+ Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_POSBIGNUM ||
+ Item.uLabelType != QCBOR_TYPE_INT64 ||
+ Item.label.int64 != 64 ||
+ UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_NEGBIGNUM ||
+ Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
+ return -1;
+ }
+
+ if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+ return -1;
+ if(Item.uDataType != QCBOR_TYPE_NEGBIGNUM ||
+ Item.uLabelType != QCBOR_TYPE_INT64 ||
+ Item.label.int64 != -64 ||
+ UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+static int CheckItemWithIntLabel(QCBORDecodeContext *pCtx, uint8_t uDataType, uint8_t uNestingLevel, uint8_t uNextNest, int64_t nLabel, QCBORItem *pItem)
+{
+ QCBORItem Item;
+ int nCBORError;
+
+ if((nCBORError = QCBORDecode_GetNext(pCtx, &Item))) return -1;
+ if(Item.uDataType != uDataType) return -1;
+ if(uNestingLevel > 0) {
+ if(Item.uLabelType != QCBOR_TYPE_INT64 && Item.uLabelType != QCBOR_TYPE_UINT64) return -1;
+ if(Item.uLabelType == QCBOR_TYPE_INT64) {
+ if(Item.label.int64 != nLabel) return -1;
+ } else {
+ if(Item.label.uint64 != (uint64_t)nLabel) return -1;
+ }
+ }
+ if(Item.uNestingLevel != uNestingLevel) return -1;
+ if(Item.uNextNestLevel != uNextNest) return -1;
+
+ if(pItem) {
+ *pItem = Item;
+ }
+ return 0;
+}
+
+
+// Same code checks definite and indefinite length versions of the map
+static int CheckCSRMaps(QCBORDecodeContext *pDC)
+{
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 0, 1, 0, NULL)) return -1;
+
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 1, 2, -23, NULL)) return -1;
+
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 2, 3, -20, NULL)) return -1;
+
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -18, NULL)) return -1;
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -17, NULL)) return -1;
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -15, NULL)) return -1;
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -16, NULL)) return -1;
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 2, -14, NULL)) return -1;
+
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 2, 3, -19, NULL)) return -1;
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 3, 4, -11, NULL)) return -1;
+
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_INT64, 4, 3, -9, NULL)) return -1;
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_BYTE_STRING, 3, 1, -10, NULL)) return -1;
+
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 1, 2, -22, NULL)) return -1;
+ if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_INT64, 2, 0, -5, NULL)) return -1;
+
+ if(QCBORDecode_Finish(pDC)) return -2;
+
+ return 0;
+}
+
+
+/*
+// cbor.me decoded output
+{
+ -23: {
+ -20: {
+ -18: "Organization",
+ -17: "SSG",
+ -15: "Confusion",
+ -16: "San Diego",
+ -14: "US"
+ },
+ -19: {
+ -11: {
+ -9: -7
+ },
+ -10: '\u0001\u0002\u0003\u0004\u0005\u0006\a\b\t\n'
+ }
+ },
+ -22: {
+ -5: -3
+ }
+}
+ */
+
+
+static uint8_t spCSRInput[] = {
+ 0xa2, 0x36, 0xa2, 0x33, 0xa5, 0x31, 0x6c, 0x4f,
+ 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x30, 0x63, 0x53, 0x53, 0x47,
+ 0x2e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x75, 0x73,
+ 0x69, 0x6f, 0x6e, 0x2f, 0x69, 0x53, 0x61, 0x6e,
+ 0x20, 0x44, 0x69, 0x65, 0x67, 0x6f, 0x2d, 0x62,
+ 0x55, 0x53, 0x32, 0xa2, 0x2a, 0xa1, 0x28, 0x26,
+ 0x29, 0x4a, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x35, 0xa1, 0x24, 0x22};
+
+int NestedMapTest()
+{
+ QCBORDecodeContext DCtx;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRInput), QCBOR_DECODE_MODE_NORMAL);
+
+ return CheckCSRMaps(&DCtx);
+}
+
+
+
+int StringDecoderModeFailTest()
+{
+ QCBORDecodeContext DCtx;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRInput), QCBOR_DECODE_MODE_MAP_STRINGS_ONLY);
+
+ QCBORItem Item;
+ QCBORError nCBORError;
+
+ if(QCBORDecode_GetNext(&DCtx, &Item)) {
+ return -1;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return -2;
+ }
+
+ nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+ if(nCBORError != QCBOR_ERR_MAP_LABEL_TYPE) {
+ return -3;
+ }
+
+ return 0;
+}
+
+
+// Same map as above, but using indefinite lengths
+static uint8_t spCSRInputIndefLen[] = {
+ 0xbf, 0x36, 0xbf, 0x33, 0xbf, 0x31, 0x6c, 0x4f,
+ 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x30, 0x63, 0x53, 0x53, 0x47,
+ 0x2e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x75, 0x73,
+ 0x69, 0x6f, 0x6e, 0x2f, 0x69, 0x53, 0x61, 0x6e,
+ 0x20, 0x44, 0x69, 0x65, 0x67, 0x6f, 0x2d, 0x62,
+ 0x55, 0x53, 0xff, 0x32, 0xbf, 0x2a, 0xbf, 0x28,
+ 0x26, 0xff, 0x29, 0x4a, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xff, 0xff,
+ 0x35, 0xbf, 0x24, 0x22, 0xff, 0xff};
+
+int NestedMapTestIndefLen()
+{
+ QCBORDecodeContext DCtx;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRInputIndefLen), QCBOR_DECODE_MODE_NORMAL);
+
+ return CheckCSRMaps(&DCtx);
+}
+
+
+
+static UsefulBufC make_nested_indefinite_arrays(int n, UsefulBuf Storage)
+{
+ UsefulOutBuf UOB;
+ UsefulOutBuf_Init(&UOB, Storage);
+
+ int i;
+ for(i = 0; i < n; i++) {
+ UsefulOutBuf_AppendByte(&UOB, 0x9f);
+ }
+
+ for(i = 0; i < n; i++) {
+ UsefulOutBuf_AppendByte(&UOB, 0xff);
+ }
+ return UsefulOutBuf_OutUBuf(&UOB);
+}
+
+
+static int parse_indeflen_nested(UsefulBufC Nested, int nNestLevel)
+{
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, Nested, 0);
+
+ int j;
+ for(j = 0; j < nNestLevel; j++) {
+ QCBORItem Item;
+ int nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(j >= QCBOR_MAX_ARRAY_NESTING) {
+ // Should be in error
+ if(nReturn != QCBOR_ERR_ARRAY_NESTING_TOO_DEEP) {
+ return -4;
+ } else {
+ return 0; // Decoding doesn't recover after an error
+ }
+ } else {
+ // Should be no error
+ if(nReturn) {
+ return -9; // Should not have got an error
+ }
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -7;
+ }
+ }
+ int nReturn = QCBORDecode_Finish(&DC);
+ if(nReturn) {
+ return -3;
+ }
+ return 0;
+}
+
+
+int IndefiniteLengthNestTest()
+{
+ UsefulBuf_MAKE_STACK_UB(Storage, 50);
+ int i;
+ for(i=1; i < QCBOR_MAX_ARRAY_NESTING+4; i++) {
+ const UsefulBufC Nested = make_nested_indefinite_arrays(i, Storage);
+ int nReturn = parse_indeflen_nested(Nested, i);
+ if(nReturn) {
+ return nReturn;
+ }
+ }
+ return 0;
+}
+
+
+
+static const uint8_t spIndefiniteArray[] = {0x9f, 0x01, 0x82, 0x02, 0x03, 0xff}; // [1, [2, 3]]
+static const uint8_t spIndefiniteArrayBad1[] = {0x9f}; // No closing break
+static const uint8_t spIndefiniteArrayBad2[] = {0x9f, 0x9f, 0x02, 0xff}; // Not enough closing breaks
+static const uint8_t spIndefiniteArrayBad3[] = {0x9f, 0x02, 0xff, 0xff}; // Too many closing breaks
+static const uint8_t spIndefiniteArrayBad4[] = {0x81, 0x9f}; // Unclosed indeflen inside def len
+static const uint8_t spIndefiniteArrayBad5[] = {0x9f, 0xd1, 0xff}; // confused tag
+
+int IndefiniteLengthArrayMapTest()
+{
+ int nResult;
+ // --- first test -----
+ UsefulBufC IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteArray);
+
+ // Decode it and see if it is OK
+ UsefulBuf_MAKE_STACK_UB(MemPool, 150);
+ QCBORDecodeContext DC;
+ QCBORItem Item;
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+ QCBORDecode_GetNext(&DC, &Item);
+
+ if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.uNestingLevel != 0 ||
+ Item.uNextNestLevel != 1) {
+ return -111;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.uNestingLevel != 1 ||
+ Item.uNextNestLevel != 1) {
+ return -2;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+ Item.uNestingLevel != 1 ||
+ Item.uNextNestLevel != 2) {
+ return -3;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.uNestingLevel != 2 ||
+ Item.uNextNestLevel != 2) {
+ return -4;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.uNestingLevel != 2 ||
+ Item.uNextNestLevel != 0) {
+ return -5;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -6;
+ }
+
+ // --- next test -----
+ IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteArrayBad1);
+
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -7;
+ }
+
+ nResult = QCBORDecode_Finish(&DC);
+ if(nResult != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -8;
+ }
+
+
+ // --- next test -----
+ IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteArrayBad2);
+
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -9;
+ }
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -10;
+ }
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_INT64) {
+ return -11;
+ }
+
+ nResult = QCBORDecode_Finish(&DC);
+ if(nResult != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -12;
+ }
+
+
+ // --- next test -----
+ IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteArrayBad3);
+
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -13;
+ }
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult != QCBOR_ERR_BAD_BREAK) {
+ return -14;
+ }
+
+
+ // --- next test -----
+ IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteArrayBad4);
+
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -15;
+ }
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -16;
+ }
+
+ nResult = QCBORDecode_Finish(&DC);
+ if(nResult != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ return -17;
+ }
+
+ // --- next test -----
+ IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteArrayBad5);
+
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult || Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -18;
+ }
+
+ nResult = QCBORDecode_GetNext(&DC, &Item);
+ if(nResult != QCBOR_ERR_BAD_BREAK) {
+ return -19;
+ }
+
+ return 0;
+}
+
+
+static const uint8_t spIndefiniteLenString[] = {
+ 0x81, // Array of length one
+ 0x7f, // text string marked with indefinite length
+ 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, // first segment
+ 0x64, 0x6d, 0x69, 0x6e, 0x67, // second segment
+ 0xff // ending break
+};
+
+static const uint8_t spIndefiniteLenStringBad2[] = {
+ 0x81, // Array of length one
+ 0x7f, // text string marked with indefinite length
+ 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, // first segment
+ 0x44, 0x6d, 0x69, 0x6e, 0x67, // second segment of wrong type
+ 0xff // ending break
+};
+
+static const uint8_t spIndefiniteLenStringBad3[] = {
+ 0x81, // Array of length one
+ 0x7f, // text string marked with indefinite length
+ 0x01, 0x02, // Not a string
+ 0xff // ending break
+};
+
+static const uint8_t spIndefiniteLenStringBad4[] = {
+ 0x81, // Array of length one
+ 0x7f, // text string marked with indefinite length
+ 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, // first segment
+ 0x64, 0x6d, 0x69, 0x6e, 0x67, // second segment
+ // missing end of string
+};
+
+static const uint8_t spIndefiniteLenStringLabel[] = {
+ 0xa1, // Array of length one
+ 0x7f, // text string marked with indefinite length
+ 0x65, 0x73, 0x74, 0x72, 0x75, 0x75, // first segment
+ 0x64, 0x6d, 0x69, 0x6e, 0x67, // second segment
+ 0xff, // ending break
+ 0x01 // integer being labeled.
+};
+
+static UsefulBufC MakeIndefiniteBigBstr(UsefulBuf Storage) // TODO: size this
+{
+ UsefulOutBuf UOB;
+
+ UsefulOutBuf_Init(&UOB, Storage);
+ UsefulOutBuf_AppendByte(&UOB, 0x81);
+ UsefulOutBuf_AppendByte(&UOB, 0x5f);
+
+ int i = 0;
+ for(int nChunkSize = 1; nChunkSize <= 128; nChunkSize *= 2) {
+ UsefulOutBuf_AppendByte(&UOB, 0x58);
+ UsefulOutBuf_AppendByte(&UOB, (uint8_t)nChunkSize);
+ for(int j = 0; j < nChunkSize; j++ ) {
+ UsefulOutBuf_AppendByte(&UOB, i);
+ i++;
+ }
+ }
+ UsefulOutBuf_AppendByte(&UOB, 0xff);
+
+ return UsefulOutBuf_OutUBuf(&UOB);
+}
+
+static int CheckBigString(UsefulBufC BigString)
+{
+ if(BigString.len != 255) {
+ return 1;
+ }
+
+ for(uint8_t i = 0; i < 255; i++){
+ if(((const uint8_t *)BigString.ptr)[i] != i) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+int IndefiniteLengthStringTest()
+{
+ QCBORDecodeContext DC;
+ QCBORItem Item;
+ // big enough for MakeIndefiniteBigBstr() + MemPool overhead
+ UsefulBuf_MAKE_STACK_UB(MemPool, 350);
+
+ // --- Simple normal indefinite length string ------
+ UsefulBufC IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteLenString);
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ if(QCBORDecode_SetMemPool(&DC, MemPool, false)) {
+ return -1;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)) {
+ return -2;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY || Item.uDataAlloc) {
+ return -3;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)) {
+ return -4;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING || !Item.uDataAlloc) {
+ return -5;
+ }
+ if(QCBORDecode_Finish(&DC)) {
+ return -6;
+ }
+
+ // ----- types mismatch ---
+ QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteLenStringBad2), QCBOR_DECODE_MODE_NORMAL);
+
+ if(QCBORDecode_SetMemPool(&DC, MemPool, false)) {
+ return -7;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)) {
+ return -8;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -9;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item) != QCBOR_ERR_INDEFINITE_STRING_CHUNK) {
+ return -10;
+ }
+
+ // ----- not a string ---
+ QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteLenStringBad3), QCBOR_DECODE_MODE_NORMAL);
+
+ if(QCBORDecode_SetMemPool(&DC, MemPool, false)) {
+ return -11;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)) {
+ return -12;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -13;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item) != QCBOR_ERR_INDEFINITE_STRING_CHUNK) {
+ return -14;
+ }
+
+ // ----- no end -----
+ QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteLenStringBad4), QCBOR_DECODE_MODE_NORMAL);
+
+ if(QCBORDecode_SetMemPool(&DC, MemPool, false)) {
+ return -15;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)) {
+ return -16;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -17;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item) != QCBOR_ERR_HIT_END) {
+ return -18;
+ }
+
+ // ------ Don't set a string allocator and see an error -----
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -19;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item) != QCBOR_ERR_NO_STRING_ALLOCATOR) {
+ return -20;
+ }
+
+ // ----- Mempool is way too small -----
+ UsefulBuf_MAKE_STACK_UB(MemPoolTooSmall, 20); // 20 is too small no matter what
+
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+ if(!QCBORDecode_SetMemPool(&DC, MemPoolTooSmall, false)) {
+ return -21;
+ }
+
+ // ----- Mempool is way too small -----
+ UsefulBuf_MAKE_STACK_UB(BigIndefBStrStorage, 290);
+ const UsefulBufC BigIndefBStr = MakeIndefiniteBigBstr(BigIndefBStrStorage);
+
+ UsefulBuf_MAKE_STACK_UB(MemPoolSmall, 80); // 80 is big enough for MemPool overhead, but not BigIndefBStr
+
+ QCBORDecode_Init(&DC, BigIndefBStr, QCBOR_DECODE_MODE_NORMAL);
+ if(QCBORDecode_SetMemPool(&DC, MemPoolSmall, false)) {
+ return -22;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -23;
+ }
+ if(QCBORDecode_GetNext(&DC, &Item) != QCBOR_ERR_STRING_ALLOCATE) {
+ return -24;
+ }
+
+ // ---- big bstr -----
+ QCBORDecode_Init(&DC, BigIndefBStr, QCBOR_DECODE_MODE_NORMAL);
+
+ if(QCBORDecode_SetMemPool(&DC, MemPool, false)) {
+ return -25;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)) {
+ return -26;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY || Item.uDataAlloc) {
+ return -26;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)) {
+ return -27;
+ }
+ if(Item.uDataType != QCBOR_TYPE_BYTE_STRING || !Item.uDataAlloc || Item.uNestingLevel != 1) {
+ return -28;
+ }
+ if(CheckBigString(Item.val.string)) {
+ return -3;
+ }
+ if(QCBORDecode_Finish(&DC)) {
+ return -29;
+ }
+
+ // --- label is an indefinite length string ------
+ QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteLenStringLabel), QCBOR_DECODE_MODE_NORMAL);
+
+ if(QCBORDecode_SetMemPool(&DC, MemPool, false)) {
+ return -30;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return -31;
+ }
+
+ if(QCBORDecode_GetNext(&DC, &Item)){
+ return -32;
+ }
+ if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING || Item.uDataType != QCBOR_TYPE_INT64 ||
+ Item.uDataAlloc || !Item.uLabelAlloc ||
+ UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("struuming"))) {
+ return -33;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -34;
+ }
+
+ return 0;
+}
+
+
+int AllocAllStringsTest()
+{
+ QCBORDecodeContext DC;
+ QCBORError nCBORError;
+
+
+ // First test, use the "CSRMap" as easy input and checking
+ QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRInput), QCBOR_DECODE_MODE_NORMAL);
+
+ UsefulBuf_MAKE_STACK_UB(Pool, sizeof(spCSRInput) + QCBOR_DECODE_MIN_MEM_POOL_SIZE);
+
+ nCBORError = QCBORDecode_SetMemPool(&DC, Pool, 1); // Turn on copying.
+ if(nCBORError) {
+ return -1;
+ }
+
+ if(CheckCSRMaps(&DC)) {
+ return -2;
+ }
+
+ // Next parse, save pointers to a few strings, destroy original and see all is OK.
+ UsefulBuf_MAKE_STACK_UB(CopyOfStorage, sizeof(pValidMapEncoded) + QCBOR_DECODE_MIN_MEM_POOL_SIZE);
+ const UsefulBufC CopyOf = UsefulBuf_Copy(CopyOfStorage, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded));
+
+ QCBORDecode_Init(&DC, CopyOf, QCBOR_DECODE_MODE_NORMAL);
+ UsefulBuf_Set(Pool, '/');
+ QCBORDecode_SetMemPool(&DC, Pool, 1); // Turn on copying.
+
+ QCBORItem Item1, Item2, Item3, Item4;
+ if((nCBORError = QCBORDecode_GetNext(&DC, &Item1)))
+ return nCBORError;
+ if(Item1.uDataType != QCBOR_TYPE_MAP ||
+ Item1.val.uCount != 3)
+ return -3;
+ if((nCBORError = QCBORDecode_GetNext(&DC, &Item1)))
+ return nCBORError;
+ if((nCBORError = QCBORDecode_GetNext(&DC, &Item2)))
+ return nCBORError;
+ if((nCBORError = QCBORDecode_GetNext(&DC, &Item3)))
+ return nCBORError;
+ if((nCBORError = QCBORDecode_GetNext(&DC, &Item4)))
+ return nCBORError;
+
+ UsefulBuf_Set(CopyOfStorage, '_');
+
+ if(Item1.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ Item1.uDataType != QCBOR_TYPE_INT64 ||
+ Item1.val.int64 != 42 ||
+ UsefulBuf_Compare(Item1.label.string, UsefulBuf_FromSZ("first integer"))) {
+ return -4;
+ }
+
+
+ if(Item2.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item2.label.string, UsefulBuf_FromSZ("an array of two strings")) ||
+ Item2.uDataType != QCBOR_TYPE_ARRAY ||
+ Item2.val.uCount != 2)
+ return -5;
+
+ if(Item3.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item3.val.string, UsefulBuf_FromSZ("string1"))) {
+ return -6;
+ }
+
+ if(Item4.uDataType != QCBOR_TYPE_TEXT_STRING ||
+ UsefulBuf_Compare(Item4.val.string, UsefulBuf_FromSZ("string2"))) {
+ return -7;
+ }
+
+ // Next parse with a pool that is too small
+ UsefulBuf_MAKE_STACK_UB(SmallPool, QCBOR_DECODE_MIN_MEM_POOL_SIZE + 1);
+ QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded), QCBOR_DECODE_MODE_NORMAL);
+ QCBORDecode_SetMemPool(&DC, SmallPool, 1); // Turn on copying.
+ if((nCBORError = QCBORDecode_GetNext(&DC, &Item1)))
+ return -8;
+ if(Item1.uDataType != QCBOR_TYPE_MAP ||
+ Item1.val.uCount != 3) {
+ return -9;
+ }
+ if(!(nCBORError = QCBORDecode_GetNext(&DC, &Item1))){
+ if(!(nCBORError = QCBORDecode_GetNext(&DC, &Item2))) {
+ if(!(nCBORError = QCBORDecode_GetNext(&DC, &Item3))) {
+ nCBORError = QCBORDecode_GetNext(&DC, &Item4);
+ }
+ }
+ }
+ if(nCBORError != QCBOR_ERR_STRING_ALLOCATE) {
+ return -10;
+ }
+
+ return 0;
+}
+
+// Cheating declaration to get to the special test hook
+size_t MemPoolTestHook_GetPoolSize(void *ctx);
+
+
+int MemPoolTest(void)
+{
+ // Set up the decoder with a tiny bit of CBOR to parse
+ QCBORDecodeContext DC;
+ const uint8_t pMinimalCBOR[] = {0xa0}; // One empty map
+ QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pMinimalCBOR),0);
+
+ // Set up an memory pool of 100 bytes
+ UsefulBuf_MAKE_STACK_UB(Pool, 100);
+ QCBORError nError = QCBORDecode_SetMemPool(&DC, Pool, 0);
+ if(nError) {
+ return -9;
+ }
+
+ // Cheat a little to get to the string allocator object
+ // so we can call it directly to test it
+ QCBORStringAllocator *pAlloc = (QCBORStringAllocator *)DC.pStringAllocator;
+ // Cheat some more to know exactly the
+ size_t uAvailPool = MemPoolTestHook_GetPoolSize(pAlloc);
+
+ // First test -- ask for too much in one go
+ UsefulBuf Allocated = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, NULL, uAvailPool+1);
+ if(!UsefulBuf_IsNULL(Allocated)) {
+ return -1;
+ }
+
+
+ // Re do the set up for the next test that will do a successful alloc,
+ // a fail, a free and then success
+ // This test should work on 32 and 64-bit machines if the compiler
+ // does the expected thing with pointer sizes for the internal
+ // MemPool implementation leaving 44 or 72 bytes of pool memory.
+ QCBORDecode_SetMemPool(&DC, Pool, 0);
+
+ // Cheat a little to get to the string allocator object
+ // so we can call it directly to test it
+ pAlloc = (QCBORStringAllocator *)DC.pStringAllocator;
+ // Cheat some more to know exactly the
+ uAvailPool = MemPoolTestHook_GetPoolSize(pAlloc);
+
+ Allocated = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, NULL, uAvailPool-1);
+ if(UsefulBuf_IsNULL(Allocated)) { // expected to succeed
+ return -2;
+ }
+ UsefulBuf Allocated2 = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, NULL, uAvailPool/2);
+ if(!UsefulBuf_IsNULL(Allocated2)) { // expected to fail
+ return -3;
+ }
+ (*pAlloc->fFree)(pAlloc->pAllocaterContext, Allocated.ptr);
+ Allocated = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, NULL, uAvailPool/2);
+ if(UsefulBuf_IsNULL(Allocated)) { // succeed because of the free
+ return -4;
+ }
+
+
+ // Re do set up for next test that involves a successful alloc,
+ // and a successful realloc and a failed realloc
+ QCBORDecode_SetMemPool(&DC, Pool, 0);
+
+ // Cheat a little to get to the string allocator object
+ // so we can call it directly to test it
+ pAlloc = (QCBORStringAllocator *)DC.pStringAllocator;
+ Allocated = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, NULL, uAvailPool/2);
+ if(UsefulBuf_IsNULL(Allocated)) { // expected to succeed
+ return -5;
+ }
+ Allocated2 = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, Allocated.ptr, uAvailPool);
+ if(UsefulBuf_IsNULL(Allocated2)) {
+ return -6;
+ }
+ if(Allocated2.ptr != Allocated.ptr || Allocated2.len != uAvailPool) {
+ return -7;
+ }
+ UsefulBuf Allocated3 = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, Allocated.ptr, uAvailPool+1);
+ if(!UsefulBuf_IsNULL(Allocated3)) { // expected to fail
+ return -8;
+ }
+
+ return 0;
+}
+
diff --git a/lib/ext/qcbor/test/qcbor_decode_tests.h b/lib/ext/qcbor/test/qcbor_decode_tests.h
new file mode 100644
index 0000000..2b09c55
--- /dev/null
+++ b/lib/ext/qcbor/test/qcbor_decode_tests.h
@@ -0,0 +1,229 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+#ifndef __QCBOR__qcbort_decode_tests__
+#define __QCBOR__qcbort_decode_tests__
+
+#include "qcbor.h"
+
+
+/*
+ Notes:
+
+ - All the functions in qcbor.h are called once in the aggregation of all the tests below.
+
+ - All the types that are supported are given as input and parsed by these tests
+
+ - There is some hostile input such as invalid lengths and CBOR too complex
+ and types this parser doesn't handle
+
+ */
+
+
+
+
+/*
+ Parse a well-known set of integers including those around the boundaries and
+ make sure the expected values come out
+ */
+int IntegerValuesParseTest(void);
+
+
+
+
+
+/*
+ Decode a simple CBOR encoded array and make sure it returns all the correct values.
+ This is a decode test.
+ */
+int SimpleArrayTest(void);
+
+
+/*
+ Make sure a maximally deep array can be parsed and that the
+ reported nesting level is correct. This uses test vector
+ of CBOR encoded data with a depth of 10. This a parse test.
+ */
+int ParseDeepArrayTest(void);
+
+
+/*
+ See that the correct error is reported when parsing
+ an array of depth 11, one too large.
+ */
+int ParseTooDeepArrayTest(void);
+
+
+/*
+ Try to parse some legit CBOR types that this parsers
+ doesn't support.
+ */
+int UnsupportedCBORDecodeTest(void);
+
+
+/*
+ This takes the encoded CBOR integers used in the above test and parses
+ it over and over with one more byte less each time. It should fail
+ every time on incorrect CBOR input. This is a hostile input decode test.
+ */
+int ShortBufferParseTest(void);
+
+
+/*
+ Same as ShortBufferParseTest, but with a different encoded CBOR input.
+ It is another hostile input test
+ */
+int ShortBufferParseTest2(void);
+
+
+/*
+ Parses the somewhat complicated CBOR MAP and makes sure all the correct
+ values parse out. About 15 values are tested. This is a decode test.
+ */
+int ParseMapTest(void);
+
+
+
+int FloatValuesTest1(void);
+
+
+
+int SimpleValuesTest1(void);
+
+
+/*
+
+ */
+int ParseMapAsArrayTest(void);
+
+
+
+int ParseSimpleTest(void);
+
+
+
+/*
+ Tests a number of failure cases on bad CBOR to get the right error code
+ */
+int FailureTests(void);
+
+
+/*
+ Parses all possible inputs that are two bytes long. Main point
+ is that the test doesn't crash as it doesn't evaluate the
+ input for correctness in any way.
+
+ (Parsing all possible 3 byte strings takes too long on all but
+ very fast machines).
+ */
+int ComprehensiveInputTest(void);
+
+
+/*
+ Parses all possible inputs that are four bytes long. Main point
+ is that the test doesn't crash as it doesn't evaluate the
+ input for correctness in any way. This runs very slow, so it
+ is only practical as a once-in-a-while regression test on
+ fast machines.
+ */
+int BigComprehensiveInputTest(void);
+
+
+/*
+ Thest the date types -- epoch and strings
+ */
+int DateParseTest(void);
+
+
+/*
+ Test optional tags like the CBOR magic number.
+ */
+int OptTagParseTest(void);
+
+
+/*
+ Parse some big numbers, positive and negative
+ */
+int BignumParseTest(void);
+
+
+int StringDecoderModeFailTest(void);
+
+
+/*
+ Parse some nested maps
+ */
+int NestedMapTest(void);
+
+
+/*
+ Parse maps with indefinite lengths
+ */
+int NestedMapTestIndefLen(void);
+
+
+/*
+ Parse some maps and arrays with indefinite lengths.
+ Includes some error cases.
+ */
+int IndefiniteLengthArrayMapTest(void);
+
+
+/*
+ Parse indefinite length strings. Uses
+ MemPool. Includes error cases.
+ */
+int IndefiniteLengthStringTest(void);
+
+
+/*
+ Test deep nesting of indefinite length
+ maps and arrays including too deep.
+ */
+int IndefiniteLengthNestTest(void);
+
+
+/*
+ Test parsing strings were all strings, not
+ just indefinite length strings, are
+ allocated. Includes error test cases.
+ */
+int AllocAllStringsTest(void);
+
+
+/*
+ Direct test of MemPool string allocator
+ */
+int MemPoolTest(void);
+
+
+#endif /* defined(__QCBOR__qcbort_decode_tests__) */
diff --git a/lib/ext/qcbor/test/qcbor_encode_tests.c b/lib/ext/qcbor/test/qcbor_encode_tests.c
new file mode 100644
index 0000000..2a22cf1
--- /dev/null
+++ b/lib/ext/qcbor/test/qcbor_encode_tests.c
@@ -0,0 +1,2007 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+#include "qcbor.h"
+#include "qcbor_encode_tests.h"
+
+
+/*
+ This is the test set for CBOR encoding.
+
+ This is largely complete for the implemented.
+
+ A few more things to do include:
+ - Add a test for counting the top level items and adding it back in with AddRaw()
+ - Run on some different CPUs like 32-bit and maybe even 16-bit
+ - Test the large array count limit
+ - Add the CBOR diagnostic output for every expected
+
+ */
+
+//#define PRINT_FUNCTIONS_FOR_DEBUGGINGXX
+
+#ifdef PRINT_FUNCTIONS_FOR_DEBUGGINGXX
+#include <stdio.h>
+
+// ifdef these out to not have compiler warnings
+static void printencoded(const uint8_t *pEncoded, size_t nLen)
+{
+ size_t i;
+ for(i = 0; i < nLen; i++) {
+ uint8_t Z = pEncoded[i];
+ printf("%02x ", Z);
+ }
+ printf("\n");
+
+ fflush(stdout);
+}
+
+
+// Do the comparison and print out where it fails
+static int UsefulBuf_Compare_Print(UsefulBufC U1, UsefulBufC U2) {
+ size_t i;
+ for(i = 0; i < U1.len; i++) {
+ if(((uint8_t *)U1.ptr)[i] != ((uint8_t *)U2.ptr)[i]) {
+ printf("Position: %d Actual: 0x%x Expected: 0x%x\n", i, ((uint8_t *)U1.ptr)[i], ((uint8_t *)U2.ptr)[i]);
+ return 1;
+ }
+ }
+ return 0;
+
+}
+
+#define CheckResults(Enc, Expected) \
+ UsefulBuf_Compare_Print(Enc, (UsefulBufC){Expected, sizeof(Expected)})
+
+#else
+
+#define CheckResults(Enc, Expected) \
+ UsefulBuf_Compare(Enc, (UsefulBufC){Expected, sizeof(Expected)})
+
+#endif
+
+
+
+// One big buffer that is used by all the tests to encode into
+// Putting it in uninitialized data is better than using a lot
+// of stack. The tests should run on small devices too.
+static uint8_t spBigBuf[2200];
+
+
+
+/*
+ Some very minimal tests.
+ */
+int BasicEncodeTest()
+{
+ // Very simple CBOR, a map with one boolean that is true in it
+ QCBOREncodeContext EC;
+
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddBoolToMapN(&EC, 66, true);
+ QCBOREncode_CloseMap(&EC);
+
+ UsefulBufC Encoded;
+ if(QCBOREncode_Finish(&EC, &Encoded)) {
+ return -1;
+ }
+
+
+ // Decode it and see that is right
+ QCBORDecodeContext DC;
+ QCBORItem Item;
+ QCBORDecode_Init(&DC, Encoded, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return -2;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_TRUE) {
+ return -3;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -4;
+ }
+
+
+ // Make another encoded message with the CBOR from the previous put into this one
+ UsefulBuf_MAKE_STACK_UB(MemoryForEncoded2, 20);
+ QCBOREncode_Init(&EC, MemoryForEncoded2);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddUInt64(&EC, 451);
+ QCBOREncode_AddEncoded(&EC, Encoded);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddEncodedToMapN(&EC, -70000, Encoded);
+ QCBOREncode_CloseMap(&EC);
+ QCBOREncode_CloseArray(&EC);
+
+ UsefulBufC Encoded2;
+ if(QCBOREncode_Finish(&EC, &Encoded2)) {
+ return -5;
+ }
+ /*
+ [ // 0 1:3
+ 451, // 1 1:2
+ { // 1 1:2 2:1
+ 66: true // 2 1:1
+ },
+ { // 1 1:1 2:1
+ -70000: { // 2 1:1 2:1 3:1
+ 66: true // 3 XXXXXX
+ }
+ }
+ ]
+
+
+
+ 83 # array(3)
+ 19 01C3 # unsigned(451)
+ A1 # map(1)
+ 18 42 # unsigned(66)
+ F5 # primitive(21)
+ A1 # map(1)
+ 3A 0001116F # negative(69999)
+ A1 # map(1)
+ 18 42 # unsigned(66)
+ F5 # primitive(21)
+ */
+
+ // Decode it and see if it is OK
+ QCBORDecode_Init(&DC, Encoded2, QCBOR_DECODE_MODE_NORMAL);
+
+ // 0 1:3
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY || Item.val.uCount != 3) {
+ return -6;
+ }
+
+ // 1 1:2
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_INT64 || Item.val.uint64 != 451) {
+ return -7;
+ }
+
+ // 1 1:2 2:1
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 1) {
+ return -8;
+ }
+
+ // 2 1:1
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_TRUE) {
+ return -9;
+ }
+
+ // 1 1:1 2:1
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 1) {
+ return -10;
+ }
+
+ // 2 1:1 2:1 3:1
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 1 || Item.uLabelType != QCBOR_TYPE_INT64 || Item.label.int64 != -70000) {
+ return -11;
+ }
+
+ // 3 XXXXXX
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_TRUE || Item.uLabelType != QCBOR_TYPE_INT64 || Item.label.int64 != 66) {
+ return -12;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -13;
+ }
+
+ return 0;
+}
+
+
+
+static const uint8_t spExpectedEncodedAll[] = {
+ 0x98, 0x22, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
+ 0x64, 0x1a, 0x05, 0x5d, 0x23, 0x15, 0x65, 0x49, 0x4e, 0x54,
+ 0x36, 0x34, 0xd8, 0x4c, 0x1b, 0x00, 0x00, 0x00, 0x12, 0x16,
+ 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0xa4, 0x63, 0x4c, 0x42,
+ 0x4c, 0x18, 0x4d, 0x23, 0x18, 0x58, 0x78, 0x1a, 0x4e, 0x45,
+ 0x47, 0x4c, 0x42, 0x4c, 0x54, 0x48, 0x41, 0x54, 0x20, 0x49,
+ 0x53, 0x20, 0x4b, 0x49, 0x4e, 0x44, 0x20, 0x4f, 0x46, 0x20,
+ 0x4c, 0x4f, 0x4e, 0x47, 0x3b, 0x00, 0x00, 0x02, 0x2d, 0x9a,
+ 0xc6, 0x94, 0x55, 0x3a, 0x05, 0xf5, 0xe0, 0xff, 0x3a, 0x2f,
+ 0xaf, 0x07, 0xff, 0xc1, 0x1a, 0x8e, 0x15, 0x1c, 0x8a,
+ 0xa3, 0x74, 0x4c, 0x6f, 0x6e, 0x67, 0x4c, 0x69, 0x76, 0x65,
+ 0x44, 0x65, 0x6e, 0x69, 0x73, 0x52, 0x69, 0x74, 0x63, 0x68,
+ 0x69, 0x65, 0xc1, 0x1a, 0x53, 0x72, 0x4e, 0x00, 0x66, 0x74,
+ 0x69, 0x6d, 0x65, 0x28, 0x29, 0xc1, 0x1a, 0x58, 0x0d, 0x41,
+ 0x72, 0x39, 0x07, 0xb0, 0xc1, 0x1a, 0x58, 0x0d, 0x3f, 0x76,
+ 0x42, 0xff, 0x00, 0xa3, 0x66, 0x62, 0x69, 0x6e, 0x62, 0x69,
+ 0x6e, 0xda, 0x00, 0x01, 0x86, 0xa0, 0x41, 0x00, 0x66, 0x62,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x43, 0x01, 0x02, 0x03, 0x00,
+ 0x44, 0x04, 0x02, 0x03, 0xfe, 0x6f, 0x62, 0x61, 0x72, 0x20,
+ 0x62, 0x61, 0x72, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x62, 0x61,
+ 0x72, 0x64, 0x6f, 0x6f, 0x66, 0x0a, 0xd8, 0x20, 0x78, 0x6b,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x74, 0x61,
+ 0x63, 0x6b, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x32, 0x38, 0x30, 0x35, 0x39,
+ 0x36, 0x39, 0x37, 0x2f, 0x68, 0x6f, 0x77, 0x2d, 0x64, 0x6f,
+ 0x2d, 0x69, 0x2d, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x2d,
+ 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x2d, 0x64, 0x65,
+ 0x62, 0x75, 0x67, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2d, 0x62, 0x75, 0x69, 0x6c,
+ 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x2d, 0x78, 0x63, 0x6f, 0x64,
+ 0x65, 0x2d, 0x36, 0x2d, 0x37, 0x2d, 0x38, 0xd8, 0x22, 0x78,
+ 0x1c, 0x59, 0x57, 0x35, 0x35, 0x49, 0x47, 0x4e, 0x68, 0x63,
+ 0x6d, 0x35, 0x68, 0x62, 0x43, 0x42, 0x77, 0x62, 0x47, 0x56,
+ 0x68, 0x63, 0x33, 0x56, 0x79, 0x5a, 0x51, 0x3d, 0x3d, 0xd8,
+ 0x23, 0x67, 0x5b, 0x5e, 0x61, 0x62, 0x63, 0x5d, 0x2b, 0xd8,
+ 0x24, 0x79, 0x01, 0x57, 0x4d, 0x49, 0x4d, 0x45, 0x2d, 0x56,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x2e,
+ 0x30, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
+ 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x6d, 0x75, 0x6c, 0x74,
+ 0x69, 0x70, 0x61, 0x72, 0x74, 0x2f, 0x6d, 0x69, 0x78, 0x65,
+ 0x64, 0x3b, 0x0a, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72,
+ 0x79, 0x3d, 0x22, 0x58, 0x58, 0x58, 0x58, 0x62, 0x6f, 0x75,
+ 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x22, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
+ 0x20, 0x61, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61,
+ 0x72, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+ 0x20, 0x69, 0x6e, 0x20, 0x4d, 0x49, 0x4d, 0x45, 0x20, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x0a, 0x0a, 0x2d, 0x2d,
+ 0x58, 0x58, 0x58, 0x58, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61,
+ 0x72, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65,
+ 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61,
+ 0x69, 0x6e, 0x0a, 0x0a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79,
+ 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x0a, 0x2d, 0x2d, 0x58,
+ 0x58, 0x58, 0x58, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72,
+ 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x43, 0x6f, 0x6e,
+ 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a,
+ 0x20, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69,
+ 0x6e, 0x3b, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+ 0x2d, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69,
+ 0x6f, 0x6e, 0x3a, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68,
+ 0x6d, 0x65, 0x6e, 0x74, 0x3b, 0x0a, 0x66, 0x69, 0x6c, 0x65,
+ 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x73, 0x74,
+ 0x2e, 0x74, 0x78, 0x74, 0x22, 0x0a, 0x0a, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61,
+ 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+ 0x74, 0x65, 0x78, 0x74, 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58,
+ 0x58, 0x58, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79,
+ 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x2d, 0xae, 0x65, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x6f, 0x66, 0x6f, 0x6f, 0x20, 0x62,
+ 0x61, 0x72, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x66, 0x6f, 0x6f,
+ 0x64, 0x5f, 0x5f, 0x5f, 0x5f, 0x67, 0x66, 0x6f, 0x6f, 0x20,
+ 0x62, 0x61, 0x72, 0x66, 0x28, 0x29, 0x28, 0x29, 0x28, 0x29,
+ 0xd9, 0x03, 0xe8, 0x6b, 0x72, 0x61, 0x62, 0x20, 0x72, 0x61,
+ 0x62, 0x20, 0x6f, 0x6f, 0x66, 0x16, 0x6f, 0x66, 0x6f, 0x6f,
+ 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x66,
+ 0x6f, 0x6f, 0x62, 0x5e, 0x5e, 0x69, 0x6f, 0x6f, 0x6f, 0x6f,
+ 0x6f, 0x6f, 0x6f, 0x6f, 0x66, 0x18, 0x63, 0x6d, 0x66, 0x66,
+ 0x66, 0x66, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+ 0x66, 0x63, 0x52, 0x46, 0x43, 0xd8, 0x20, 0x78, 0x31, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x6f, 0x6f,
+ 0x6c, 0x73, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72,
+ 0x67, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2f, 0x72, 0x66, 0x63,
+ 0x37, 0x30, 0x34, 0x39, 0x23, 0x73, 0x65, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x2d, 0x32, 0x2e, 0x34, 0x2e, 0x35, 0x18, 0x89,
+ 0xd8, 0x20, 0x6f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x62, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x2f, 0x68, 0x77,
+ 0x68, 0x65, 0x6e, 0x69, 0x6d, 0x36, 0x34, 0xd8, 0x22, 0x6c,
+ 0x63, 0x47, 0x78, 0x6c, 0x59, 0x58, 0x4e, 0x31, 0x63, 0x6d,
+ 0x55, 0x75, 0x18, 0x40, 0xd8, 0x22, 0x68, 0x63, 0x33, 0x56,
+ 0x79, 0x5a, 0x53, 0x34, 0x3d, 0x64, 0x70, 0x6f, 0x70, 0x6f,
+ 0xd8, 0x23, 0x68, 0x31, 0x30, 0x30, 0x5c, 0x73, 0x2a, 0x6d,
+ 0x6b, 0x38, 0x32, 0xd8, 0x23, 0x66, 0x70, 0x65, 0x72, 0x6c,
+ 0x5c, 0x42, 0x63, 0x4e, 0x65, 0x64, 0xd8, 0x24, 0x79, 0x01,
+ 0x57, 0x4d, 0x49, 0x4d, 0x45, 0x2d, 0x56, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70,
+ 0x65, 0x3a, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61,
+ 0x72, 0x74, 0x2f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x3b, 0x0a,
+ 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x3d, 0x22,
+ 0x58, 0x58, 0x58, 0x58, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61,
+ 0x72, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x22, 0x0a, 0x0a,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20,
+ 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x20,
+ 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x69, 0x6e,
+ 0x20, 0x4d, 0x49, 0x4d, 0x45, 0x20, 0x66, 0x6f, 0x72, 0x6d,
+ 0x61, 0x74, 0x2e, 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58, 0x58,
+ 0x58, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20,
+ 0x74, 0x65, 0x78, 0x74, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+ 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x74,
+ 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x0a,
+ 0x0a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58, 0x58, 0x58,
+ 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20, 0x74,
+ 0x65, 0x78, 0x74, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+ 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x3b, 0x0a,
+ 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x44, 0x69,
+ 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a,
+ 0x20, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e,
+ 0x74, 0x3b, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
+ 0x65, 0x3d, 0x22, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78,
+ 0x74, 0x22, 0x0a, 0x0a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x74, 0x74, 0x61,
+ 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x65, 0x78,
+ 0x74, 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58, 0x58, 0x58, 0x62,
+ 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2d, 0x2d, 0x0a, 0xd8, 0x24, 0x79, 0x01, 0x57,
+ 0x4d, 0x49, 0x4d, 0x45, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65,
+ 0x3a, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72,
+ 0x74, 0x2f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x3b, 0x0a, 0x62,
+ 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x3d, 0x22, 0x58,
+ 0x58, 0x58, 0x58, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72,
+ 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x22, 0x0a, 0x0a, 0x54,
+ 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6d,
+ 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6d,
+ 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x69, 0x6e, 0x20,
+ 0x4d, 0x49, 0x4d, 0x45, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61,
+ 0x74, 0x2e, 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58, 0x58, 0x58,
+ 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20, 0x74,
+ 0x65, 0x78, 0x74, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+ 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x0a, 0x0a,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x74, 0x65, 0x78,
+ 0x74, 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58, 0x58, 0x58, 0x62,
+ 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+ 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78,
+ 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x3b, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x44, 0x69, 0x73,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20,
+ 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74,
+ 0x3b, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65,
+ 0x3d, 0x22, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74,
+ 0x22, 0x0a, 0x0a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63,
+ 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58, 0x58, 0x58, 0x62, 0x6f,
+ 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20, 0x74, 0x65, 0x78,
+ 0x74, 0x2d, 0x2d, 0xc0, 0x74, 0x32, 0x30, 0x30, 0x33, 0x2d,
+ 0x31, 0x32, 0x2d, 0x31, 0x33, 0x54, 0x31, 0x38, 0x3a, 0x33,
+ 0x30, 0x3a, 0x30, 0x32, 0x5a, 0xa2, 0x68, 0x42, 0x65, 0x64,
+ 0x20, 0x74, 0x69, 0x6d, 0x65, 0xc0, 0x78, 0x1c, 0x32, 0x30,
+ 0x30, 0x33, 0x2d, 0x31, 0x32, 0x2d, 0x31, 0x33, 0x54, 0x31,
+ 0x38, 0x3a, 0x33, 0x30, 0x3a, 0x30, 0x32, 0x2e, 0x32, 0x35,
+ 0x2b, 0x30, 0x31, 0x3a, 0x30, 0x30, 0x18, 0x58, 0xc0, 0x78,
+ 0x1c, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x31, 0x32, 0x2d, 0x31,
+ 0x33, 0x54, 0x31, 0x38, 0x3a, 0x33, 0x30, 0x3a, 0x30, 0x32,
+ 0x2e, 0x32, 0x35, 0x2b, 0x30, 0x31, 0x3a, 0x30, 0x30, 0xf7,
+ 0xa3, 0x64, 0x64, 0x61, 0x72, 0x65, 0xd8, 0x42, 0xf5, 0x62,
+ 0x75, 0x75, 0xf4, 0x1a, 0x00, 0x0b, 0x41, 0x62, 0xf6, 0x80,
+ 0xa3, 0x78, 0x1c, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x74, 0x61, 0x67, 0x67, 0x65, 0x64, 0x20,
+ 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x61, 0x72, 0x72, 0x61,
+ 0x79, 0xd9, 0x04, 0x45, 0x80, 0x65, 0x61, 0x6c, 0x61, 0x62,
+ 0x6c, 0x80, 0x18, 0x2a, 0x80, 0xa1, 0x68, 0x69, 0x6e, 0x20,
+ 0x61, 0x20, 0x6d, 0x61, 0x70, 0xa1, 0x19, 0x15, 0xb4, 0xa1,
+ 0x6e, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x69, 0x6e, 0x20, 0x61,
+ 0x20, 0x69, 0x6e, 0x20, 0x61, 0xd9, 0x23, 0x7f, 0xa0, 0xa5,
+ 0x62, 0x73, 0x31, 0xd8, 0x58, 0xf8, 0xff, 0x62, 0x73, 0x32,
+ 0xe0, 0x62, 0x73, 0x33, 0xd8, 0x58, 0xf8, 0x21, 0x1a, 0x05,
+ 0x44, 0x8c, 0x06, 0xd8, 0x58, 0xf8, 0xff, 0x18, 0x59, 0xd8,
+ 0x58, 0xf3, 0xd8, 0x25, 0x50, 0x53, 0x4d, 0x41, 0x52, 0x54,
+ 0x43, 0x53, 0x4c, 0x54, 0x54, 0x43, 0x46, 0x49, 0x43, 0x41,
+ 0x32, 0xa2, 0x64, 0x55, 0x55, 0x55, 0x55, 0xd8, 0x25, 0x50,
+ 0x53, 0x4d, 0x41, 0x52, 0x54, 0x43, 0x53, 0x4c, 0x54, 0x54,
+ 0x43, 0x46, 0x49, 0x43, 0x41, 0x32, 0x18, 0x63, 0xd8, 0x25,
+ 0x50, 0x53, 0x4d, 0x41, 0x52, 0x54, 0x43, 0x53, 0x4c, 0x54,
+ 0x54, 0x43, 0x46, 0x49, 0x43, 0x41, 0x32, 0xf5, 0xf4, 0xa2,
+ 0x71, 0x47, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0xf5, 0x19,
+ 0x10, 0x41, 0xf5, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x63, 0x42, 0x4E, 0x2B,
+ 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x40, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x42, 0x4E, 0x2D, 0xC3, 0x49,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
+ 0x3F, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+
+static const char *szMIME = "\
+MIME-Version: 1.0\n\
+Content-Type: multipart/mixed;\n\
+boundary=\"XXXXboundary text\"\n\
+\n\
+This is a multipart message in MIME format.\n\
+\n\
+--XXXXboundary text\n\
+Content-Type: text/plain\n\
+\n\
+this is the body text\n\
+\n\
+--XXXXboundary text\n\
+Content-Type: text/plain;\n\
+Content-Disposition: attachment;\n\
+filename=\"test.txt\"\n\
+\n\
+this is the attachment text\n\
+\n\
+--XXXXboundary text--";
+
+
+int AllAddMethodsTest()
+{
+ // TODO: this test should be broken down into several so it is more managable. Tags and labels could be more sensible
+ QCBOREncodeContext ECtx;
+ int nReturn = 0;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenArray(&ECtx);
+
+ // Some ints that are tagged and have strings preceeding them (not labels becase it is not a map)
+ QCBOREncode_AddSZString(&ECtx, "UINT62");
+ QCBOREncode_AddTag(&ECtx, 100);
+ QCBOREncode_AddUInt64(&ECtx, 89989909);
+ QCBOREncode_AddSZString(&ECtx, "INT64");
+ QCBOREncode_AddTag(&ECtx, 76);
+ QCBOREncode_AddInt64(&ECtx, 77689989909);
+ QCBOREncode_AddUInt64(&ECtx,0);
+ QCBOREncode_AddInt64(&ECtx, -44);
+
+ // ints that go in maps
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddUInt64ToMap(&ECtx, "LBL", 77);
+ QCBOREncode_AddUInt64ToMapN(&ECtx, -4, 88);
+ QCBOREncode_AddInt64ToMap(&ECtx, "NEGLBLTHAT IS KIND OF LONG", -2394893489238);
+ QCBOREncode_AddInt64ToMapN(&ECtx, -100000000, -800000000);
+ QCBOREncode_CloseMap(&ECtx);
+
+ // Epoch Date
+ QCBOREncode_AddDateEpoch(&ECtx, 2383748234);
+
+ // Epoch date with labels
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddDateEpochToMap(&ECtx, "LongLiveDenisRitchie", 1400000000);
+ QCBOREncode_AddDateEpochToMap(&ECtx, "time()", 1477263730);
+ QCBOREncode_AddDateEpochToMapN(&ECtx, -1969, 1477263222);
+ QCBOREncode_CloseMap(&ECtx);
+
+ // Binary blobs
+ QCBOREncode_AddBytes(&ECtx, ((UsefulBufC) {(uint8_t []){0xff, 0x00}, 2}));
+
+ // binary blobs in maps
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddSZString(&ECtx, "binbin");
+ QCBOREncode_AddTag(&ECtx, 100000);
+ QCBOREncode_AddBytes(&ECtx, ((UsefulBufC) {(uint8_t []){0x00}, 1}));
+ QCBOREncode_AddBytesToMap(&ECtx, "blabel", ((UsefulBufC) {(uint8_t []){0x01, 0x02, 0x03}, 3}));
+ QCBOREncode_AddBytesToMapN(&ECtx, 0, ((UsefulBufC){(uint8_t []){0x04, 0x02, 0x03, 0xfe}, 4}));
+ QCBOREncode_CloseMap(&ECtx);
+
+ // text blobs
+ QCBOREncode_AddText(&ECtx, UsefulBuf_FROM_SZ_LITERAL("bar bar foo bar"));
+ QCBOREncode_AddSZString(&ECtx, "oof\n");
+ QCBOREncode_AddURI(&ECtx, UsefulBuf_FROM_SZ_LITERAL("http://stackoverflow.com/questions/28059697/how-do-i-toggle-between-debug-and-release-builds-in-xcode-6-7-8"));
+ QCBOREncode_AddB64Text(&ECtx, UsefulBuf_FROM_SZ_LITERAL("YW55IGNhcm5hbCBwbGVhc3VyZQ=="));
+ QCBOREncode_AddRegex(&ECtx, UsefulBuf_FROM_SZ_LITERAL("[^abc]+"));
+ QCBOREncode_AddMIMEData(&ECtx, UsefulBuf_FromSZ(szMIME));
+
+ // text blobs in maps
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddTextToMap(&ECtx, "#####", UsefulBuf_FROM_SZ_LITERAL("foo bar foo foo"));
+ QCBOREncode_AddTextToMap(&ECtx, "____", UsefulBuf_FROM_SZ_LITERAL("foo bar"));
+ QCBOREncode_AddSZString(&ECtx, "()()()");
+ QCBOREncode_AddTag(&ECtx, 1000);
+ QCBOREncode_AddSZString(&ECtx, "rab rab oof");
+ QCBOREncode_AddTextToMapN(&ECtx,22, UsefulBuf_FROM_SZ_LITERAL("foo foo foo foo"));
+ QCBOREncode_AddSZStringToMap(&ECtx, "^^", "oooooooof");
+ QCBOREncode_AddSZStringToMapN(&ECtx, 99, "ffffoooooooof");
+ QCBOREncode_AddURIToMap(&ECtx, "RFC", UsefulBuf_FROM_SZ_LITERAL("https://tools.ietf.org/html/rfc7049#section-2.4.5"));
+ QCBOREncode_AddURIToMapN(&ECtx, 0x89, UsefulBuf_FROM_SZ_LITERAL("http://cbor.me/"));
+ QCBOREncode_AddB64TextToMap(&ECtx, "whenim64", UsefulBuf_FROM_SZ_LITERAL("cGxlYXN1cmUu"));
+ QCBOREncode_AddB64TextToMapN(&ECtx, 64, UsefulBuf_FROM_SZ_LITERAL("c3VyZS4="));
+ QCBOREncode_AddRegexToMap(&ECtx, "popo", UsefulBuf_FROM_SZ_LITERAL("100\\s*mk")); // x code string literal bug
+ QCBOREncode_AddRegexToMapN(&ECtx, -51, UsefulBuf_FROM_SZ_LITERAL("perl\\B")); // x code string literal bug
+ QCBOREncode_AddMIMEDataToMap(&ECtx, "Ned", UsefulBuf_FromSZ(szMIME));
+ QCBOREncode_AddMIMEDataToMapN(&ECtx, 10, UsefulBuf_FromSZ(szMIME));
+ QCBOREncode_CloseMap(&ECtx);
+
+ // Date strings
+ QCBOREncode_AddDateString(&ECtx, "2003-12-13T18:30:02Z");
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddDateStringToMap(&ECtx, "Bed time", "2003-12-13T18:30:02.25+01:00");
+ QCBOREncode_AddDateStringToMapN(&ECtx, 88, "2003-12-13T18:30:02.25+01:00");
+ QCBOREncode_CloseMap(&ECtx);
+
+ // true / false ...
+ QCBOREncode_AddSimple(&ECtx, CBOR_SIMPLEV_UNDEF);
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddSZString(&ECtx, "dare");
+ QCBOREncode_AddTag(&ECtx, 66);
+ QCBOREncode_AddBool(&ECtx, true);
+ QCBOREncode_AddBoolToMap(&ECtx, "uu", false);
+ QCBOREncode_AddSimpleToMapN(&ECtx, 737634, CBOR_SIMPLEV_NULL);
+ QCBOREncode_CloseMap(&ECtx);
+
+ // opening an array
+ QCBOREncode_OpenArray(&ECtx);
+ QCBOREncode_CloseArray(&ECtx);
+
+ // opening arrays in a map
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddSZString(&ECtx, "label and tagged empty array");
+ QCBOREncode_AddTag(&ECtx, 1093);
+ QCBOREncode_OpenArray(&ECtx);
+ QCBOREncode_CloseArray(&ECtx);
+ QCBOREncode_OpenArrayInMap(&ECtx, "alabl");
+ QCBOREncode_CloseArray(&ECtx);
+ QCBOREncode_OpenArrayInMapN(&ECtx, 42);
+ QCBOREncode_CloseArray(&ECtx);
+ QCBOREncode_CloseMap(&ECtx);
+
+ // opening maps with labels and tagging
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_OpenMapInMap(&ECtx, "in a map");
+ QCBOREncode_OpenMapInMapN(&ECtx, 5556);
+ QCBOREncode_AddSZString(&ECtx, "in a in a in a");
+ QCBOREncode_AddTag(&ECtx, 9087);
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_CloseMap(&ECtx);
+ QCBOREncode_CloseMap(&ECtx);
+ QCBOREncode_CloseMap(&ECtx);
+ QCBOREncode_CloseMap(&ECtx);
+
+
+ // Extended simple values (these are not standard...)
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddSZString(&ECtx, "s1");
+ QCBOREncode_AddTag(&ECtx, 88);
+ QCBOREncode_AddSimple(&ECtx, 255);
+ QCBOREncode_AddSimpleToMap(&ECtx, "s2", 0);
+ QCBOREncode_AddSZString(&ECtx, "s3");
+ QCBOREncode_AddTag(&ECtx, 88);
+ QCBOREncode_AddSimple(&ECtx, 33);
+ QCBOREncode_AddInt64(&ECtx, 88378374); // label before tag
+ QCBOREncode_AddTag(&ECtx, 88);
+ QCBOREncode_AddSimple(&ECtx, 255);
+ QCBOREncode_AddInt64(&ECtx, 89); // label before tag
+ QCBOREncode_AddTag(&ECtx, 88);
+ QCBOREncode_AddSimple(&ECtx, 19);
+ QCBOREncode_CloseMap(&ECtx);
+
+ // UUIDs
+ static const uint8_t ppppUUID[] = {0x53, 0x4D, 0x41, 0x52, 0x54, 0x43, 0x53, 0x4C, 0x54, 0x54, 0x43, 0x46, 0x49, 0x43, 0x41, 0x32};
+ const UsefulBufC XXUUID = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(ppppUUID);
+ QCBOREncode_AddBinaryUUID(&ECtx, XXUUID);
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddBinaryUUIDToMap(&ECtx, "UUUU", XXUUID);
+ QCBOREncode_AddBinaryUUIDToMapN(&ECtx, 99, XXUUID);
+ QCBOREncode_CloseMap(&ECtx);
+
+ // Bool
+ QCBOREncode_AddBool(&ECtx, true);
+ QCBOREncode_AddBool(&ECtx, false);
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddBoolToMap(&ECtx, "George is the man", true);
+ QCBOREncode_AddBoolToMapN(&ECtx, 010101, true);
+ QCBOREncode_CloseMap(&ECtx);
+
+
+ static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ const UsefulBufC BIGNUM = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBignum);
+ QCBOREncode_AddPositiveBignum(&ECtx, BIGNUM);
+ QCBOREncode_AddNegativeBignum(&ECtx, BIGNUM);
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddPositiveBignumToMap(&ECtx, "BN+", BIGNUM);
+ QCBOREncode_AddPositiveBignumToMapN(&ECtx, 64, BIGNUM);
+ QCBOREncode_AddNegativeBignumToMap(&ECtx, "BN-", BIGNUM);
+ QCBOREncode_AddNegativeBignumToMapN(&ECtx, -64, BIGNUM);
+ QCBOREncode_CloseMap(&ECtx);
+
+ QCBOREncode_CloseArray(&ECtx);
+
+ UsefulBufC Enc;
+
+ if(QCBOREncode_Finish(&ECtx, &Enc)) {
+ nReturn = -1;
+ goto Done;
+ }
+
+ if(CheckResults(Enc, spExpectedEncodedAll))
+ nReturn = -2;
+
+Done:
+ return nReturn;
+}
+
+/*
+ 98 2F # array(47)
+ 3B 7FFFFFFFFFFFFFFF # negative(9223372036854775807)
+ 3B 0000000100000000 # negative(4294967296)
+ 3A FFFFFFFF # negative(4294967295)
+ 3A FFFFFFFE # negative(4294967294)
+ 3A FFFFFFFD # negative(4294967293)
+ 3A 7FFFFFFF # negative(2147483647)
+ 3A 7FFFFFFE # negative(2147483646)
+ 3A 00010001 # negative(65537)
+ 3A 00010000 # negative(65536)
+ 39 FFFF # negative(65535)
+ 39 FFFE # negative(65534)
+ 39 FFFD # negative(65533)
+ 39 0100 # negative(256)
+ 38 FF # negative(255)
+ 38 FE # negative(254)
+ 38 FD # negative(253)
+ 38 18 # negative(24)
+ 37 # negative(23)
+ 36 # negative(22)
+ 20 # negative(0)
+ 00 # unsigned(0)
+ 00 # unsigned(0)
+ 01 # unsigned(1)
+ 16 # unsigned(22)
+ 17 # unsigned(23)
+ 18 18 # unsigned(24)
+ 18 19 # unsigned(25)
+ 18 1A # unsigned(26)
+ 18 FE # unsigned(254)
+ 18 FF # unsigned(255)
+ 19 0100 # unsigned(256)
+ 19 0101 # unsigned(257)
+ 19 FFFE # unsigned(65534)
+ 19 FFFF # unsigned(65535)
+ 1A 00010000 # unsigned(65536)
+ 1A 00010001 # unsigned(65537)
+ 1A 00010002 # unsigned(65538)
+ 1A 7FFFFFFF # unsigned(2147483647)
+ 1A 7FFFFFFF # unsigned(2147483647)
+ 1A 80000000 # unsigned(2147483648)
+ 1A 80000001 # unsigned(2147483649)
+ 1A FFFFFFFE # unsigned(4294967294)
+ 1A FFFFFFFF # unsigned(4294967295)
+ 1B 0000000100000000 # unsigned(4294967296)
+ 1B 0000000100000001 # unsigned(4294967297)
+ 1B 7FFFFFFFFFFFFFFF # unsigned(9223372036854775807)
+ 1B FFFFFFFFFFFFFFFF # unsigned(18446744073709551615)
+ */
+static const uint8_t spExpectedEncodedInts[] = {
+ 0x98, 0x2f, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3b, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x3a, 0xff, 0xff, 0xff,
+ 0xff, 0x3a, 0xff, 0xff, 0xff, 0xfe, 0x3a, 0xff,
+ 0xff, 0xff, 0xfd, 0x3a, 0x7f, 0xff, 0xff, 0xff,
+ 0x3a, 0x7f, 0xff, 0xff, 0xfe, 0x3a, 0x00, 0x01,
+ 0x00, 0x01, 0x3a, 0x00, 0x01, 0x00, 0x00, 0x39,
+ 0xff, 0xff, 0x39, 0xff, 0xfe, 0x39, 0xff, 0xfd,
+ 0x39, 0x01, 0x00, 0x38, 0xff, 0x38, 0xfe, 0x38,
+ 0xfd, 0x38, 0x18, 0x37, 0x36, 0x20, 0x00, 0x00,
+ 0x01, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19, 0x18,
+ 0x1a, 0x18, 0xfe, 0x18, 0xff, 0x19, 0x01, 0x00,
+ 0x19, 0x01, 0x01, 0x19, 0xff, 0xfe, 0x19, 0xff,
+ 0xff, 0x1a, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00,
+ 0x01, 0x00, 0x01, 0x1a, 0x00, 0x01, 0x00, 0x02,
+ 0x1a, 0x7f, 0xff, 0xff, 0xff, 0x1a, 0x7f, 0xff,
+ 0xff, 0xff, 0x1a, 0x80, 0x00, 0x00, 0x00, 0x1a,
+ 0x80, 0x00, 0x00, 0x01, 0x1a, 0xff, 0xff, 0xff,
+ 0xfe, 0x1a, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1b,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff};
+
+/*
+
+ Test the generation of integers. This also ends up testing
+ encoding of all the different lengths. It encodes integers
+ of many lengths and values, especially around the boundaries
+ for different types of integers. It compares the output
+ to expected values generated from http://cbor.me.
+
+ */
+int IntegerValuesTest1()
+{
+ QCBOREncodeContext ECtx;
+ int nReturn = 0;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_OpenArray(&ECtx);
+
+ QCBOREncode_AddInt64(&ECtx, -9223372036854775807LL - 1);
+ QCBOREncode_AddInt64(&ECtx, -4294967297);
+ QCBOREncode_AddInt64(&ECtx, -4294967296);
+ QCBOREncode_AddInt64(&ECtx, -4294967295);
+ QCBOREncode_AddInt64(&ECtx, -4294967294);
+ QCBOREncode_AddInt64(&ECtx, -2147483648);
+ QCBOREncode_AddInt64(&ECtx, -2147483647);
+ QCBOREncode_AddInt64(&ECtx, -65538);
+ QCBOREncode_AddInt64(&ECtx, -65537);
+ QCBOREncode_AddInt64(&ECtx, -65536);
+ QCBOREncode_AddInt64(&ECtx, -65535);
+ QCBOREncode_AddInt64(&ECtx, -65534);
+ QCBOREncode_AddInt64(&ECtx, -257);
+ QCBOREncode_AddInt64(&ECtx, -256);
+ QCBOREncode_AddInt64(&ECtx, -255);
+ QCBOREncode_AddInt64(&ECtx, -254);
+ QCBOREncode_AddInt64(&ECtx, -25);
+ QCBOREncode_AddInt64(&ECtx, -24);
+ QCBOREncode_AddInt64(&ECtx, -23);
+ QCBOREncode_AddInt64(&ECtx, -1);
+ QCBOREncode_AddInt64(&ECtx, 0);
+ QCBOREncode_AddUInt64(&ECtx, 0ULL);
+ QCBOREncode_AddInt64(&ECtx, 1);
+ QCBOREncode_AddInt64(&ECtx, 22);
+ QCBOREncode_AddInt64(&ECtx, 23);
+ QCBOREncode_AddInt64(&ECtx, 24);
+ QCBOREncode_AddInt64(&ECtx, 25);
+ QCBOREncode_AddInt64(&ECtx, 26);
+ QCBOREncode_AddInt64(&ECtx, 254);
+ QCBOREncode_AddInt64(&ECtx, 255);
+ QCBOREncode_AddInt64(&ECtx, 256);
+ QCBOREncode_AddInt64(&ECtx, 257);
+ QCBOREncode_AddInt64(&ECtx, 65534);
+ QCBOREncode_AddInt64(&ECtx, 65535);
+ QCBOREncode_AddInt64(&ECtx, 65536);
+ QCBOREncode_AddInt64(&ECtx, 65537);
+ QCBOREncode_AddInt64(&ECtx, 65538);
+ QCBOREncode_AddInt64(&ECtx, 2147483647);
+ QCBOREncode_AddInt64(&ECtx, 2147483647);
+ QCBOREncode_AddInt64(&ECtx, 2147483648);
+ QCBOREncode_AddInt64(&ECtx, 2147483649);
+ QCBOREncode_AddInt64(&ECtx, 4294967294);
+ QCBOREncode_AddInt64(&ECtx, 4294967295);
+ QCBOREncode_AddInt64(&ECtx, 4294967296);
+ QCBOREncode_AddInt64(&ECtx, 4294967297);
+ QCBOREncode_AddInt64(&ECtx, 9223372036854775807LL);
+ QCBOREncode_AddUInt64(&ECtx, 18446744073709551615ULL);
+
+ QCBOREncode_CloseArray(&ECtx);
+
+ UsefulBufC Enc;
+ if(QCBOREncode_Finish(&ECtx, &Enc)) {
+ nReturn = -1;
+ }
+
+ if(CheckResults(Enc, spExpectedEncodedInts))
+ return -2;
+
+ return(nReturn);
+}
+
+
+/*
+ 85 # array(5)
+ F5 # primitive(21)
+ F4 # primitive(20)
+ F6 # primitive(22)
+ F7 # primitive(23)
+ A1 # map(1)
+ 65 # text(5)
+ 554E446566 # "UNDef"
+ F7 # primitive(23)
+ */
+static const uint8_t spExpectedEncodedSimple[] = {
+ 0x85, 0xf5, 0xf4, 0xf6, 0xf7, 0xa1, 0x65, 0x55, 0x4e, 0x44, 0x65, 0x66, 0xf7};
+
+int SimpleValuesTest1()
+{
+ QCBOREncodeContext ECtx;
+ int nReturn = 0;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_OpenArray(&ECtx);
+
+ QCBOREncode_AddSimple(&ECtx, CBOR_SIMPLEV_TRUE);
+ QCBOREncode_AddSimple(&ECtx, CBOR_SIMPLEV_FALSE);
+ QCBOREncode_AddSimple(&ECtx, CBOR_SIMPLEV_NULL);
+ QCBOREncode_AddSimple(&ECtx, CBOR_SIMPLEV_UNDEF);
+
+ QCBOREncode_OpenMap(&ECtx);
+
+ QCBOREncode_AddSimpleToMap(&ECtx, "UNDef", CBOR_SIMPLEV_UNDEF);
+ QCBOREncode_CloseMap(&ECtx);
+
+ QCBOREncode_CloseArray(&ECtx);
+
+ UsefulBufC ECBOR;
+ if(QCBOREncode_Finish(&ECtx, &ECBOR)) {
+ nReturn = -1;
+ }
+
+ if(CheckResults(ECBOR, spExpectedEncodedSimple))
+ return -2;
+
+ return(nReturn);
+}
+
+
+/*
+ 83 # array(3)
+ C0 # tag(0)
+ 74 # text(20)
+ 323031332D30332D32315432303A30343A30305A # "2013-03-21T20:04:00Z"
+ C1 # tag(1)
+ 1A 514B67B0 # unsigned(1363896240)
+ A2 # map(2)
+ 78 19 # text(25)
+ 53616D706C6520446174652066726F6D205246432033333339 # "Sample Date from RFC 3339"
+ C0 # tag(0)
+ 77 # text(23)
+ 313938352D30342D31325432333A32303A35302E35325A # "1985-04-12T23:20:50.52Z"
+ 62 # text(2)
+ 5344 # "SD"
+ C1 # tag(1)
+ 19 03E7 # unsigned(999)
+ */
+static const uint8_t spExpectedEncodedDates[] = {
+ 0x83, 0xc0, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x30, 0x33,
+ 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x34, 0x3a,
+ 0x30, 0x30, 0x5a, 0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0, 0xa2,
+ 0x78, 0x19, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44,
+ 0x61, 0x74, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x52,
+ 0x46, 0x43, 0x20, 0x33, 0x33, 0x33, 0x39, 0xc0, 0x77, 0x31,
+ 0x39, 0x38, 0x35, 0x2d, 0x30, 0x34, 0x2d, 0x31, 0x32, 0x54,
+ 0x32, 0x33, 0x3a, 0x32, 0x30, 0x3a, 0x35, 0x30, 0x2e, 0x35,
+ 0x32, 0x5a, 0x62, 0x53, 0x44, 0xc1, 0x19, 0x03, 0xe7
+};
+
+int EncodeDateTest()
+{
+ QCBOREncodeContext ECtx;
+ int nReturn = 0;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenArray(&ECtx);
+
+
+ QCBOREncode_AddDateString(&ECtx, "2013-03-21T20:04:00Z"); // from CBOR RFC
+ QCBOREncode_AddDateEpoch(&ECtx, 1363896240); // from CBOR RFC
+
+
+ QCBOREncode_OpenMap(&ECtx);
+
+ QCBOREncode_AddDateStringToMap(&ECtx, "Sample Date from RFC 3339", "1985-04-12T23:20:50.52Z");
+
+ QCBOREncode_AddDateEpochToMap(&ECtx, "SD", 999);
+
+ QCBOREncode_CloseMap(&ECtx);
+
+ QCBOREncode_CloseArray(&ECtx);
+
+ UsefulBufC ECBOR;
+
+ if(QCBOREncode_Finish(&ECtx, &ECBOR)) {
+ nReturn = -1;
+ }
+
+ if(CheckResults(ECBOR, spExpectedEncodedDates))
+ return -2;
+
+ return(nReturn);
+}
+
+
+int ArrayNestingTest1()
+{
+ QCBOREncodeContext ECtx;
+ int i;
+ int nReturn = 0;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ for(i = QCBOR_MAX_ARRAY_NESTING; i; i--) {
+ QCBOREncode_OpenArray(&ECtx);
+ }
+ for(i = QCBOR_MAX_ARRAY_NESTING; i; i--) {
+ QCBOREncode_CloseArray(&ECtx);
+ }
+ UsefulBufC Encoded;
+ if(QCBOREncode_Finish(&ECtx, &Encoded)) {
+ nReturn = -1;
+ }
+
+ return(nReturn);
+}
+
+
+
+int ArrayNestingTest2()
+{
+ QCBOREncodeContext ECtx;
+ int i;
+ int nReturn = 0;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ for(i = QCBOR_MAX_ARRAY_NESTING+1; i; i--) {
+ QCBOREncode_OpenArray(&ECtx);
+ }
+ for(i = QCBOR_MAX_ARRAY_NESTING; i; i--) {
+ QCBOREncode_CloseArray(&ECtx);
+ }
+
+ UsefulBufC Encoded;
+ if(QCBOREncode_Finish(&ECtx, &Encoded) != QCBOR_ERR_ARRAY_NESTING_TOO_DEEP) {
+ nReturn = -1;
+ }
+
+ return(nReturn);
+}
+
+
+
+int ArrayNestingTest3()
+{
+ QCBOREncodeContext ECtx;
+ int i;
+ int nReturn = 0;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ for(i = QCBOR_MAX_ARRAY_NESTING; i; i--) {
+ QCBOREncode_OpenArray(&ECtx);
+ }
+ for(i = QCBOR_MAX_ARRAY_NESTING+1 ; i; i--) {
+ QCBOREncode_CloseArray(&ECtx);
+ }
+ UsefulBufC Encoded;
+ if(QCBOREncode_Finish(&ECtx, &Encoded) != QCBOR_ERR_TOO_MANY_CLOSES) {
+ nReturn = -1;
+ }
+
+ return(nReturn);
+}
+
+
+/*
+ 81 # array(1)
+ 81 # array(1)
+ 81 # array(1)
+ 81 # array(1)
+ 80 # array(0)
+*/
+static const uint8_t spFiveArrarys[] = {0x81, 0x81, 0x81, 0x81, 0x80};
+
+// Validated at http://cbor.me and by manually examining its output
+/*
+ 82 # array(2)
+ 81 # array(1)
+ 81 # array(1)
+ 81 # array(1)
+ 81 # array(1)
+ 80 # array(0)
+ 98 2F # array(47)
+ 3B 7FFFFFFFFFFFFFFF # negative(9223372036854775807)
+ 3B 0000000100000000 # negative(4294967296)
+ 3A FFFFFFFF # negative(4294967295)
+ 3A FFFFFFFE # negative(4294967294)
+ 3A FFFFFFFD # negative(4294967293)
+ 3A 7FFFFFFF # negative(2147483647)
+ 3A 7FFFFFFE # negative(2147483646)
+ 3A 00010001 # negative(65537)
+ 3A 00010000 # negative(65536)
+ 39 FFFF # negative(65535)
+ 39 FFFE # negative(65534)
+ 39 FFFD # negative(65533)
+ 39 0100 # negative(256)
+ 38 FF # negative(255)
+ 38 FE # negative(254)
+ 38 FD # negative(253)
+ 38 18 # negative(24)
+ 37 # negative(23)
+ 36 # negative(22)
+ 20 # negative(0)
+ 00 # unsigned(0)
+ 00 # unsigned(0)
+ 01 # unsigned(1)
+ 16 # unsigned(22)
+ 17 # unsigned(23)
+ 18 18 # unsigned(24)
+ 18 19 # unsigned(25)
+ 18 1A # unsigned(26)
+ 18 FE # unsigned(254)
+ 18 FF # unsigned(255)
+ 19 0100 # unsigned(256)
+ 19 0101 # unsigned(257)
+ 19 FFFE # unsigned(65534)
+ 19 FFFF # unsigned(65535)
+ 1A 00010000 # unsigned(65536)
+ 1A 00010001 # unsigned(65537)
+ 1A 00010002 # unsigned(65538)
+ 1A 7FFFFFFF # unsigned(2147483647)
+ 1A 7FFFFFFF # unsigned(2147483647)
+ 1A 80000000 # unsigned(2147483648)
+ 1A 80000001 # unsigned(2147483649)
+ 1A FFFFFFFE # unsigned(4294967294)
+ 1A FFFFFFFF # unsigned(4294967295)
+ 1B 0000000100000000 # unsigned(4294967296)
+ 1B 0000000100000001 # unsigned(4294967297)
+ 1B 7FFFFFFFFFFFFFFF # unsigned(9223372036854775807)
+ 1B FFFFFFFFFFFFFFFF # unsigned(18446744073709551615)
+ */
+static const uint8_t spEncodeRawExpected[] = {
+ 0x82, 0x81, 0x81, 0x81, 0x81, 0x80, 0x98, 0x2f,
+ 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x3a, 0xff, 0xff, 0xff, 0xff, 0x3a,
+ 0xff, 0xff, 0xff, 0xfe, 0x3a, 0xff, 0xff, 0xff,
+ 0xfd, 0x3a, 0x7f, 0xff, 0xff, 0xff, 0x3a, 0x7f,
+ 0xff, 0xff, 0xfe, 0x3a, 0x00, 0x01, 0x00, 0x01,
+ 0x3a, 0x00, 0x01, 0x00, 0x00, 0x39, 0xff, 0xff,
+ 0x39, 0xff, 0xfe, 0x39, 0xff, 0xfd, 0x39, 0x01,
+ 0x00, 0x38, 0xff, 0x38, 0xfe, 0x38, 0xfd, 0x38,
+ 0x18, 0x37, 0x36, 0x20, 0x00, 0x00, 0x01, 0x16,
+ 0x17, 0x18, 0x18, 0x18, 0x19, 0x18, 0x1a, 0x18,
+ 0xfe, 0x18, 0xff, 0x19, 0x01, 0x00, 0x19, 0x01,
+ 0x01, 0x19, 0xff, 0xfe, 0x19, 0xff, 0xff, 0x1a,
+ 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, 0x01, 0x00,
+ 0x01, 0x1a, 0x00, 0x01, 0x00, 0x02, 0x1a, 0x7f,
+ 0xff, 0xff, 0xff, 0x1a, 0x7f, 0xff, 0xff, 0xff,
+ 0x1a, 0x80, 0x00, 0x00, 0x00, 0x1a, 0x80, 0x00,
+ 0x00, 0x01, 0x1a, 0xff, 0xff, 0xff, 0xfe, 0x1a,
+ 0xff, 0xff, 0xff, 0xff, 0x1b, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x1b, 0x7f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+
+int EncodeRawTest()
+{
+ QCBOREncodeContext ECtx;
+
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_OpenArray(&ECtx);
+ QCBOREncode_AddEncoded(&ECtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spFiveArrarys));
+ QCBOREncode_AddEncoded(&ECtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedEncodedInts));
+ QCBOREncode_CloseArray(&ECtx);
+
+ UsefulBufC EncodedRawTest;
+
+ if(QCBOREncode_Finish(&ECtx, &EncodedRawTest)) {
+ return -4;
+ }
+
+ if(CheckResults(EncodedRawTest, spEncodeRawExpected)) {
+ return -5;
+ }
+
+ return 0;
+}
+
+/*
+ This returns a pointer to spBigBuf
+ */
+static int CreateMap(uint8_t **pEncoded, size_t *pEncodedLen)
+{
+ QCBOREncodeContext ECtx;
+ int nReturn = -1;
+
+ *pEncoded = NULL;
+ *pEncodedLen = INT32_MAX;
+ size_t uFirstSizeEstimate = 0;
+
+ // loop runs CBOR encoding twice. First with no buffer to
+ // calucate the length so buffer can be allocated correctly,
+ // and last with the buffer to do the actual encoding
+ do {
+ QCBOREncode_Init(&ECtx, (UsefulBuf){*pEncoded, *pEncodedLen});
+ QCBOREncode_OpenMap(&ECtx);
+ QCBOREncode_AddInt64ToMap(&ECtx, "first integer", 42);
+ QCBOREncode_OpenArrayInMap(&ECtx, "an array of two strings");
+ QCBOREncode_AddText(&ECtx, ((UsefulBufC) {"string1", 7}));
+ QCBOREncode_AddText(&ECtx, ((UsefulBufC) {"string2", 7}));
+ QCBOREncode_CloseArray(&ECtx);
+ QCBOREncode_OpenMapInMap(&ECtx, "map in a map");
+ QCBOREncode_AddBytesToMap(&ECtx,"bytes 1", ((UsefulBufC) { "xxxx", 4}));
+ QCBOREncode_AddBytesToMap(&ECtx, "bytes 2",((UsefulBufC) { "yyyy", 4}));
+ QCBOREncode_AddInt64ToMap(&ECtx, "another int", 98);
+ QCBOREncode_AddTextToMap(&ECtx, "text 2", ((UsefulBufC) {"lies, damn lies and statistics", 30}));
+ QCBOREncode_CloseMap(&ECtx);
+ QCBOREncode_CloseMap(&ECtx);
+
+ if(QCBOREncode_FinishGetSize(&ECtx, pEncodedLen))
+ goto Done;
+ if(*pEncoded != NULL) {
+ if(uFirstSizeEstimate != *pEncodedLen) {
+ nReturn = 1;
+ } else {
+ nReturn = 0;
+ }
+ goto Done;
+ }
+ *pEncoded = spBigBuf;
+ uFirstSizeEstimate = *pEncodedLen;
+
+ } while(1);
+
+ Done:
+ return(nReturn);
+}
+
+/*
+ A3 # map(3)
+ 6D # text(13)
+ 666972737420696E7465676572 # "first integer"
+ 18 2A # unsigned(42)
+ 77 # text(23)
+ 616E206172726179206F662074776F20737472696E6773 # "an array of two strings"
+ 82 # array(2)
+ 67 # text(7)
+ 737472696E6731 # "string1"
+ 67 # text(7)
+ 737472696E6732 # "string2"
+ 6C # text(12)
+ 6D617020696E2061206D6170 # "map in a map"
+ A4 # map(4)
+ 67 # text(7)
+ 62797465732031 # "bytes 1"
+ 44 # bytes(4)
+ 78787878 # "xxxx"
+ 67 # text(7)
+ 62797465732032 # "bytes 2"
+ 44 # bytes(4)
+ 79797979 # "yyyy"
+ 6B # text(11)
+ 616E6F7468657220696E74 # "another int"
+ 18 62 # unsigned(98)
+ 66 # text(6)
+ 746578742032 # "text 2"
+ 78 1E # text(30)
+ 6C6965732C2064616D6E206C69657320616E642073746174697374696373 # "lies, damn lies and statistics"
+ */
+static const uint8_t spValidMapEncoded[] = {
+ 0xa3, 0x6d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x69, 0x6e,
+ 0x74, 0x65, 0x67, 0x65, 0x72, 0x18, 0x2a, 0x77, 0x61, 0x6e,
+ 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x77, 0x6f, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x73, 0x82, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31,
+ 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x32, 0x6c, 0x6d,
+ 0x61, 0x70, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x6d, 0x61,
+ 0x70, 0xa4, 0x67, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x31,
+ 0x44, 0x78, 0x78, 0x78, 0x78, 0x67, 0x62, 0x79, 0x74, 0x65,
+ 0x73, 0x20, 0x32, 0x44, 0x79, 0x79, 0x79, 0x79, 0x6b, 0x61,
+ 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x74,
+ 0x18, 0x62, 0x66, 0x74, 0x65, 0x78, 0x74, 0x20, 0x32, 0x78,
+ 0x1e, 0x6c, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x6d,
+ 0x6e, 0x20, 0x6c, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63,
+ 0x73 } ;
+
+
+int MapEncodeTest()
+{
+ uint8_t *pEncodedMaps;
+ size_t nEncodedMapLen;
+
+ if(CreateMap(&pEncodedMaps, &nEncodedMapLen)) {
+ return -1;
+ }
+
+ int nReturn = 0;
+ if(memcmp(spValidMapEncoded, pEncodedMaps, sizeof(spValidMapEncoded)))
+ nReturn = 2;
+
+ return(nReturn);
+}
+
+
+/*
+ @brief Encode the RTIC results
+
+ @param[in] nRResult CBOR_SIMPLEV_TRUE, CBOR_SIMPLEV_FALSE or CBOR_SIMPLEV_NULL
+ @param[in] time Time stamp in UNIX epoch time or 0 for no time stamp
+ @param[in] szAlexString Diagnostic code.
+ @param[in[ pOut Buffer to put the result in
+ @param[in/out] pnLen Size of pOut buffer when called; length of data output in buffer on return
+
+ @return
+ One of the CBOR encoder errors. QCBOR_SUCCESS, which is has value 0, if no error.
+
+ The size of pOut should be 30 bytes plus the length of pnLen. If you make it too
+ short an error will be returned. This function will never write off the end
+ of the buffer passed to it.
+
+ If the result is 0, then the correct encoded CBOR is in pOut and *pnLen is the
+ length of the encoded CBOR.
+
+ */
+
+static UsefulBufC FormatRTICResults(int nRResult, uint64_t time, const char *szType, const char *szAlexString, UsefulBuf Storage)
+{
+ // Buffer that the result will be written in to
+ // It is fixed size and small that a stack variable will be fine
+ // QCBOREncode will never write off the end of this buffer. If it won't fit QCBOREncode_Finish will return an error.
+
+ // Context for the encoder
+ QCBOREncodeContext ECtx;
+ QCBOREncode_Init(&ECtx, Storage);
+
+ // All the RTIC results are grouped in a CBOR Map which will get turned into a JSON Object
+ // Contents are label / value pairs
+ QCBOREncode_OpenMap(&ECtx);
+
+ { // Brace / indention just to show CBOR encoding nesting
+
+ // The result: 0 if scan happened and found nothing; 1 if it happened and found something wrong; 2 if it didn't happen
+ QCBOREncode_AddSimpleToMap(&ECtx, "integrity", nRResult);
+
+ // Add the diagnostic code
+ QCBOREncode_AddSZStringToMap(&ECtx, "type", szType);
+
+ // Add a time stamp
+ if(time) {
+ QCBOREncode_AddDateEpochToMap(&ECtx, "time", time);
+ }
+
+ // Add the diagnostic code
+ QCBOREncode_AddSZStringToMap(&ECtx, "diag", szAlexString);
+
+ // Open a subordinate map for telemtry data
+ QCBOREncode_OpenMapInMap(&ECtx, "telemetry");
+
+ { // Brace / indention just to show CBOR encoding nesting
+
+ // Add a few fake integers and buffers for now.
+ QCBOREncode_AddInt64ToMap(&ECtx, "Shoe Size", 12);
+
+ // Add a few fake integers and buffers for now.
+ QCBOREncode_AddInt64ToMap(&ECtx, "IQ", 0xffffffff);
+
+ // Add a few fake integers and buffers for now.
+ static const uint8_t pPV[] = {0x66, 0x67, 0x00, 0x56, 0xaa, 0xbb, 0x01, 0x01};
+ const UsefulBufC WSPV = {pPV, sizeof(pPV)};
+
+ QCBOREncode_AddBytesToMap(&ECtx, "WhaleSharkPatternVector", WSPV);
+ }
+ }
+
+ // Close the telemetry map
+ QCBOREncode_CloseMap(&ECtx);
+
+ // Close the map
+ QCBOREncode_CloseMap(&ECtx);
+
+ UsefulBufC Result;
+
+ QCBOREncode_Finish(&ECtx, &Result);
+
+ return Result;
+}
+
+
+/*
+ A5 # map(5)
+ 69 # text(9)
+ 696E74656772697479 # "integrity"
+ F4 # primitive(20)
+ 64 # text(4)
+ 74797065 # "type"
+ 66 # text(6)
+ 726563656E74 # "recent"
+ 64 # text(4)
+ 74696D65 # "time"
+ C1 # tag(1)
+ 1A 580D4172 # unsigned(1477263730)
+ 64 # text(4)
+ 64696167 # "diag"
+ 6A # text(10)
+ 30784131654335303031 # "0xA1eC5001"
+ 69 # text(9)
+ 74656C656D65747279 # "telemetry"
+ A3 # map(3)
+ 69 # text(9)
+ 53686F652053697A65 # "Shoe Size"
+ 0C # unsigned(12)
+ 62 # text(2)
+ 4951 # "IQ"
+ 1A FFFFFFFF # unsigned(4294967295)
+ 77 # text(23)
+ 5768616C65536861726B5061747465726E566563746F72 # "WhaleSharkPatternVector"
+ 48 # bytes(8)
+ 66670056AABB0101 # "fg\x00V\xAA\xBB\x01\x01"
+ */
+static const uint8_t spExpectedRTIC[] = {
+ 0xa5, 0x69, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74,
+ 0x79, 0xf4, 0x64, 0x74, 0x79, 0x70, 0x65, 0x66, 0x72, 0x65,
+ 0x63, 0x65, 0x6e, 0x74, 0x64, 0x74, 0x69, 0x6d, 0x65, 0xc1,
+ 0x1a, 0x58, 0x0d, 0x41, 0x72, 0x64, 0x64, 0x69, 0x61, 0x67,
+ 0x6a, 0x30, 0x78, 0x41, 0x31, 0x65, 0x43, 0x35, 0x30, 0x30,
+ 0x31, 0x69, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72,
+ 0x79, 0xa3, 0x69, 0x53, 0x68, 0x6f, 0x65, 0x20, 0x53, 0x69,
+ 0x7a, 0x65, 0x0c, 0x62, 0x49, 0x51, 0x1a, 0xff, 0xff, 0xff,
+ 0xff, 0x77, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x53, 0x68, 0x61,
+ 0x72, 0x6b, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x56,
+ 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x66, 0x67, 0x00, 0x56,
+ 0xaa, 0xbb, 0x01, 0x01};
+
+
+int RTICResultsTest()
+{
+ const UsefulBufC Encoded = FormatRTICResults(CBOR_SIMPLEV_FALSE, 1477263730,
+ "recent", "0xA1eC5001",
+ UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ if(UsefulBuf_IsNULLC(Encoded)) {
+ return -1;
+ }
+
+ if(CheckResults(Encoded, spExpectedRTIC)) {
+ return -2;
+ }
+
+ return 0;
+}
+
+
+/*
+ 82 # array(2)
+ 19 01C3 # unsigned(451)
+ 43 # bytes(3)
+ 1901D2 # "\x19\x01\xD2"
+*/
+static const uint8_t spExpectedBstrWrap[] = {0x82, 0x19, 0x01, 0xC3, 0x43, 0x19, 0x01, 0xD2};
+
+/*
+ Very basic bstr wrapping test
+ */
+int BstrWrapTest()
+{
+ QCBOREncodeContext EC;
+
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddUInt64(&EC, 451);
+
+ QCBOREncode_BstrWrap(&EC);
+ QCBOREncode_AddUInt64(&EC, 466);
+
+ UsefulBufC Wrapped;
+ QCBOREncode_CloseBstrWrap(&EC, &Wrapped);
+
+ QCBOREncode_CloseArray(&EC);
+
+ UsefulBufC Encoded;
+ if(QCBOREncode_Finish(&EC, &Encoded)) {
+ return -1;
+ }
+
+ if(CheckResults(Encoded, spExpectedBstrWrap)) {
+ return -2;
+ }
+
+ /* Another test; see about handling length calculation */
+ QCBOREncode_Init(&EC, (UsefulBuf){NULL, INT32_MAX});
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_BstrWrap(&EC);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddNULL(&EC);
+ QCBOREncode_CloseArray(&EC);
+ UsefulBufC BStr;
+ QCBOREncode_CloseBstrWrap(&EC, &BStr);
+ // 2 is one byte for an array of length 1 and 1 byte for a NULL
+ if(BStr.ptr != NULL || BStr.len != 2) {
+ return -5;
+ }
+
+ return 0;
+}
+
+
+
+int BstrWrapErrorTest()
+{
+ // -------------- Test closing a bstrwrap when it is an array that is open -----------
+ QCBOREncodeContext EC;
+
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddUInt64(&EC, 451);
+
+ QCBOREncode_BstrWrap(&EC);
+ QCBOREncode_AddUInt64(&EC, 466);
+ QCBOREncode_OpenArray(&EC);
+
+ UsefulBufC Wrapped;
+ QCBOREncode_CloseBstrWrap(&EC, &Wrapped);
+
+ QCBOREncode_CloseArray(&EC);
+
+ UsefulBufC Encoded2;
+ if(QCBOREncode_Finish(&EC, &Encoded2) != QCBOR_ERR_CLOSE_MISMATCH) {
+ return -1;
+ }
+
+ // ----------- test closing a bstrwrap when nothing is open ---------------------
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_CloseBstrWrap(&EC, &Wrapped);
+ if(QCBOREncode_Finish(&EC, &Encoded2) != QCBOR_ERR_TOO_MANY_CLOSES) {
+ return -2;
+ }
+
+ // --------------- test nesting too deep ----------------------------------
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ for(int i = 1; i < 18; i++) {
+ QCBOREncode_BstrWrap(&EC);
+ }
+ QCBOREncode_AddBool(&EC, true);
+
+ for(int i = 1; i < 18; i++) {
+ QCBOREncode_CloseBstrWrap(&EC, &Wrapped);
+ }
+
+ if(QCBOREncode_Finish(&EC, &Encoded2) != QCBOR_ERR_ARRAY_NESTING_TOO_DEEP) {
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+// Part of bstr_wrap_nest_test
+/*
+ 83 array with three
+ 53 byte string with 19 bytes
+ 01 #1
+ 50 byte string with 16 bytes
+ 02
+ 4D byte string with 13 bytes
+ 03
+ 4A byte string with 10 bytes
+ 04
+ 47 byte string with 7 bytes
+ 05
+ 44 byte string with 4 bytes
+ 06
+ 41 byte string with 1 byte
+ 07
+ 01
+ 02
+ 03
+ 04
+ 05
+ 06
+ 07
+ A2 map with two items
+ 18 20 label for byte string
+ 54 byte string of length 20
+ 82 Array with two items
+ 10 The integer value 10
+ A2 map with two items
+ 18 21 label for byte string
+ 44 byte string with 4 bytes
+ 81 array with 1 item
+ 11 integer value 11
+ 18 30 integer value 30
+ 18 40 integer label 40
+ 65 68 65 6C 6C 6F text string hello
+ 18 31 integer value 31
+ 18 41 integer label 41
+ 65 68 65 6C 6C 6F text string hello
+
+
+ */
+
+
+/*
+ 83 # array(3)
+ 56 # bytes(22)
+ 00530150024D034A0447054406410700010203040506 # "\x00S\x01P\x02M\x03J\x04G\x05D\x06A\a\x00\x01\x02\x03\x04\x05\x06"
+ 07 # unsigned(7)
+ A2 # map(2)
+ 18 20 # unsigned(32)
+ 54 # bytes(20)
+ 8210A21821448111183018406568656C6C6F1831 # "\x82\x10\xA2\x18!D\x81\x11\x180\x18@ehello\x181"
+ 18 41 # unsigned(65)
+ 65 # text(5)
+ 68656C6C6F # "hello"
+ */
+static const uint8_t spExpectedDeepBstr[] =
+{
+ 0x83, 0x56, 0x00, 0x53, 0x01, 0x50, 0x02, 0x4D,
+ 0x03, 0x4A, 0x04, 0x47, 0x05, 0x44, 0x06, 0x41,
+ 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0xA2, 0x18, 0x20, 0x54, 0x82, 0x10, 0xA2,
+ 0x18, 0x21, 0x44, 0x81, 0x11, 0x18, 0x30, 0x18,
+ 0x40, 0x65, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x18,
+ 0x31, 0x18, 0x41, 0x65, 0x68, 0x65, 0x6C, 0x6C,
+ 0x6F
+};
+
+// Part of bstr_wrap_nest_test
+static int DecodeNextNested(UsefulBufC Wrapped)
+{
+ int nReturn;
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, Wrapped, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORItem Item;
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -12;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn == QCBOR_ERR_HIT_END) {
+ return 0;
+ }
+ if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return -13;
+ }
+ nReturn = DecodeNextNested(Item.val.string);
+ if(nReturn) {
+ return nReturn;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -14;
+ }
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -15;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -16;
+ }
+
+ return 0;
+}
+
+// Part of bstr_wrap_nest_test
+static int DecodeNextNested2(UsefulBufC Wrapped)
+{
+ int nReturn;
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, Wrapped, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORItem Item;
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -12;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -12;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return 0;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return -13;
+ }
+ nReturn = DecodeNextNested2(Item.val.string);
+ if(nReturn) {
+ return nReturn;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return -12;
+ }
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -12;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -16;
+ }
+
+ return 0;
+}
+
+
+int BstrWrapNestTest()
+{
+ QCBOREncodeContext EC;
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ // ---- Make a complicated nested CBOR structure ---
+#define BSTR_TEST_DEPTH 10
+
+ QCBOREncode_OpenArray(&EC);
+
+ for(int i = 0; i < BSTR_TEST_DEPTH-2; i++) {
+ QCBOREncode_BstrWrap(&EC);
+ QCBOREncode_AddUInt64(&EC, i);
+ }
+
+ for(int i = 0; i < BSTR_TEST_DEPTH-2; i++) {
+ QCBOREncode_CloseBstrWrap(&EC, NULL);
+ QCBOREncode_AddUInt64(&EC, i);
+ }
+
+ for(int i = 0; i < (BSTR_TEST_DEPTH-2)/3; i++) {
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_BstrWrapInMapN(&EC, i+0x20);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddUInt64(&EC, i+0x10);
+ }
+
+ for(int i = 0; i < (BSTR_TEST_DEPTH-2)/3; i++) {
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_AddUInt64(&EC, i+0x30);
+ QCBOREncode_CloseBstrWrap(&EC, NULL);
+ QCBOREncode_AddSZStringToMapN(&EC, i+0x40, "hello");
+ QCBOREncode_CloseMap(&EC);
+ }
+ QCBOREncode_CloseArray(&EC);
+
+ UsefulBufC Encoded;
+ if(QCBOREncode_Finish(&EC, &Encoded)) {
+ return -1;
+ }
+
+ // ---Compare it to expected. Expected was hand checked with use of CBOR playground ----
+ if(UsefulBuf_Compare(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedDeepBstr), Encoded)) {
+ return -25;
+ }
+
+
+ // ---- Decode it and see if it is OK ------
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, Encoded, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORItem Item;
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY || Item.val.uCount != 3) {
+ return -2;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return -3;
+ }
+
+ int nReturn = DecodeNextNested(Item.val.string);
+ if(nReturn) {
+ return nReturn;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -12;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 2) {
+ return -2;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return -3;
+ }
+ nReturn = DecodeNextNested2(Item.val.string);
+ if(nReturn) {
+ return nReturn;
+ }
+
+ nReturn = QCBORDecode_GetNext(&DC, &Item);
+ if(nReturn) {
+ return -11;
+ }
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return -12;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -16;
+ }
+
+ return 0;
+}
+
+
+static const uint8_t spSignature[] = {
+ 0x8e, 0xb3, 0x3e, 0x4c, 0xa3, 0x1d, 0x1c, 0x46, 0x5a, 0xb0,
+ 0x5a, 0xac, 0x34, 0xcc, 0x6b, 0x23, 0xd5, 0x8f, 0xef, 0x5c,
+ 0x08, 0x31, 0x06, 0xc4, 0xd2, 0x5a, 0x91, 0xae, 0xf0, 0xb0,
+ 0x11, 0x7e, 0x2a, 0xf9, 0xa2, 0x91, 0xaa, 0x32, 0xe1, 0x4a,
+ 0xb8, 0x34, 0xdc, 0x56, 0xed, 0x2a, 0x22, 0x34, 0x44, 0x54,
+ 0x7e, 0x01, 0xf1, 0x1d, 0x3b, 0x09, 0x16, 0xe5, 0xa4, 0xc3,
+ 0x45, 0xca, 0xcb, 0x36};
+
+/*
+ D2 # tag(18)
+ 84 # array(4)
+ 43 # bytes(3)
+ A10126 # "\xA1\x01&"
+ A1 # map(1)
+ 04 # unsigned(4)
+ 42 # bytes(2)
+ 3131 # "11"
+ 54 # bytes(20)
+ 546869732069732074686520636F6E74656E742E # "This is the content."
+ 58 40 # bytes(64)
+ 8EB33E4CA31D1C465AB05AAC34CC6B23D58FEF5C083106C4D25A91AEF0B0117E2AF9A291AA32E14AB834DC56ED2A223444547E01F11D3B0916E5A4C345CACB36 # "\x8E\xB3>L\xA3\x1D\x1CFZ\xB0Z\xAC4\xCCk#\xD5\x8F\xEF\\\b1\x06\xC4\xD2Z\x91\xAE\xF0\xB0\x11~*\xF9\xA2\x91\xAA2\xE1J\xB84\xDCV\xED*\"4DT~\x01\xF1\x1D;\t\x16\xE5\xA4\xC3E\xCA\xCB6"
+ */
+static const uint8_t spExpected[] = {
+ 0xD2, 0x84, 0x43, 0xA1, 0x01, 0x26, 0xA1, 0x04, 0x42, 0x31,
+ 0x31, 0x54, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E,
+ 0x74, 0x2E, 0x58, 0x40, 0x8E, 0xB3, 0x3E, 0x4C, 0xA3, 0x1D,
+ 0x1C, 0x46, 0x5A, 0xB0, 0x5A, 0xAC, 0x34, 0xCC, 0x6B, 0x23,
+ 0xD5, 0x8F, 0xEF, 0x5C, 0x08, 0x31, 0x06, 0xC4, 0xD2, 0x5A,
+ 0x91, 0xAE, 0xF0, 0xB0, 0x11, 0x7E, 0x2A, 0xF9, 0xA2, 0x91,
+ 0xAA, 0x32, 0xE1, 0x4A, 0xB8, 0x34, 0xDC, 0x56, 0xED, 0x2A,
+ 0x22, 0x34, 0x44, 0x54, 0x7E, 0x01, 0xF1, 0x1D, 0x3B, 0x09,
+ 0x16, 0xE5, 0xA4, 0xC3, 0x45, 0xCA, 0xCB, 0x36};
+
+/*
+ this corresponds exactly to the example in RFC 8152
+ section C.2.1. This doesn't actually verify the signature
+ though that would be nice as it would make the test
+ really good. That would require bring in ECDSA crypto
+ to this test.
+ */
+int CoseSign1TBSTest()
+{
+ // All of this is from RFC 8152 C.2.1
+ const char *szKid = "11";
+ const UsefulBufC Kid = UsefulBuf_FromSZ(szKid);
+ const char *szPayload = "This is the content.";
+ const UsefulBufC Payload = UsefulBuf_FromSZ(szPayload);
+ static const uint8_t pProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ const UsefulBufC ProtectedHeaders = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pProtectedHeaders);
+
+ // It would be good to compare this to the output from
+ // a COSE implementation like COSE-C. It has been checked
+ // against the CBOR playground.
+ const UsefulBufC Signature = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSignature);
+
+ QCBOREncodeContext EC;
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ // top level array for cose sign1, 18 is the tag for COSE sign
+ QCBOREncode_AddTag(&EC, CBOR_TAG_COSE_SIGN1);
+ QCBOREncode_OpenArray(&EC);
+
+ // Add protected headers
+ QCBOREncode_AddBytes(&EC, ProtectedHeaders);
+
+ // Empty map with unprotected headers
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddBytesToMapN(&EC, 4, Kid);
+ QCBOREncode_CloseMap(&EC);
+
+ // The payload
+ UsefulBufC WrappedPayload;
+ QCBOREncode_BstrWrap(&EC);
+ QCBOREncode_AddEncoded(&EC, Payload); // Payload is not actually CBOR in example C.2.1
+ QCBOREncode_CloseBstrWrap(&EC, &WrappedPayload);
+
+ // Check we got back the actual payload expected
+ if(UsefulBuf_Compare(WrappedPayload, Payload)) {
+ return -1;
+ }
+
+ // The signature
+ QCBOREncode_AddBytes(&EC, Signature);
+ QCBOREncode_CloseArray(&EC);
+
+ // Finish and check the results
+ UsefulBufC COSE_Sign1;
+ if(QCBOREncode_Finish(&EC, &COSE_Sign1)) {
+ return -2;
+ }
+
+ // 98 is the size from RFC 8152 C.2.1
+ if(COSE_Sign1.len != 98) {
+ return -3;
+ }
+
+ if(CheckResults(COSE_Sign1, spExpected)) {
+ return -4;
+ }
+
+ return 0;
+}
+
+
+int EncodeErrorTests()
+{
+ QCBOREncodeContext EC;
+
+
+ // ------ Test for QCBOR_ERR_BUFFER_TOO_LARGE ------
+ // Do all of these tests with NULL buffers so no actual large allocations are neccesary
+ UsefulBuf Buffer = (UsefulBuf){NULL, UINT32_MAX};
+
+ // First verify no error from a big buffer
+ QCBOREncode_Init(&EC, Buffer);
+ QCBOREncode_OpenArray(&EC);
+ // 6 is the CBOR overhead for opening the array and encodng the length
+ // This exactly fills the buffer.
+ QCBOREncode_AddBytes(&EC, (UsefulBufC){NULL, UINT32_MAX-6});
+ QCBOREncode_CloseArray(&EC);
+ size_t xx;
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_SUCCESS) {
+ return -1;
+ }
+
+ // Second verify error from an array in encoded output too large
+ QCBOREncode_Init(&EC, Buffer);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddBytes(&EC, (UsefulBufC){NULL, UINT32_MAX-6});
+ QCBOREncode_OpenArray(&EC); // Where QCBOR internally encounters and records error
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_CloseArray(&EC);
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_BUFFER_TOO_LARGE) {
+ return -2;
+ }
+
+ // Third, fit an array in exactly at max position allowed
+ QCBOREncode_Init(&EC, Buffer);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddBytes(&EC, (UsefulBufC){NULL, QCBOR_MAX_ARRAY_OFFSET-6});
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_CloseArray(&EC);
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_SUCCESS) {
+ return -10;
+ }
+
+
+ // ----- QCBOR_ERR_BUFFER_TOO_SMALL --------------
+ // Work close to the 4GB size limit for a better test
+ const uint32_t uLargeSize = UINT32_MAX - 1024;
+ UsefulBuf Large = (UsefulBuf){NULL,uLargeSize};
+
+ QCBOREncode_Init(&EC, Large);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddBytes(&EC, (UsefulBufC){NULL, uLargeSize/2 + 1});
+ QCBOREncode_CloseArray(&EC);
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_SUCCESS) {
+ // Making sure it succeeds when it should first
+ return -3;
+ }
+
+ QCBOREncode_Init(&EC, Large);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddBytes(&EC, (UsefulBufC){NULL, uLargeSize/2 + 1});
+ QCBOREncode_AddBytes(&EC, (UsefulBufC){NULL, uLargeSize/2});
+ QCBOREncode_CloseArray(&EC);
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_BUFFER_TOO_SMALL) {
+ // Now just 1 byte over, see that it fails
+ return -4;
+ }
+
+
+ // ----- QCBOR_ERR_ARRAY_NESTING_TOO_DEEP -------
+ QCBOREncode_Init(&EC, Large);
+ for(int i = QCBOR_MAX_ARRAY_NESTING; i > 0; i--) {
+ QCBOREncode_OpenArray(&EC);
+ }
+ for(int i = QCBOR_MAX_ARRAY_NESTING; i > 0; i--) {
+ QCBOREncode_CloseArray(&EC);
+ }
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_SUCCESS) {
+ // Making sure it succeeds when it should first
+ return -5;
+ }
+
+ QCBOREncode_Init(&EC, Large);
+ for(int i = QCBOR_MAX_ARRAY_NESTING+1; i > 0; i--) {
+ QCBOREncode_OpenArray(&EC);
+ }
+ for(int i = QCBOR_MAX_ARRAY_NESTING+1; i > 0; i--) {
+ QCBOREncode_CloseArray(&EC);
+ }
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_ARRAY_NESTING_TOO_DEEP) {
+ // One more level to cause error
+ return -6;
+ }
+
+
+ // ------ QCBOR_ERR_TOO_MANY_CLOSES --------
+ QCBOREncode_Init(&EC, Large);
+ for(int i = QCBOR_MAX_ARRAY_NESTING; i > 0; i--) {
+ QCBOREncode_OpenArray(&EC);
+ }
+ for(int i = QCBOR_MAX_ARRAY_NESTING+1; i > 0; i--) {
+ QCBOREncode_CloseArray(&EC);
+ }
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_TOO_MANY_CLOSES) {
+ // One more level to cause error
+ return -7;
+ }
+
+
+ // ------ QCBOR_ERR_CLOSE_MISMATCH --------
+ QCBOREncode_Init(&EC, Large);
+ QCBOREncode_OpenArray(&EC);
+ UsefulBufC Wrap;
+ QCBOREncode_CloseBstrWrap(&EC, &Wrap);
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_CLOSE_MISMATCH) {
+ return -8;
+ }
+
+
+ // ------ QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN ---------
+ QCBOREncode_Init(&EC, Large);
+ for(int i = QCBOR_MAX_ARRAY_NESTING; i > 0; i--) {
+ QCBOREncode_OpenArray(&EC);
+ }
+ for(int i = QCBOR_MAX_ARRAY_NESTING-1; i > 0; i--) {
+ QCBOREncode_CloseArray(&EC);
+ }
+ if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+ // One more level to cause error
+ return -9;
+ }
+
+ /* QCBOR_ERR_ARRAY_TOO_LONG is not tested here as
+ it would require a 64KB of RAM to test */
+
+ return 0;
+}
+
diff --git a/lib/ext/qcbor/test/qcbor_encode_tests.h b/lib/ext/qcbor/test/qcbor_encode_tests.h
new file mode 100644
index 0000000..33703d8
--- /dev/null
+++ b/lib/ext/qcbor/test/qcbor_encode_tests.h
@@ -0,0 +1,145 @@
+/*==============================================================================
+ Copyright (c) 2016-2018, The Linux Foundation.
+ Copyright (c) 2018-2019, Laurence Lundblade.
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors, nor the name "Laurence Lundblade" may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ==============================================================================*/
+
+#ifndef __QCBOR__qcbor_encode_tests__
+#define __QCBOR__qcbor_encode_tests__
+
+#include "qcbor.h"
+
+
+/*
+ Notes:
+
+ - All the functions in qcbor.h are called once in the aggregation of all the tests below.
+
+ - All the types that are supported are given as input and parsed by these tests
+
+ - There is some hostile input such as invalid lengths and CBOR too complex
+ and types this parser doesn't handle
+
+ */
+
+
+/*
+ Most basic test.
+ */
+int BasicEncodeTest(void);
+
+
+/*
+ Encode lots of integer values, particularly around the boundary and make sure they
+ Match the expected binary output. Primarily an encoding test.
+ */
+int IntegerValuesTest1(void);
+
+
+
+/*
+ Create nested arrays to the max depth allowed and make sure it succeeds.
+ This is an encoding test.
+ */
+int ArrayNestingTest1(void);
+
+
+/*
+ Create nested arrays to one more than the meax depth and make sure it fails.
+ This is an encoding test.
+ */
+int ArrayNestingTest2(void);
+
+
+/*
+ Encoding test.
+ Create arrays to max depth and close one extra time and look for correct error code
+ */
+int ArrayNestingTest3(void);
+
+
+/*
+ This tests the QCBOREncode_AddRaw() function by adding two chunks or RAWCBOR to an
+ array and comparing with expected values. This is an encoding test.
+ */
+int EncodeRawTest(void);
+
+
+/*
+ This creates a somewhat complicated CBOR MAP and verifies it against expected
+ data. This is an encoding test.
+ */
+int MapEncodeTest(void);
+
+
+
+/*
+ Encodes a goodly number of floats and doubles and checks encoding is right
+ */
+int FloatValuesTest1(void);
+
+
+/*
+ Encodes true, false and the like
+ */
+int SimpleValuesTest1(void);
+
+
+/*
+ Encodes most data formats that are supported */
+int EncodeDateTest(void);
+
+
+/*
+ Encodes particular data structure that a particular app will need...
+ */
+int RTICResultsTest(void);
+
+
+/*
+ Calls all public encode methods in qcbor.h once.
+ */
+int AllAddMethodsTest(void);
+
+/*
+ The binary string wrapping of maps and arrays used by COSE
+ */
+int BstrWrapTest(void);
+
+int BstrWrapErrorTest(void);
+
+int BstrWrapNestTest(void);
+
+int CoseSign1TBSTest(void);
+
+int EncodeErrorTests(void);
+
+
+
+#endif /* defined(__QCBOR__qcbor_encode_tests__) */
diff --git a/lib/ext/qcbor/test/run_tests.c b/lib/ext/qcbor/test/run_tests.c
new file mode 100644
index 0000000..6e35620
--- /dev/null
+++ b/lib/ext/qcbor/test/run_tests.c
@@ -0,0 +1,285 @@
+/*==============================================================================
+ run_tests.c -- test aggregator and results reporting
+
+ Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created on 9/30/18
+ ==============================================================================*/
+
+#include "run_tests.h"
+#include "UsefulBuf.h"
+#include <stdbool.h>
+
+#include "float_tests.h"
+#include "qcbor_decode_tests.h"
+#include "qcbor_encode_tests.h"
+#include "UsefulBuf_Tests.h"
+
+
+
+// Used to test RunTests
+int fail_test()
+{
+ return -44;
+}
+
+
+
+
+/*
+ Convert a number up to 999999999 to a string. This is so sprintf doesn't
+ have to be linked in so as to minimized dependencies even in test code.
+ */
+const char *NumToString(int32_t nNum, UsefulBuf StringMem)
+{
+ const int32_t nMax = 1000000000;
+
+ UsefulOutBuf OutBuf;
+ UsefulOutBuf_Init(&OutBuf, StringMem);
+
+ if(nNum < 0) {
+ UsefulOutBuf_AppendByte(&OutBuf, '-');
+ nNum = -nNum;
+ }
+ if(nNum > nMax-1) {
+ return "XXX";
+ }
+
+ bool bDidSomeOutput = false;
+ for(int n = nMax; n > 0; n/=10) {
+ int x = nNum/n;
+ if(x || bDidSomeOutput){
+ bDidSomeOutput = true;
+ UsefulOutBuf_AppendByte(&OutBuf, '0' + x);
+ nNum -= x * n;
+ }
+ }
+ if(!bDidSomeOutput){
+ UsefulOutBuf_AppendByte(&OutBuf, '0');
+ }
+ UsefulOutBuf_AppendByte(&OutBuf, '\0');
+
+ return UsefulOutBuf_GetError(&OutBuf) ? "" : StringMem.ptr;
+}
+
+
+
+
+typedef int (test_fun_t)(void);
+typedef const char * (test_fun2_t)(void);
+
+
+#define TEST_ENTRY(test_name) {#test_name, test_name, true}
+#define TEST_ENTRY_DISABLED(test_name) {#test_name, test_name, false}
+
+typedef struct {
+ const char *szTestName;
+ test_fun_t *test_fun;
+ bool bEnabled;
+} test_entry;
+
+typedef struct {
+ const char *szTestName;
+ test_fun2_t *test_fun;
+ bool bEnabled;
+} test_entry2;
+
+test_entry2 s_tests2[] = {
+ TEST_ENTRY(UBUTest_CopyUtil),
+ TEST_ENTRY(UOBTest_NonAdversarial),
+ TEST_ENTRY(TestBasicSanity),
+ TEST_ENTRY(UOBTest_BoundaryConditionsTest),
+ TEST_ENTRY(UBMacroConversionsTest),
+ TEST_ENTRY(UBUtilTests),
+ TEST_ENTRY(UIBTest_IntegerFormat)
+};
+
+
+test_entry s_tests[] = {
+ TEST_ENTRY(ParseMapAsArrayTest),
+ TEST_ENTRY_DISABLED(AllocAllStringsTest),
+ TEST_ENTRY(IndefiniteLengthNestTest),
+ TEST_ENTRY(NestedMapTestIndefLen),
+ TEST_ENTRY(ParseSimpleTest),
+ TEST_ENTRY(EncodeRawTest),
+ TEST_ENTRY(RTICResultsTest),
+ TEST_ENTRY(MapEncodeTest),
+ TEST_ENTRY(ArrayNestingTest1),
+ TEST_ENTRY(ArrayNestingTest2),
+ TEST_ENTRY(ArrayNestingTest3),
+ TEST_ENTRY(EncodeDateTest),
+ TEST_ENTRY(SimpleValuesTest1),
+ TEST_ENTRY(IntegerValuesTest1),
+ TEST_ENTRY(AllAddMethodsTest),
+ TEST_ENTRY(ParseTooDeepArrayTest),
+ TEST_ENTRY(ComprehensiveInputTest),
+ TEST_ENTRY(ParseMapTest),
+ TEST_ENTRY_DISABLED(IndefiniteLengthArrayMapTest),
+ TEST_ENTRY(BasicEncodeTest),
+ TEST_ENTRY(NestedMapTest),
+ TEST_ENTRY(BignumParseTest),
+ TEST_ENTRY(OptTagParseTest),
+ TEST_ENTRY(DateParseTest),
+ TEST_ENTRY(ShortBufferParseTest2),
+ TEST_ENTRY(ShortBufferParseTest),
+ TEST_ENTRY(ParseDeepArrayTest),
+ TEST_ENTRY(SimpleArrayTest),
+ TEST_ENTRY(IntegerValuesParseTest),
+ TEST_ENTRY_DISABLED(MemPoolTest),
+ TEST_ENTRY_DISABLED(IndefiniteLengthStringTest),
+ TEST_ENTRY(HalfPrecisionDecodeBasicTests),
+ TEST_ENTRY(DoubleAsSmallestTest),
+ TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
+ TEST_ENTRY(BstrWrapTest),
+ TEST_ENTRY(BstrWrapErrorTest),
+ TEST_ENTRY(BstrWrapNestTest),
+ TEST_ENTRY(CoseSign1TBSTest),
+ TEST_ENTRY(StringDecoderModeFailTest),
+ TEST_ENTRY_DISABLED(BigComprehensiveInputTest),
+ TEST_ENTRY(EncodeErrorTests),
+ //TEST_ENTRY(fail_test),
+};
+
+
+int RunTests(const char *szTestNames[], OutputStringCB pfOutput, void *poutCtx, int *pNumTestsRun)
+{
+ int nTestsFailed = 0;
+ int nTestsRun = 0;
+ UsefulBuf_MAKE_STACK_UB(StringStorage, 5);
+
+ test_entry2 *t2;
+ const test_entry2 *s_tests2_end = s_tests2 + sizeof(s_tests2)/sizeof(test_entry2);
+
+ for(t2 = s_tests2; t2 < s_tests2_end; t2++) {
+ if(szTestNames[0]) {
+ // Some tests have been named
+ const char **szRequestedNames;
+ for(szRequestedNames = szTestNames; *szRequestedNames; szRequestedNames++) {
+ if(!strcmp(t2->szTestName, *szRequestedNames)) {
+ break; // Name matched
+ }
+ }
+ if(*szRequestedNames == NULL) {
+ // Didn't match this test
+ continue;
+ }
+ } else {
+ // no tests named, but don't run "disabled" tests
+ if(!t2->bEnabled) {
+ // Don't run disabled tests when all tests are being run
+ // as indicated by no specific test names being given
+ continue;
+ }
+ }
+
+ const char * szTestResult = (t2->test_fun)();
+ nTestsRun++;
+ if(pfOutput) {
+ (*pfOutput)(t2->szTestName, poutCtx, 0);
+ }
+
+ if(szTestResult) {
+ if(pfOutput) {
+ (*pfOutput)(" FAILED (returned ", poutCtx, 0);
+ (*pfOutput)(szTestResult, poutCtx, 0);
+ (*pfOutput)(")", poutCtx, 1);
+ }
+ nTestsFailed++;
+ } else {
+ if(pfOutput) {
+ (*pfOutput)( " PASSED", poutCtx, 1);
+ }
+ }
+ }
+
+
+ test_entry *t;
+ const test_entry *s_tests_end = s_tests + sizeof(s_tests)/sizeof(test_entry);
+
+ for(t = s_tests; t < s_tests_end; t++) {
+ if(szTestNames[0]) {
+ // Some tests have been named
+ const char **szRequestedNames;
+ for(szRequestedNames = szTestNames; *szRequestedNames; szRequestedNames++) {
+ if(!strcmp(t->szTestName, *szRequestedNames)) {
+ break; // Name matched
+ }
+ }
+ if(*szRequestedNames == NULL) {
+ // Didn't match this test
+ continue;
+ }
+ } else {
+ // no tests named, but don't run "disabled" tests
+ if(!t->bEnabled) {
+ // Don't run disabled tests when all tests are being run
+ // as indicated by no specific test names being given
+ continue;
+ }
+ }
+
+ int nTestResult = (t->test_fun)();
+ nTestsRun++;
+ if(pfOutput) {
+ (*pfOutput)(t->szTestName, poutCtx, 0);
+ }
+
+ if(nTestResult) {
+ if(pfOutput) {
+ (*pfOutput)(" FAILED (returned ", poutCtx, 0);
+ (*pfOutput)(NumToString(nTestResult, StringStorage), poutCtx, 0);
+ (*pfOutput)(")", poutCtx, 1);
+ }
+ nTestsFailed++;
+ } else {
+ if(pfOutput) {
+ (*pfOutput)( " PASSED", poutCtx, 1);
+ }
+ }
+ }
+
+ if(pNumTestsRun) {
+ *pNumTestsRun = nTestsRun;
+ }
+
+ if(pfOutput) {
+ (*pfOutput)( "SUMMARY: ", poutCtx, 0);
+ (*pfOutput)( NumToString(nTestsRun, StringStorage), poutCtx, 0);
+ (*pfOutput)( " tests run; ", poutCtx, 0);
+ (*pfOutput)( NumToString(nTestsFailed, StringStorage), poutCtx, 0);
+ (*pfOutput)( " tests failed", poutCtx, 1);
+ }
+
+ return nTestsFailed;
+}
+
+
+
+
+static void PrintSize(const char *szWhat, uint32_t uSize, OutputStringCB pfOutput, void *pOutCtx)
+{
+ UsefulBuf_MAKE_STACK_UB(buffer, 20);
+
+ (*pfOutput)(szWhat, pOutCtx, 0);
+ (*pfOutput)(" ", pOutCtx, 0);
+ (*pfOutput)(NumToString(uSize, buffer), pOutCtx, 0);
+ (*pfOutput)("", pOutCtx, 1);
+}
+
+void PrintSizes(OutputStringCB pfOutput, void *pOutCtx)
+{
+ // Type and size of return from sizeof() varies. These will never be large so cast is safe
+ PrintSize("sizeof(QCBORTrackNesting)", (uint32_t)sizeof(QCBORTrackNesting), pfOutput, pOutCtx);
+ PrintSize("sizeof(QCBOREncodeContext)", (uint32_t)sizeof(QCBOREncodeContext), pfOutput, pOutCtx);
+ PrintSize("sizeof(QCBORDecodeNesting)", (uint32_t)sizeof(QCBORDecodeNesting), pfOutput, pOutCtx);
+ PrintSize("sizeof(QCBORDecodeContext)", (uint32_t)sizeof(QCBORDecodeContext), pfOutput, pOutCtx);
+ PrintSize("sizeof(QCBORItem)", (uint32_t)sizeof(QCBORItem), pfOutput, pOutCtx);
+ PrintSize("sizeof(QCBORStringAllocator)",(uint32_t)sizeof(QCBORStringAllocator), pfOutput, pOutCtx);
+ PrintSize("sizeof(QCBORTagListIn)", (uint32_t)sizeof(QCBORTagListIn), pfOutput, pOutCtx);
+ PrintSize("sizeof(QCBORTagListOut)", (uint32_t)sizeof(QCBORTagListOut), pfOutput, pOutCtx);
+ (*pfOutput)("", pOutCtx, 1);
+}
diff --git a/lib/ext/qcbor/test/run_tests.h b/lib/ext/qcbor/test/run_tests.h
new file mode 100644
index 0000000..734d4f8
--- /dev/null
+++ b/lib/ext/qcbor/test/run_tests.h
@@ -0,0 +1,66 @@
+/*==============================================================================
+ run_tests.h -- test aggregator and results reporting
+
+ Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created 9/30/18
+ ==============================================================================*/
+
+/**
+ @file run_tests.h
+*/
+
+/**
+ @brief Type for function to output a text string
+
+ @param[in] szString The string to output
+ @param[in] pOutCtx A context pointer; NULL if not needed
+ @param[in] bNewline If non-zero, output a newline after the string
+
+ This is a prototype of a function to be passed to RunTests() to
+ output text strings.
+
+ This can be implemented with stdio (if available) using a straight
+ call to fputs() where the FILE * is passed as the pOutCtx as shown in
+ the example code below. This code is for Linux where the newline is
+ a \\n. Windows usually prefers \\r\\n.
+
+ @code
+ static void fputs_wrapper(const char *szString, void *pOutCtx, int bNewLine)
+ {
+ fputs(szString, (FILE *)pOutCtx);
+ if(bNewLine) {
+ fputs("\n", pOutCtx);
+ }
+ }
+ @endcode
+*/
+typedef void (*OutputStringCB)(const char *szString, void *pOutCtx, int bNewline);
+
+
+/**
+ @brief Runs the QCBOR tests.
+
+ @param[in] szTestNames An argv-style list of test names to run. If
+ empty, all are run.
+ @param[in] pfOutput Function that is called to output text strings.
+ @param[in] pOutCtx Context pointer passed to output function.
+ @param[out] pNumTestsRun Returns the number of tests run. May be NULL.
+
+ @return The number of tests that failed. Zero means overall success.
+ */
+int RunTests(const char *szTestNames[], OutputStringCB pfOutput, void *pOutCtx, int *pNumTestsRun);
+
+
+/**
+ @brief Print sizes of encoder / decoder contexts.
+
+ @param[in] pfOutput Function that is called to output text strings.
+ @param[in] pOutCtx Context pointer passed to output function.
+ */
+void PrintSizes(OutputStringCB pfOutput, void *pOutCtx);
+