Giter Site home page Giter Site logo

laurencelundblade / qcbor Goto Github PK

View Code? Open in Web Editor NEW
172.0 12.0 45.0 2.51 MB

Comprehensive, powerful, commercial-quality CBOR encoder/ decoder that is still suited for small devices.

License: Other

C 99.30% Makefile 0.34% CMake 0.35%
cbor cbor-library cbor-encoder portable iot c rfc-7049 rfc-8949 serialization serialization-library

qcbor's Introduction

QCBOR Logo

QCBOR is a powerful, commercial-quality CBOR encoder/decoder that implements these RFCs:

  • RFC8949 The CBOR Standard. (Nearly everything except sorting of encoded maps)
  • RFC7049 The previous CBOR standard. Replaced by RFC 8949.
  • RFC8742 CBOR Sequences
  • RFC8943 CBOR Dates

QCBOR Characteristics

Implemented in C with minimal dependency – Dependent only on C99, <stdint.h>, <stddef.h>, <stdbool.h> and <string.h> making it highly portable. <math.h> and <fenv.h> are used too, but their use can disabled. No #ifdefs or compiler options need to be set for QCBOR to run correctly.

Focused on C / native data representation – Careful conversion of CBOR data types in to C data types, handling over and underflow, strict typing and such so the caller doesn't have to worry so much about this and so code using QCBOR passes static analyzers easier. 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 176 bytes, decode context is 312 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.

Easy decoding of maps – The "spiffy decode" functions allow fetching map items directly by label. Detection of duplicate map items is automatically performed. This makes decoding of complex protocols much simpler, say when compared to TinyCBOR.

Supports most of RFC 8949 – With some size limits, all data types and formats in the specification are supported. Map sorting is main CBOR feature that is not supported. The same decoding API supports both definite and indefinite-length map and array decoding. Decoding indefinite length strings is supported but requires a string allocator be set up. Encoding of indefinite length strings is planned, but not yet supported.

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 and handling of binary data.

Small code size – In the smallest configuration the object code is less than 4KB on 64-bit x86 CPUs. The design is such that object code for QCBOR APIs not used is not referenced.

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 minimal and the same as the library's.

Spiffy Decode

These are functions to decode particular data types. They are an alternative to and built on top of QCBORDecode_GetNext(). They do type checking and in some cases sophisticated type conversion.

Spiffy decode supports easier map and array decoding. A map can be descended into with QCBORDecode_EnterMap(). When a map has been entered, members can be retrieved by label. Detection of duplicate map labels, an error, is automatically performed.

An internal error state is maintained. This simplifies the decode implementation as an error check is only needed at the end of the decode, rather than on every function.

An outcome is that decoding implementations are simple and involve many fewer lines of code. They also tend to parallel the encoding implementations as seen in the following example.

 /* Encode */
 QCBOREncode_Init(&EncodeCtx, Buffer);
 QCBOREncode_OpenMap(&EncodeCtx);
 QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pE->Manufacturer);
 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pE->uDisplacement);
 QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pE->uHorsePower);
 QCBOREncode_CloseMap(&EncodeCtx);
 uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedEngine);

 /* Decode */
 QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
 QCBORDecode_EnterMap(&DecodeCtx);
 QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
 QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
 QCBORDecode_ExitMap(&DecodeCtx);
 uErr = QCBORDecode_Finish(&DecodeCtx);

The spiffy decode functions will handle definite and indefinite length maps and arrays without the caller having to do anything. This includes mixed definite and indefinte maps and arrays. (Some work remains to support map searching with indefinite length strings.)

Comparison to TinyCBOR

TinyCBOR is a popular widely used implementation. Like QCBOR, it is a solid, well-maintained commercial quality implementation. This section is for folks trying to understand the difference in the approach between QCBOR and TinyCBOR.

TinyCBOR's API is more minimalist and closer to the CBOR encoding mechanics than QCBOR's. QCBOR's API is at a somewhat higher level of abstraction.

QCBOR really does implement just about everything described in RFC 8949. The main part missing is sorting of maps when encoding. TinyCBOR implements a smaller part of the standard.

No detailed code size comparison has been made, but in a spot check that encodes and decodes a single integer shows QCBOR about 25% larger. QCBOR encoding is actually smaller, but QCBOR decoding is larger. This includes the code to call the library, which is about the same for both libraries, and the code linked from the libraries. QCBOR is a bit more powerful, so you get value for the extra code brought in, especially when decoding more complex protocols.

QCBOR tracks encoding and decoding errors internally so the caller doesn't have to check the return code of every call to an encode or decode function. In many cases the error check is only needed as the last step or an encode or decode. TinyCBOR requires an error check on each call.

QCBOR provides a substantial feature that allows searching for data items in a map by label. It works for integer and text string labels (and at some point byte-string labels). This includes detection of items with duplicate labels. This makes the code for decoding CBOR simpler, similar to the encoding code and easier to read. TinyCBOR supports search by string, but no integer, nor duplicate detection.

QCBOR provides explicit support many of the registered CBOR tags. For example, QCBOR supports big numbers and decimal fractions including their conversion to floats, uint64_t and such.

Generally, QCBOR supports safe conversion of most CBOR number formats into number formats supported in C. For example, a data item can be fetched and converted to a C uint64_t whether the input CBOR is an unsigned 64-bit integer, signed 64-bit integer, floating-point number, big number, decimal fraction or a big float. The conversion is performed with full proper error detection of overflow and underflow.

QCBOR has a special feature for decoding byte-string wrapped CBOR. It treats this similar to entering an array with one item. This is particularly use for CBOR protocols like COSE that make use of byte-string wrapping. The implementation of these protocols is simpler and uses less memory.

QCBOR's test suite is written in the same portable C that QCBOR is where TinyCBOR requires Qt for its test. QCBOR's test suite is designed to be able to run on small embedded devices the same as QCBOR.

Code Status

The official current release is version 1.3. Changes over the last few years have been only minor bug fixes, minor feature additions and documentation improvements. QCBOR 1.x is highly stable.

Work on some larger feature additions is ongoing in "dev" branch. This includes more explicit support for preferred serialization and CDE (CBOR Deterministic Encoding). It will eventually be release as QCBOR 2.x.

QCBOR was originally developed by Qualcomm. It was open sourced through CAF with a permissive Linux license, September 2018 (thanks Qualcomm!).

Building

There is a simple makefile for the UNIX style command line binary that compiles everything to run the tests. CMake is also available, please read the "Building with CMake" section for more information.

These eleven files, the contents of the src and inc directories, make up the entire implementation.

  • inc
    • UsefulBuf.h
    • qcbor_private.h
    • qcbor_common.h
    • qcbor_encode.h
    • qcbor_decode.h
    • qcbor_spiffy_decode.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 test directory includes the 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.

While this code will run fine without configuration, there are several C pre processor macros that can be #defined in order to:

  • use a more efficient implementation
  • to reduce code size
  • to improve performance (a little)
  • remove features to reduce code size

See the comment sections on "Configuration" in inc/UsefulBuf.h and the pre processor defines that start with QCBOR_DISABLE_XXX.

Building with CMake

CMake can also be used to build QCBOR and the test application. Having the root CMakeLists.txt file, QCBOR can be easily integrated with your project's existing CMake environment. The result of the build process is a static library, to build a shared library instead you must add the -DBUILD_SHARED_LIBS=ON option at the CMake configuration step. The tests can be built into a simple command line application to run them as it was mentioned before; or it can be built as a library to be integrated with your development environment. The BUILD_QCBOR_TEST CMake option can be used for building the tests, it can have three values: APP, LIB or OFF (default, test are not included in the build).

Building the QCBOR library:

cd <QCBOR_base_folder>
# Configuring the project and generating a native build system
cmake -S . -B <build_folder>
# Building the project
cmake --build <build_folder>

Building and running the QCBOR test app:

cd <QCBOR_base_folder>
# Configuring the project and generating a native build system
cmake -S . -B <build_folder> -DBUILD_QCBOR_TEST=APP
# Building the project
cmake --build <build_folder>
# Running the test app
.<build_folder>/test/qcbortest

To enable all the compiler warnings that are used in the QCBOR release process you can use the BUILD_QCBOR_WARN option at the CMake configuration step:

cmake -S . -B <build_folder> -DBUILD_QCBOR_WARN=ON

Floating Point Support & Configuration

By default, all QCBOR floating-point features are enabled:

  • Encoding and decoding of basic float types, single and double-precision
  • Encoding and decoding of half-precision with conversion to/from single and double-precision
  • Preferred serialization of floating-point
  • Floating point dates
  • Methods that can convert big numbers, decimal fractions and other numbers to/from floating-point

If full floating-point is not needed, the following #defines can be used to reduce object code size and dependency.

See discussion in qcbor_encode.h for other details.

#define QCBOR_DISABLE_FLOAT_HW_USE

This removes dependency on:

  • Floating-point hardware and floating-point instructions
  • <math.h> and <fenv.h>
  • The math library (libm, -lm)

For most limited environments, this removes enough floating-point dependencies to be able to compile and run QCBOR.

Note that this does not remove use of the types double and float from QCBOR, but it limits QCBOR's use of them to converting the encoded byte stream to them and copying them. Converting and copying them usually don't require any hardware, libraries or includes. The C compiler takes care of it on its own.

QCBOR uses its own implementation of half-precision float-pointing that doesn't depend on math libraries. It uses masks and shifts instead. Thus, even with this define, half-precision encoding and decoding works.

When this is defined, the QCBOR functionality lost is minimal and only for decoding:

  • Decoding floating-point format dates are not handled
  • There is no conversion between floats and integers when decoding. For example, QCBORDecode_GetUInt64ConvertAll() will be unable to convert to and from float-point.
  • Floats will be unconverted to double when decoding.

No interfaces are disabled or removed with this define. If input that requires floating-point conversion or functions are called that request floating-point conversion, an error code like QCBOR_ERR_HW_FLOAT_DISABLED will be returned.

This saves only a small amount of object code. The primary purpose for defining this is to remove dependency on floating point hardware and libraries.

#define QCBOR_DISABLE_PREFERRED_FLOAT

This eliminates support for half-precision and CBOR preferred serialization by disabling QCBOR's shift and mask based implementation of half-precision floating-point.

With this defined, single and double-precision floating-point numbers can still be encoded and decoded. Conversion of floating-point to and from integers, big numbers and such is also supported. Floating-point dates are still supported.

The primary reason to define this is to save object code. Roughly 900 bytes are saved, though about half of this can be saved just by not calling any functions that encode floating-point numbers.

#define USEFULBUF_DISABLE_ALL_FLOAT

This eliminates floating point support completely (along with related function headers). This is useful if the compiler options deny the usage of floating point operations completely, and the usage soft floating point ABI is not possible.

Compiler options

Compilers support a number of options that control which float-point related code is generated. For example, it is usually possible to give options to the compiler to avoid all floating-point hardware and instructions, to use software and replacement libraries instead. These are usually bigger and slower, but these options may still be useful in getting QCBOR to run in some environments in combination with QCBOR_DISABLE_FLOAT_HW_USE. In particular, -mfloat-abi=soft, disables use of hardware instructions for the float and double types in C for some architectures.

CMake options

If you are using CMake, it can also be used to configure the floating-point support. These options can be enabled by adding them to the CMake configuration step and setting their value to 'ON' (True). The following table shows the available options and the associated #defines.

| CMake option                      | #define                       |
|-----------------------------------|-------------------------------|
| QCBOR_OPT_DISABLE_FLOAT_HW_USE    | QCBOR_DISABLE_FLOAT_HW_USE    |
| QCBOR_OPT_DISABLE_FLOAT_PREFERRED | QCBOR_DISABLE_PREFERRED_FLOAT |
| QCBOR_OPT_DISABLE_FLOAT_ALL       | USEFULBUF_DISABLE_ALL_FLOAT   |

Code Size

These are approximate sizes on a 64-bit x86 CPU with the -Os optimization.

|               | smallest | largest |
|---------------|----------|---------|
| encode only   |      900 |    2100 |
| decode only   |     1550 |   13300 |
| combined      |     2450 |   15500 |

From the table above, one can see that the amount of code pulled in from the QCBOR library varies a lot, ranging from 1KB to 15KB. The main factor is in this is the number of QCBOR functions called and which ones they are. QCBOR is constructed with less internal interdependency so only code necessary for the called functions is brought in.

Encoding is simpler and smaller. An encode-only implementation may bring in only 1KB of code.

Encoding of floating-point brings in a little more code as does encoding of tagged types and encoding of bstr wrapping.

Basic decoding using QCBORDecode_GetNext() brings in 3KB.

Use of the supplied MemPool by calling QCBORDecode_SetMemPool() to setup to decode indefinite-length strings adds 0.5KB.

Basic use of spiffy decode to brings in about 3KB. Using more spiffy decode functions, such as those for tagged types bstr wrapping brings in more code.

Finally, use of all of the integer conversion functions will bring in about 5KB, though you can use the simpler ones like QCBORDecode_GetInt64() without bringing in very much code.

In addition to using fewer QCBOR functions, the following are some ways to make the code smaller.

The gcc compiler output is usually smaller than llvm because stack guards are off by default (be sure you actually have gcc and not llvm installed to be invoked by the gcc command). You can also turn off stack gaurds with llvm. It is safe to turn off stack gaurds with this code because Usefulbuf provides similar defenses and this code was carefully written to be defensive.

If QCBOR is installed as a shared library, then of course only one copy of the code is in memory no matter how many applications use it.

Disabling Features

Here's the list of all features that can be disabled to save object code. The amount saved is an approximation.

| #define                                 | Saves |
| ----------------------------------------| ------|
| QCBOR_DISABLE_ENCODE_USAGE_GUARDS       |   150 |
| QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS |   400 |
| QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS  |   200 |
| QCBOR_DISABLE_UNCOMMON_TAGS             |   100 |
| QCBOR_DISABLE_EXP_AND_MANTISSA          |   400 |
| QCBOR_DISABLE_PREFERRED_FLOAT           |   900 |
| QCBOR_DISABLE_FLOAT_HW_USE              |    50 |
| QCBOR_DISABLE_TAGS                      |   400 |
| USEFULBUF_DISABLE_ALL_FLOAT             |   950 |

QCBOR_DISABLE_ENCODE_USAGE_GUARDS affects encoding only. It doesn't disable any encoding features, just some error checking. Disable it when you are confident that an encoding implementation is complete and correct.

Indefinite lengths are a feature of CBOR that makes encoding simpler and the decoding more complex. They allow the encoder to not have to know the length of a string, map or array when they start encoding it. Their main use is when encoding has to be done on a very constrained device. Conversely when decoding on a very constrained device, it is good to prohibit use of indefinite lengths so the decoder can be smaller.

The QCBOR decode API processes both definite and indefinite lengths with the same API, except to decode indefinite-length strings a storage allocator must be configured.

To reduce the size of the decoder define QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS particularly if you are not configuring a storage allocator.

Further reduction can be by defining QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS which will result in an error when an indefinite-length map or array arrives for decoding.

QCBOR_DISABLE_UNCOMMON_TAGS disables the decoding of explicit tags for base 64, regex, UUID and MIME data. This just disables the automatic recognition of these from a major type 6 tag.

QCBOR_DISABLE_EXP_AND_MANTISSA disables the decoding of decimal fractions and big floats.

QCBOR_DISABLE_TAGS disables all decoding of CBOR tags. If the input has a single tag, the error is unrecoverable so it is suitable only for protocols that have no tags. "Borrowed" tag content formats (e.g. an epoch-based date without the tag number), can still be processed.

See the discussion above on floating-point.

Size of spiffy decode

When creating a decode implementation, there is a choice of whether or not to use spiffy decode features or to just use QCBORDecode_GetNext().

The implementation using spiffy decode will be simpler resulting in the calling code being smaller, but the amount of code brought in from the QCBOR library will be larger. Basic use of spiffy decode brings in about 2KB of object code. If object code size is not a concern, then it is probably better to use spiffy decode because it is less work, there is less complexity and less testing to worry about.

If code size is a concern, then use of QCBORDecode_GetNext() will probably result in smaller overall code size for simpler CBOR protocols. However, if the CBOR protocol is complex then use of spiffy decode may reduce overall code size. An example of a complex protocol is one that involves decoding a lot of maps or maps that have many data items in them. The overall code may be smaller because the general purpose spiffy decode map processor is the one used for all the maps.

Other Software Using QCBOR

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
  • Michael Eckel for Makefile improvements
  • Jan Jongboom for indefinite length encoding
  • Peter Uiterwijk for error strings and other
  • Michael Richarson for CI set up and fixing some compiler warnings
  • Máté Tóth-Pál for float-point disabling and other
  • Dave Thaler for portability to Windows

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-2021, Laurence Lundblade. All rights reserved. Copyright (c) 2021-2023, Arm Limited. All rights reserved.

qcbor's People

Contributors

adam2809 avatar akil avatar asiderr avatar davidvincze avatar dnav avatar dthaler avatar eckelmeckel avatar escherstair avatar gtravisb avatar hannestschofenig avatar janjongboom avatar jhand2 avatar laurencelundblade avatar matetothpal avatar mcr avatar mephistophiles avatar mniestroj avatar plietar avatar puiterwijk avatar rgilton avatar rvanputten avatar yotann avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qcbor's Issues

[feature request] Convenience decode functions for tagged data

I'm not sure why QCBOR API was designed to treat tags as if they're attached to an enclosed items instead of the other way around but this makes decoders for protocols where major type of an item depend on a tag that wraps it quite inconvinient.

For example, imagine a protocol where one of the fields may contain some complex structure of useful payload or an error code integer. To distinguish, protocol wraps payload in 0x30 tag and error code in 0x31.

In order to decode that, one would have to QCBORDecode_VPeekNext (since it's unknown what major type of the next item) next item into some one-shot QCBORItem variable (since QCBOR attaches tags to an item) and then use QCBORDecode_GetNthTag (since VPeekNext was used) to get the tag, then switch-case on the tag, handle unknown tag values and proceed with spiffy decode functions deeper into the structure. It gets worse if there are multiple nested tags.

I believe that a couple of new functions would ease the situation. One basic decode function, say QCBORDecode_GetTag(uint64_t* tag), that consumes next tag and fills out argument. And one function for spiffy decode, say QCBORDecode_EnsureTag(uint64_t tag) that consumes next tag and checks that it's equal to an argument.

If those functions are present one could write more linear code, something like:

QCBORDecode_EnsureTag(&ctx, TAG_SUCCESS);
QCBORDecode_EnterArray(&ctx);
// decode payload
if (QCBORDecode_GetError(&ctx) != QCBOR_SUCCESS) {
  QCBORDecode_Rewind(&ctx);
  // decode error
}

Instead of multiple nested switch-cases or ifs and a bunch of temp variables.

There doesn't appear to be a way to determine how much space is needed for encoding

qcbor_encode.h has a comment that says:

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 maximum 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.

I don't see a way to encode with no output buffer to compute the needed length. I can create the encoding context with a NULL UsefulBuf. But if I do this, QCBOREncode_Finish() and QCBOREncode_FinishGetSize() just return QCBOR_ERR_BUFFER_TOO_SMALL, and I don't see a way to get the required length. The comment says to look in the examples, but I don't see any of the example code doing the suggested double-invocation. Am I missing something?

Uninitialized data warnings

With GCC 10.2.1 / Linux:

/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_GetTaggedStringInMapSZ’:
/home/matt/src/QCBOR/src/qcbor_decode.c:3180:9: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3180 |       if(uDataType == puTypeList[i]) {
      |         ^
/home/matt/src/QCBOR/src/qcbor_decode.c:3287:14: note: ‘Item.uDataType’ was declared here
 3287 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3201:80: warning: ‘Item.uTags[0]’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3201 |    if(!(TagSpec.uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS) &&
/home/matt/src/QCBOR/src/qcbor_decode.c:3287:14: note: ‘Item.uTags[0]’ was declared here
 3287 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_EnterBstrWrappedFromMapN’:
/home/matt/src/QCBOR/src/qcbor_decode.c:3201:80: warning: ‘Item.uTags[0]’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3201 |    if(!(TagSpec.uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS) &&
/home/matt/src/QCBOR/src/qcbor_decode.c:3695:14: note: ‘Item.uTags[0]’ was declared here
 3695 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3209:14: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3209 |    const int nItemType = pItem->uDataType;
      |              ^~~~~~~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3695:14: note: ‘Item.uDataType’ was declared here
 3695 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_EnterBstrWrappedFromMapSZ’:
/home/matt/src/QCBOR/src/qcbor_decode.c:3180:9: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3180 |       if(uDataType == puTypeList[i]) {
      |         ^
/home/matt/src/QCBOR/src/qcbor_decode.c:3713:14: note: ‘Item.uDataType’ was declared here
 3713 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3201:80: warning: ‘Item.uTags[0]’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3201 |    if(!(TagSpec.uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS) &&
/home/matt/src/QCBOR/src/qcbor_decode.c:3713:14: note: ‘Item.uTags[0]’ was declared here
 3713 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_GetBoolInMapSZ’:
/home/matt/src/QCBOR/src/qcbor_decode.c:2747:4: warning: ‘*((void *)&Item+48)’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 2747 |    memcpy(pMe->uLastTags, pItem->uTags, sizeof(pItem->uTags));
      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3815:14: note: ‘*((void *)&Item+48)’ was declared here
 3815 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3763:4: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3763 |    switch(pItem->uDataType) {
      |    ^~~~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3815:14: note: ‘Item.uDataType’ was declared here
 3815 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_GetEpochDateInMapSZ’:
/home/matt/src/QCBOR/src/qcbor_decode.c:1947:44: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 1946 |          const double d = pDecodedItem->uDataType == QCBOR_TYPE_DOUBLE ?
      |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1947 |                    pDecodedItem->val.dfnum :
      |                    ~~~~~~~~~~~~~~~~~~~~~~~~^
 1948 |                    (double)pDecodedItem->val.fnum;
      |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3900:14: note: ‘Item.uDataType’ was declared here
 3900 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_GetEpochDaysInMapSZ’:
/home/matt/src/QCBOR/src/qcbor_decode.c:3180:9: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3180 |       if(uDataType == puTypeList[i]) {
      |         ^
/home/matt/src/QCBOR/src/qcbor_decode.c:3998:14: note: ‘Item.uDataType’ was declared here
 3998 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3948:12: warning: ‘Item.val.epochDays’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3948 |    *pnDays = pItem->val.epochDays;
      |    ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3998:14: note: ‘Item.val.epochDays’ was declared here
 3998 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_GetBignumInMapSZ’:
/home/matt/src/QCBOR/src/qcbor_decode.c:3180:9: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3180 |       if(uDataType == puTypeList[i]) {
      |         ^
/home/matt/src/QCBOR/src/qcbor_decode.c:4117:14: note: ‘Item.uDataType’ was declared here
 4117 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:3201:80: warning: ‘Item.uTags[0]’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 3201 |    if(!(TagSpec.uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS) &&
/home/matt/src/QCBOR/src/qcbor_decode.c:4117:14: note: ‘Item.uTags[0]’ was declared here
 4117 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c: In function ‘QCBORDecode_GetDoubleConvertAll’:
/home/matt/src/QCBOR/src/qcbor_decode.c:5326:14: warning: ‘Item.uDataType’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 5326 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:5268:36: warning: ‘Item.val.expAndMantissa.nExponent’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 5268 |             *pdValue = dMantissa * pow(10, (double)pItem->val.expAndMantissa.nExponent);
      |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:5326:14: note: ‘Item.val.expAndMantissa.nExponent’ was declared here
 5326 |    QCBORItem Item;
      |              ^~~~
/home/matt/src/QCBOR/src/qcbor_decode.c:5326:14: warning: ‘Item.val.bigNum.len’ may be used uninitialized in this function [-Wmaybe-uninitialized]
/home/matt/src/QCBOR/src/qcbor_decode.c:5326:14: warning: ‘Item.val.expAndMantissa.Mantissa.bigNum.len’ may be used uninitialized in this function [-Wmaybe-uninitialized]

Enhancement: release QCBOR as MDK Software Pack

When working with ARM Cortex-M, the integration of a library is much simpler if it's available as a MDK Software Pack.
Here you can see a list of paks, released from different companies/developers.
Here you can find the official specification oh how to create a software pack (from ARM Keil).
This is an unofficial video tutorial on how to create a pack.

I was able to integrate QCBOR without a pack, so it's only an idea for the future.
It could help some developers, but it's not mandatory.

Makefile does not work due to typos

Build fails with messages like:
make: *** No rule to make target 'inc/qcbor/Usefulbuf.h', needed by 'test/qcbor_encode_tests.o'. Stop.

I discovered some typos which are responsible for that.

Further issues:

  • PHONY targets are missing.
  • qcbormin target is not triggered automatically (a all target could fix that).

BstrWrapErrorTest failing on ARM FVP AN521 (release v1.0)

I am working on the TF-M project and trying to switch from a version of qcbor which was copied to the repo to an upstream version (v1.0 release). The test fails because the error is QCBOR_ERR_BUFFER_TOO_SMALL instead of QCBOR_ERR_ARRAY_NESTING_TOO_DEEP. The latest working version I managed to find is 8c858ab the next commit fails. I found that on the second last iteration of test/qcbor_encode_tests.c:1820 the EC.nesting.pCurrentNesting->uStart goes to 2831 which causes the if at src/UsefulBuf.c:248 to go in. You can try reproducing the issue using https://www.qemu.org/docs/master/system/arm/mps2.html mps2-an521. Let me know if you have issues reproducing this problem and I will help you.

`qcbor_err_to_str` is not included in library.

The function qcbor_err_to_str(...) from qcbor_err_to_str.c is not included in the resulting library.
In the header file qcbor_common.h the function declaration is present, but qcbor_err_to_str.o is not part of the Makefile.

determine how much of a buffer was decoded

Given that struct _QCBORDecodeContext is supposed to be private, is there a way to find out how many bytes from an input buffer were consumed? So that after processing one CBOR item from a stream, I know how far to advance my pointer to process the next chunk.
Yes, if I keep the QCBORDecodeContext around, then I can just continue, but in some cases I want to hide that part.

Fail to decode an indefinite-length string with a zero-length first chunk.

When decoding the following CBOR:

5F     # bytes(*)
   40  # bytes(0)
       # ""
   FF  # primitive(*)

QCBORDecode_GetNext() returns a QCBOR_ERR_STRING_ALLOCATE error.

Bug seems to be in QCBORDecode_GetNextFullString() in file qcbor_decode.c at line 1310:

      /* The first time throurgh FullString.ptr is NULL and this is
       * equivalent to StringAllocator_Allocate(). Subsequently it is
       * not NULL and a reallocation happens.
       */
      UsefulBuf NewMem = StringAllocator_Reallocate(pAllocator,
                                                    FullString.ptr,
                                                    FullString.len + StringChunkItem.val.string.len);

      if(UsefulBuf_IsNULL(NewMem)) {
         uReturn = QCBOR_ERR_STRING_ALLOCATE;
         break;
      }

As this is the first chunk, FullString.ptr is nil and FullString.len is 0. Since StringChunkItem.val.string.len is also 0, the allocator is called with a nil pOldMem and a uNewSize of 0. The allocator then returns NULLUsefulBuf which is interpreted as an error.

Test Code

const uint8_t spTestIssueInput[] = { 0x5F, 0x40, 0xFF };

int32_t CBORTestIssue()
{
   QCBORDecodeContext DCtx;
   QCBORItem          Item;
   QCBORError         uCBORError;

   QCBORDecode_Init(&DCtx,
                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTestIssueInput),
                    QCBOR_DECODE_MODE_NORMAL);

   UsefulBuf_MAKE_STACK_UB(StringBuf, 200);
   QCBORDecode_SetMemPool(&DCtx, StringBuf, false);
   
   do {
      uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
   } while (QCBOR_SUCCESS == uCBORError);

   if (QCBOR_SUCCESS == uCBORError)
   {
      uCBORError = QCBORDecode_Finish(&DCtx);
   }

   return uCBORError;
}

Preferred Serialization support

Although QCBOR does not do map sorting there is actually no problem generating preferred serialization:

Just for fun I patched example.c like the following:

    QCBOREncode_OpenMap(&EncodeCtx);
    QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
    QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders");
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
    for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
        QCBOREncode_AddDouble(&EncodeCtx,
                              pEngine->cylinders[i].dMeasuredCompression);
    }
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
    QCBOREncode_CloseArray(&EncodeCtx);
    QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
    QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
    QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
    QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
    QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
    QCBOREncode_CloseMap(&EncodeCtx);

This returned

a765547572626ff46943796c696e6465727386f94880fb4022666666666666fb4021cccccccccccdfb4021cccccccccccdfb4022333333333333f948806a486f727365706f77657218d26c446973706c6163656d656e74190ce06c4d616e75666163747572657267506f72736368656c4e756d43796c696e64657273067344657369676e6564436f6d7072657373696f6efb4022333333333333

which according to my on-line parser https://test.webpki.org/csf-lab/convert is correct (it doesn't accept anything but CBOR using preferred serialization).

When I changed one of the cylinder arguments to 5.9604644775390625e-8 it failed but that was expected since QCBOR does not yet offer full support for preferred serialization of floating point values.

How to decode null terminated string?

I am new to CBOR and also the QCBOR library, so please excuse my ignorance in advance.

I can see that QCBOR supports the encode/decode of text by the use of a UsefulBufC struct:

static void QCBOREncode_AddText(QCBOREncodeContext* pCtx, UsefulBufC Text);

I can also see that QCBOR appears to support the encode of basic null terminated data:

static void QCBOREncode_AddSZString(QCBOREncodeContext* pCtx, const char* szString);

However, I don't see an associated decode function for QCBOREncode_AddSZString. Is this a missing feature?

OSS-Fuzz integration

Hello!

I'm an application security engineer at Google who contributes to Google's fuzzing efforts. I've written a simple fuzzer for QCBOR based on the ComprehensiveInputRecurser test in qcbor_decode_tests.c, and I'd like to know if you're interested in adding it directly to the QCBOR repository. Once it's in this repository, I can integrate it with OSS-Fuzz so that you automatically receive bugs that our fuzzers find -- all you'll need is an e-mail address associated with a Google account (here's why).

Please let me know if you're interested, and if so, how'd you prefer to add the file to this repository. The fuzzer itself is less than 20 lines long, but does require special CFLAGS to build

QCBOREncode_Init buffer

Hi,

This is not an issue but I don't know where I can ask question about the API.

So I am trying to use qcbor using a simple structure but it's difficult to understand how the API works just by reading the source. I'm stuck in the beginning with the second parameter of QCBOREncode_Init function.

This parameter is supposed to be the buffer after encoding. Looking at example.c and test/qcbor_encode_tests.c the size of the buffer doesn't seem to be calculated. For one case, the declaration is static uint8_t spBigBuf[2200] and for the other case MakeUsefulBufOnStack( EngineBuffer, 300).

Is there a way to give the real size for the buffer ?

I have another question. In which case should I use MakeUsefulBufOnStack or UsefulBuf_FROM_BYTE_ARRAY ?

const-bursting code creates warnings that really are an issue

The way that UsefulBufC is used as a result is a real problem.
It takes const item, and then changes it. This violates the const guarantees that optimizers use.
I see no advantage to this part. I propose to remove UsefulBufC, and just #define UsefulBufC UsefulBuf.

can I tag arrays opened within Maps?

if I do:
QCBOREncode_OpenArrayInMapN(qec, keyint);

can I then call
QCBOREncode_AddTag(qec, MyTag);

to insert a tag before the array is started?

UsefulBuf.h gives warnings when used with MSVC

UsefulBuf.h(677,9): warning C4068: unknown pragma 'GCC'
UsefulBuf.h(678,9): warning C4068: unknown pragma 'GCC'
UsefulBuf.h(680,9): warning C4068: unknown pragma 'GCC'
UsefulBuf.h(1760,9): warning C4068: unknown pragma 'GCC'
UsefulBuf.h(1761,9): warning C4068: unknown pragma 'GCC'
UsefulBuf.h(1763,9): warning C4068: unknown pragma 'GCC'

Any GCC pragmas should be inside something like
#ifdef GNUC
...
#endif

Writing an empty null buffer triggers undefined behaviour.

Writing an empty but null buffer to a QCBOR encoder context causes a call to memmove with a NULL pointer as its second argument, which is not allowed, even when the size is 0 (see eg. https://www.imperialviolet.org/2016/06/26/nonnull.html).
This causes sanitizers to raise errors.

Simple reproduction:

#include <qcbor/qcbor.h>
int main() {
  UsefulBuf_MAKE_STACK_UB(buffer, 16);

  QCBOREncodeContext context;
  QCBOREncode_Init(&context, buffer);
  QCBOREncode_AddBytes(&context, NULLUsefulBufC);
  return 0;
}
$ clang main.c -o main -fsanitize=undefined,address -lqcbor
$ ./main
QCBOR/src/UsefulBuf.c:271:32: runtime error: null pointer passed as argument 2, which is declared to never be null
/usr/include/string.h:48:14: note: nonnull attribute specified here
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior QCBOR/src/UsefulBuf.c:271:32 in

Full stack trace:

    #0 0x4f3270 in UsefulOutBuf_InsertUsefulBuf QCBOR/src/UsefulBuf.c:271
    #1 0x4f1289 in UsefulOutBuf_AppendUsefulBuf QCBOR/inc/qcbor/UsefulBuf.h:2100
    #2 0x4f1289 in QCBOREncode_AddBuffer QCBOR/src/qcbor_encode.c:708
    #3 0x4f06c1 in QCBOREncode_AddBytes QCBOR/inc/qcbor/qcbor_encode.h:2412
    #4 0x4f06c1 in main main.c:8
    #5 0x7fe66a171236 in __libc_start_call_main ??:?
    #6 0x7fe66a1712f4 in __libc_start_main_alias_2 ??:?
    #7 0x41b3a0 in _start /build/glibc-2.34/csu/../sysdeps/x86_64/start.S:116

Update not-well-formed test

Now that CBORbis is incorporating the not-well-formed test vectors, make sure the tests here are in sync with them. Should be very close, as they originated here.

Bugs in ieee754.c

During testing of the D-CBOR concept I ported a Java encoder to C. Just for fun I adjusted it a bit to test with the QCBOR test suite. However, "DoubleAsSmallestTest" failed. After some debugging it turned out that the master table is incorrect. I have verified my findings with https://cbor.me as well.

static const uint8_t spExpectedSmallest[] = {
 // AR Updated:
      0xF9, 0x00, 0x01,
 // AR Removed:
 //     0xFA, 0x33, 0x80, 0x00, 0x00,

 // AR Updated:
      0xF9, 0x02, 0x00,
 // AR Removed:
 //     0xFA, 0x38, 0x00, 0x00, 0x00,

 // AR updated:
      0xFA, 0x00, 0x40, 0x00, 0x00,
 // AR removed:
 //    0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

};

The enclosed file contains the method I removed from ieee754.c in order to test with my code added to the file set. It appears to generate less code than the original IEEE754_DoubleToSmallestInternal. 324 bytes versus 476 using gcc on Linux64.

d-cbor-ieee754.txt

CBOR Canonical Encoding

The documentation says the all of RFC 7049 is supported except strict mode, however I don't see any code to support sorting of map keys according to section 3.9 of the RFC. Am I incorrectly using things or is this case not covered. Could you provide a short example if it is? Thanks!

Add a method to skip over an item

The value of the item is not returned.

The item can be a deeply nested tagged structure or an integer. Skipping works no matter what it is.

3 Tests Failing on ARM Cortex-M4 CPU

I'm trying to run QCBOR on an ARM Cortex-M4 CPU and the following 3 failures.

NotWellFormedTests FAILED (returned 2079)ParseMapAsArrayTest PASSED
DecodeFailureTests FAILED (returned 9620) BstrWrapErrorTest FAILED (returned -3)`

I'm using the latest master branch c02e13e and I have the following configurations defined
QCBOR_DISABLE_FLOAT_HW_USE
USEFULBUF_CONFIG_LITTLE_ENDIAN
QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
QCBOR_DISABLE_PREFERRED_FLOAT

I've tried to determine the cause of the failures but I haven't been able to pin it down.

Thanks

possible problems with empty map in map --- how to submit unit test

I am trying to decode the following map:

A1    # map(1)
   02 # unsigned(2)
   A0 # map(0)

i.e.: {2: {}}

It seems that QCBORDecode_GetNext(&qdc, &item) returns with item.label.int64 as zero.
I think that this might be because of the empty map as the value. I'm not really sure.

I guess an example ought to go into tests/qcbor_decode_tests.c, but it would sure be nicer if this could be a single test per file somehow.

Need BKM to use CBOR

Hi., I'm looking for best known method for CBOR.

use case. i have a large structure, and i wanted to convert that structure to CBOR binary,
during the runtime i can locate the buffer and i can get the data.

Build error if ARM microlib is used

I know ARM microlib is not compliant with the ISO C library standard, and so I don't know how much interest there is in having QCBOR working if ARM microlib is used.
qcbor_decode.c includes <fenv.h> to use feclearexcept() and fetestexcept()

#include <fenv.h> /* feclearexcept(), fetestexcept() */

The problem is that microlib does not support feclearexcept() and fetestexcept() (see here).
Is it possible to add an
#ifdef __MICROLIB
to behave in different way if microlib is used?

QCBOR_Int64ToUInt64 returns -1

The helper function QCBOR_Int64ToUInt64 in qcbor_decode.h

static inline int QCBOR_Int64ToUInt64(int64_t src, uint64_t *dest)
{
   if(src > 0) {
      return -1;
   } else {
      *dest = (uint64_t) src;
   }
   return 0;
}

return -1 for positive int64_t. Is it possible that the first check is just in den wrong direction?
if(src < 0) should do the trick? Or maybe I understand something wrong.

Thanks for a short feedback and many thanks for this great library.

Make sure items are not tags

If getting an item of an expected major type, it seems one should check that there is no tag in front of it unless it is one of the tags that is OK to ignore.

Need to test this, document it, maybe fix some bugs.

Add support for map sorting during encoding

While I have an idea of how to support sorting of maps by label when encoding, it is probably a ways off. It will complicate the encoder a lot and require a string allocator. It also doesn't seem critical as sorted maps shouldn't be relied upon by protocols. However, it would be nice to get closer to complete support for RFC 7049.

Dependency on fenv.h makes float HW use with libm impossible

The hardware float functions used from fenv.h are not portable or usable on platforms that use newlib since newlib's math library libm does not implement fenv as stated in their documentation.

The aforementioned error reporting is the supported Newlib libm error handling method. However, the majority of the functions are written so as to produce the floating-point exceptions (e.g. "invalid", "divide-by-zero") as required by the C and POSIX standards, for floating-point implementations that support them. Newlib does not provide the floating-point exception access routines defined in the standards for fenv.h, though, which is why they are considered unsupported. It is mentioned in case you have separately-provided access routines so that you are aware that they can be caused.

emphasis mine

The only way to compile this library on platforms that use libm is to disable hw float support. This is fine in any encoding use cases since hw float libraries are not used, but when decoding this can cause platforms that have hardware accelerated math.h to lose out on float functionality entirely. The specific use case where this shines is when using it in a project running the raspberry pi pico sdk which depends on newlib

Possible fixes would to be to add alternative libm compatible implementations of the dependent functions allowing the user to switch implementation with a #define, or to change the current implementation to be portable across libm and non-libm systems.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.