From 1aea04d36004bd912b0f88b6b88ee60661a34cc1 Mon Sep 17 00:00:00 2001 From: Morten Ensted Date: Sun, 23 Oct 2022 20:05:26 -0400 Subject: [PATCH 1/4] For-loop copy to build up 64-bit --- src/decoder.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/decoder.c b/src/decoder.c index 197a6c7..e578d55 100644 --- a/src/decoder.c +++ b/src/decoder.c @@ -121,12 +121,14 @@ static int _get_uint64(const nanocbor_value_t *cvalue, uint32_t *value, uint_lea return NANOCBOR_ERR_END; } uint64_t tmp = 0; + uint_fast64_t val64; /* Copy the value from cbor to the least significant bytes */ - memcpy(((uint_least8_t *)&tmp) + sizeof(uint64_t) - bytes, cvalue->cur + 1U, bytes); - /* NOLINTNEXTLINE: user supplied function */ - tmp = NANOCBOR_BE64TOH_FUNC(tmp); - *value = 0; - memcpy(value, &tmp, bytes); + /* for-loop to handle excotic cpu arthitectures, eg. no 8-bit support*/ + for (uint_fast8_t i = 0; i < bytes; i++) { + val64 = 0x00000000000000ffull & *((uint_fast8_t*)(cvalue->cur + 1 + i)); + tmp |= ( val64<<(8 * (bytes - i - 1)) ); + } + *value = tmp; return (int)(1 + bytes); } From 29fe59a001e44ae09caa1956984294c8c4172e96 Mon Sep 17 00:00:00 2001 From: ainsworthkohler <122799479+ainsworthkohler@users.noreply.github.com> Date: Mon, 16 Jan 2023 10:29:23 -0500 Subject: [PATCH 2/4] addingSubmod addingSubmod --- .cirrus.yml | 50 -- .gitignore | 17 - LICENSE | 121 --- Makefile | 48 - README.md | 191 ---- doxyfile | 372 -------- include/nanocbor/config.h | 87 -- include/nanocbor/nanocbor.h | 823 ------------------ .../nanocbor/stream_encoders/memory_buffer.h | 23 - src/decoder.c | 455 ---------- src/encoder.c | 382 -------- src/memory_buffer.c | 31 - tests/automated/Makefile | 16 - tests/automated/main.c | 56 -- tests/automated/test.h | 29 - tests/automated/test_decoder.c | 217 ----- tests/automated/test_encoder.c | 127 --- tests/encode/Makefile | 12 - tests/encode/main.c | 64 -- tests/encode/out.txt | Bin 300 -> 0 bytes tests/fuzz/Makefile | 12 - tests/fuzz/README.md | 17 - tests/fuzz/inputs/array.cbor | 1 - tests/fuzz/inputs/complex.cbor | Bin 37 -> 0 bytes tests/fuzz/inputs/map.cbor | 1 - tests/fuzz/inputs/random.cbor | Bin 193 -> 0 bytes tests/fuzz/inputs/simle.cbor | 1 - tests/fuzz/main.c | 193 ---- uncrustify.cfg | 69 -- 29 files changed, 3415 deletions(-) delete mode 100644 .cirrus.yml delete mode 100644 .gitignore delete mode 100644 LICENSE delete mode 100644 Makefile delete mode 100644 README.md delete mode 100644 doxyfile delete mode 100644 include/nanocbor/config.h delete mode 100644 include/nanocbor/nanocbor.h delete mode 100644 include/nanocbor/stream_encoders/memory_buffer.h delete mode 100644 src/decoder.c delete mode 100644 src/encoder.c delete mode 100644 src/memory_buffer.c delete mode 100644 tests/automated/Makefile delete mode 100644 tests/automated/main.c delete mode 100644 tests/automated/test.h delete mode 100644 tests/automated/test_decoder.c delete mode 100644 tests/automated/test_encoder.c delete mode 100644 tests/encode/Makefile delete mode 100644 tests/encode/main.c delete mode 100644 tests/encode/out.txt delete mode 100644 tests/fuzz/Makefile delete mode 100644 tests/fuzz/README.md delete mode 100644 tests/fuzz/inputs/array.cbor delete mode 100644 tests/fuzz/inputs/complex.cbor delete mode 100644 tests/fuzz/inputs/map.cbor delete mode 100644 tests/fuzz/inputs/random.cbor delete mode 100644 tests/fuzz/inputs/simle.cbor delete mode 100644 tests/fuzz/main.c delete mode 100644 uncrustify.cfg diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index e52c59d..0000000 --- a/.cirrus.yml +++ /dev/null @@ -1,50 +0,0 @@ -container: - cpu: 1 - memory: 128Mb - -lint_task: - container: - image: silkeh/clang:latest - test_script: make clang-tidy - -build_task: - depends_on: - - lint - install_script: - - apt update - - apt install -y libcunit1-dev - matrix: - - name: clang - env: - CC: clang - container: - matrix: - image: silkeh/clang:7 - image: silkeh/clang:8 - install_libgcc_script: - - apt install -y libgcc-9-dev - - name: gcc - env: - CC: gcc - container: - matrix: - image: gcc:8 - image: gcc:9 - test_script: - - make -C tests/automated clean test - - make bin/nanocbor.so - - -arm_task: - container: - image: hiberglobal/gcc-arm-none-eabi:8-2018-q4-major - env: - CFLAGS: -mno-thumb-interwork -mcpu=cortex-m0plus -mlittle-endian -mthumb -mfloat-abi=soft -march=armv6s-m -DNANOCBOR_BYTEORDER_HEADER=\"stddef.h\" -DNANOCBOR_BE64TOH_FUNC=__builtin_bswap64 -DNANOCBOR_HTOBE64_FUNC=__builtin_bswap64 -DNANOCBOR_HTOBE32_FUNC=__builtin_bswap32 - CC: arm-none-eabi-gcc - install_script: - - apk add make - test_script: - - make objs - - arm-none-eabi-size bin/objs/* > output.txt - size_artifacts: - path: output.txt diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8463cac..0000000 --- a/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# C build files -*.o -build -bin - -# Editor related -tags -*.swp -*.swo - -# docs -docs - - -# other -outputs -core.* diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0e259d4..0000000 --- a/LICENSE +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/Makefile b/Makefile deleted file mode 100644 index 6d569ef..0000000 --- a/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -NANOCBOR_DIR ?= $(CURDIR) - -# cannot simply use CC ?= gcc, as CC defaults to "cc" if undefined on GNU Make -ifeq ($(origin CC),default) - CC := gcc -endif -RM = rm -rf -TIDY ?= clang-tidy -CFLAGS ?= - -INC_GLOBAL ?= /usr/include -INC_DIR = $(NANOCBOR_DIR)/include -SRC_DIR = $(NANOCBOR_DIR)/src - -TEST_DIR=tests - -BIN_DIR ?= bin -OBJ_DIR ?= $(BIN_DIR)/objs - -# Only check at issues present for c99 compatible code -CFLAGS_TIDY ?= -std=c99 -TIDYFLAGS=-checks=*,-llvmlibc-restrict-system-libc-headers,-bugprone-reserved-identifier,-cert-* -warnings-as-errors=* - -CFLAGS_WARN += -Wall -Wextra -pedantic -Werror -Wshadow -CFLAGS += -fPIC $(CFLAGS_WARN) -I$(INC_DIR) -I$(INC_GLOBAL) -Og -g3 - -SRCS ?= $(wildcard $(SRC_DIR)/*.c) -OBJS ?= $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS)) - -lib: $(BIN_DIR)/nanocbor.so - -prepare: - @mkdir -p $(OBJ_DIR) - -# Build a binary -$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c prepare - $(CC) $(CFLAGS) -c -o $@ $< - -objs: $(OBJS) - -$(BIN_DIR)/nanocbor.so: objs - $(CC) $(CFLAGS) $(OBJS) -o $@ -shared - -clang-tidy: - $(TIDY) $(TIDYFLAGS) $(SRCS) -- $(CFLAGS) $(CFLAGS_TIDY) - -clean: - $(RM) $(BIN_DIR) diff --git a/README.md b/README.md deleted file mode 100644 index 6d73fa2..0000000 --- a/README.md +++ /dev/null @@ -1,191 +0,0 @@ -# NanoCBOR - -NanoCBOR is a tiny [CBOR](https://tools.ietf.org/html/rfc7049) library aimed at embedded and heavily constrained devices. -It is optimized for 32 bit architectures but should run fine on 8 bit and 16 bit architectures. -NanoCBOR is optimized for decoding known CBOR structures while optimizing the flash footprint of both NanoCBOR and the code using NanoCBOR. - -The decoder of NanoCBOR should compile to 600-800 bytes on a Cortex-M0+ MCU, depending on whether floating point decoding is required. - -## Usage - -To achieve the small code size, two patterns are used throughout the decode library. - - - Every decode call will first check the type and refuse to decode if the CBOR element is not of the required type. - - Every decode call will, on successful decode, advance the decode context to the next CBOR element. - -This allows using code to call decode functions and check the return code of the function without requiring an if value of type, decode value, advance to next item dance, and requiring only a single call to decode an expected type and advance to the next element. - -### Decoding - -Start the decoding of a buffer with: - -```C -nanocbor_value_t decoder; -nanocbor_decoder_init(&decoder, buffer, buffer_len); -``` - -Where `buffer` is an `const uint_least8_t` array containing an CBOR structure. - -To decode an `int32_t` from a cbor structure and bail out if the element is not of the integer type: - -```C -int32_t value = 0; -if (nanocbor_get_int32(&decoder, &value) < 0) { - return ERR_INVALID_STRUCTURE; -} -return use_value(value); -``` - -Iterating over an CBOR array and calling a function passing every element is as simple as: - -```C -nanocbor_value_t arr; /* Array value instance */ - -if (nanocbor_enter_array(&decoder, &arr) < 0) { - return ERR_INVALID_STRUCTURE; -} -while (!nanocbor_at_end(&arr)) { - handle_array_element(&arr); -} -``` - -Decoding a map is similar to an array, except that every map entry consists of two CBOR elements requiring separate decoding. -For example, a map using integers as keys and strings as values can be decoded with: - -```C -while (!nanocbor_at_end(&map)) { - int32_t key; - const char *value; - size_t value_len; - if (nanocbor_get_int32(&map, &integer_key) < 0) { - return ERR_INVALID_STRUCTURE; - } - if (nanocbor_get_tstr(&map, &value, &value_len) < 0) { - return ERR_INVALID_STRUCTURE; - } - handle_map_element(key, value, value_len); -} -``` - -### Encoding - -NanoCBOR supports encoding via a polymorphic stream interface via function -pointers. This methods on this interface are: - -``` -typedef size_t (*FnStreamLength)(void *stream); -typedef int (*FnStreamReserve)(void *stream, size_t len); -typedef void (*FnStreamInsert)(void *stream, const void *src, size_t n); -``` - -Where `FnStreamLength` queries the stream about how many CBOR bytes have been -provided, `FnStreamReserve` reserves the requested number of bytes within the -stream, and `FnStreamInsert` moves bytes into the stream. Each of these -functions accept a `stream` object, which contains the stream state if one is -needed. This is exactly like the "this" pointer in other object oriented -languages. - -The `memory_buffer.h/c` is the canonical stream encoder, it expected a large -memory buffer that it can `memcpy` CBOR data into. - -Construction of these objects looks like: - -``` -uint_least8_t buf[64]; -memory_encoder stream; -MemoryStream_Init(&stream, buf, sizeof(buf)); - -FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; -FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; -FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; - -nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); -``` - -With a valid `nanocbor_encoder_t` you can pass it to the `fmt` functions: - -``` -nanocbor_fmt_array_indefinite(&enc); -nanocbor_fmt_float(&enc, 1.75); -nanocbor_fmt_float(&enc, 1.9990234375); -nanocbor_fmt_float(&enc, 1.99951171875); -nanocbor_fmt_float(&enc, 2.0009765625); -nanocbor_fmt_float(&enc, -1.75); -nanocbor_fmt_float(&enc, -1.9990234375); -nanocbor_fmt_float(&enc, -1.99951171875); -nanocbor_fmt_float(&enc, -2.0009765625); -nanocbor_fmt_end_indefinite(&enc); -``` - -The encoder places bytes in the stream on demand, so after you are done you can -find your encoded CBOR data within the buffer you created: - -``` -size_t len = nanocbor_encoded_len(&enc); - -printf("\n"); -for(unsigned int idx=0; idx < len; idx++) -{ - printf("%02X", buf[idx]); -} -printf("\n"); -``` - -#### Implementing a new stream - -Here is an example of a `stdout` stream and how to use it: - -``` -typedef struct stdout_stream { - int len; -} stdout_stream; - -void Stdout_Init(stdout_stream *self) { - self->len = 0; -} - -size_t Stdout_Length(stdout_stream *self) { - return self->len; -} - -int Stdout_Reserve(stdout_stream *self, size_t len) { - self->len += len; - return (int)len; -} - -void Stdout_Insert(stdout_stream *self, const void *src, size_t n) { - const uint_least8_t *bytes = (const uint_least8_t *)src; - for (size_t i = 0; i < n; ++i) { - printf("%02X ", bytes[i]); - } -} - -void encode(void) { - stdout_stream stream; - Stdout_Init(&stream); - - FnStreamLength len_fn = (FnStreamLength)Stdout_Length; - FnStreamReserve res_fn = (FnStreamReserve)Stdout_Reserve; - FnStreamInsert ins_fn = (FnStreamInsert)Stdout_Insert; - - nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); - nanocbor_fmt_array_indefinite(&enc); - nanocbor_fmt_float(&enc, 1.75); - nanocbor_fmt_float(&enc, 1.9990234375); - nanocbor_fmt_float(&enc, 1.99951171875); - nanocbor_fmt_float(&enc, 2.0009765625); - nanocbor_fmt_end_indefinite(&enc); -} -``` - - -### Dependencies - -Only dependency are two functions to provide endian conversion. -These are not provided by the library and have to be configured in the header file. -On a bare metal ARM platform, `__builtin_bswap64` and `__builtin_bswap32` can be used for this conversion. - -### Contributing - -Open an issue, PR, the usual. - diff --git a/doxyfile b/doxyfile deleted file mode 100644 index 3a85d3a..0000000 --- a/doxyfile +++ /dev/null @@ -1,372 +0,0 @@ -# Doxyfile 1.8.13 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = "NanoCBOR" -PROJECT_NUMBER = -PROJECT_BRIEF = -PROJECT_LOGO = -OUTPUT_DIRECTORY = docs -CREATE_SUBDIRS = YES -ALLOW_UNICODE_NAMES = NO -OUTPUT_LANGUAGE = English -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the -ALWAYS_DETAILED_SEC = YES -INLINE_INHERITED_MEMB = NO -FULL_PATH_NAMES = NO -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = -SHORT_NAMES = NO -JAVADOC_AUTOBRIEF = NO -QT_AUTOBRIEF = NO -MULTILINE_CPP_IS_BRIEF = NO -INHERIT_DOCS = YES -SEPARATE_MEMBER_PAGES = NO -TAB_SIZE = 4 -ALIASES = -TCL_SUBST = -OPTIMIZE_OUTPUT_FOR_C = YES -OPTIMIZE_OUTPUT_JAVA = NO -OPTIMIZE_FOR_FORTRAN = NO -OPTIMIZE_OUTPUT_VHDL = NO -EXTENSION_MAPPING = -MARKDOWN_SUPPORT = YES -TOC_INCLUDE_HEADINGS = 0 -AUTOLINK_SUPPORT = YES -BUILTIN_STL_SUPPORT = NO -CPP_CLI_SUPPORT = NO -SIP_SUPPORT = NO -IDL_PROPERTY_SUPPORT = YES -DISTRIBUTE_GROUP_DOC = NO -GROUP_NESTED_COMPOUNDS = NO -SUBGROUPING = YES -INLINE_GROUPED_CLASSES = NO -INLINE_SIMPLE_STRUCTS = NO -TYPEDEF_HIDES_STRUCT = NO -LOOKUP_CACHE_SIZE = 0 -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- -EXTRACT_ALL = NO -EXTRACT_PRIVATE = NO -EXTRACT_PACKAGE = NO -EXTRACT_STATIC = YES -EXTRACT_LOCAL_CLASSES = YES -EXTRACT_LOCAL_METHODS = NO -EXTRACT_ANON_NSPACES = NO -HIDE_UNDOC_MEMBERS = NO -HIDE_UNDOC_CLASSES = NO -HIDE_FRIEND_COMPOUNDS = NO -HIDE_IN_BODY_DOCS = NO -INTERNAL_DOCS = NO -CASE_SENSE_NAMES = YES -HIDE_SCOPE_NAMES = NO -HIDE_COMPOUND_REFERENCE= NO -SHOW_INCLUDE_FILES = YES -SHOW_GROUPED_MEMB_INC = NO -FORCE_LOCAL_INCLUDES = NO -INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = NO -SORT_MEMBERS_CTORS_1ST = NO -SORT_GROUP_NAMES = NO -SORT_BY_SCOPE_NAME = NO -STRICT_PROTO_MATCHING = NO -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES -GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = -MAX_INITIALIZER_LINES = 30 -SHOW_USED_FILES = YES -SHOW_FILES = YES -SHOW_NAMESPACES = YES -FILE_VERSION_FILTER = -LAYOUT_FILE = -CITE_BIB_FILES = -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -QUIET = NO -WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES -WARN_IF_DOC_ERROR = YES -WARN_NO_PARAMDOC = YES -WARN_AS_ERROR = NO -WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = include/ README.md -INPUT_ENCODING = UTF-8 -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.idl \ - *.ddl \ - *.odl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.cs \ - *.d \ - *.php \ - *.php4 \ - *.php5 \ - *.phtml \ - *.inc \ - *.m \ - *.markdown \ - *.md \ - *.mm \ - *.dox \ - *.py \ - *.pyw \ - *.f90 \ - *.f95 \ - *.f03 \ - *.f08 \ - *.f \ - *.for \ - *.tcl \ - *.vhd \ - *.vhdl \ - *.ucf \ - *.qsf -RECURSIVE = YES -EXCLUDE = -EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = -EXCLUDE_SYMBOLS = -EXAMPLE_PATH = -EXAMPLE_PATTERNS = * -EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = -FILTER_PATTERNS = -FILTER_SOURCE_FILES = NO -FILTER_SOURCE_PATTERNS = -USE_MDFILE_AS_MAINPAGE = README.md -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = NO -REFERENCES_RELATION = NO -REFERENCES_LINK_SOURCE = YES -SOURCE_TOOLTIPS = YES -USE_HTAGS = NO -VERBATIM_HEADERS = YES -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- -ALPHABETICAL_INDEX = YES -COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = YES -HTML_OUTPUT = . -HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_FOOTER = -HTML_STYLESHEET = -HTML_EXTRA_STYLESHEET = -HTML_EXTRA_FILES = -HTML_COLORSTYLE_HUE = 220 -HTML_COLORSTYLE_SAT = 100 -HTML_COLORSTYLE_GAMMA = 80 -HTML_TIMESTAMP = NO -HTML_DYNAMIC_SECTIONS = NO -HTML_INDEX_NUM_ENTRIES = 100 -GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Doxygen generated docs" -DOCSET_BUNDLE_ID = org.doxygen.Project -DOCSET_PUBLISHER_ID = org.doxygen.Publisher -DOCSET_PUBLISHER_NAME = Publisher -GENERATE_HTMLHELP = NO -CHM_FILE = -HHC_LOCATION = -GENERATE_CHI = NO -CHM_INDEX_ENCODING = -BINARY_TOC = NO -TOC_EXPAND = NO -GENERATE_QHP = NO -QCH_FILE = -QHP_NAMESPACE = org.doxygen.Project -QHP_VIRTUAL_FOLDER = doc -QHP_CUST_FILTER_NAME = -QHP_CUST_FILTER_ATTRS = -QHP_SECT_FILTER_ATTRS = -QHG_LOCATION = -GENERATE_ECLIPSEHELP = NO -ECLIPSE_DOC_ID = org.doxygen.Project -DISABLE_INDEX = NO -GENERATE_TREEVIEW = NO -ENUM_VALUES_PER_LINE = 4 -TREEVIEW_WIDTH = 250 -EXT_LINKS_IN_WINDOW = NO -FORMULA_FONTSIZE = 10 -FORMULA_TRANSPARENT = YES -USE_MATHJAX = NO -MATHJAX_FORMAT = HTML-CSS -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest -MATHJAX_EXTENSIONS = -MATHJAX_CODEFILE = -SEARCHENGINE = YES -SERVER_BASED_SEARCH = NO -EXTERNAL_SEARCH = NO -SEARCHENGINE_URL = -SEARCHDATA_FILE = searchdata.xml -EXTERNAL_SEARCH_ID = -EXTRA_SEARCH_MAPPINGS = -#--------------------------------------------------------------------------- -# Configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = NO -LATEX_OUTPUT = latex -LATEX_CMD_NAME = latex -MAKEINDEX_CMD_NAME = makeindex -COMPACT_LATEX = NO -PAPER_TYPE = a4 -EXTRA_PACKAGES = -LATEX_HEADER = -LATEX_FOOTER = -LATEX_EXTRA_STYLESHEET = -LATEX_EXTRA_FILES = -PDF_HYPERLINKS = YES -USE_PDFLATEX = YES -LATEX_BATCHMODE = NO -LATEX_HIDE_INDICES = NO -LATEX_SOURCE_CODE = NO -LATEX_BIB_STYLE = plain -LATEX_TIMESTAMP = NO -#--------------------------------------------------------------------------- -# Configuration options related to the RTF output -#--------------------------------------------------------------------------- -GENERATE_RTF = NO -RTF_OUTPUT = rtf -COMPACT_RTF = NO -RTF_HYPERLINKS = NO -RTF_STYLESHEET_FILE = -RTF_EXTENSIONS_FILE = -RTF_SOURCE_CODE = NO -#--------------------------------------------------------------------------- -# Configuration options related to the man page output -#--------------------------------------------------------------------------- -GENERATE_MAN = NO -MAN_OUTPUT = man -MAN_EXTENSION = .3 -MAN_SUBDIR = -MAN_LINKS = NO -#--------------------------------------------------------------------------- -# Configuration options related to the XML output -#--------------------------------------------------------------------------- -GENERATE_XML = NO -XML_OUTPUT = xml -XML_PROGRAMLISTING = YES -#--------------------------------------------------------------------------- -# Configuration options related to the DOCBOOK output -#--------------------------------------------------------------------------- -GENERATE_DOCBOOK = NO -DOCBOOK_OUTPUT = docbook -DOCBOOK_PROGRAMLISTING = NO -#--------------------------------------------------------------------------- -# Configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- -GENERATE_AUTOGEN_DEF = NO -#--------------------------------------------------------------------------- -# Configuration options related to the Perl module output -#--------------------------------------------------------------------------- -GENERATE_PERLMOD = NO -PERLMOD_LATEX = NO -PERLMOD_PRETTY = YES -PERLMOD_MAKEVAR_PREFIX = -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = NO -EXPAND_ONLY_PREDEF = NO -SEARCH_INCLUDES = YES -INCLUDE_PATH = -INCLUDE_FILE_PATTERNS = -PREDEFINED = -EXPAND_AS_DEFINED = -SKIP_FUNCTION_MACROS = YES -#--------------------------------------------------------------------------- -# Configuration options related to external references -#--------------------------------------------------------------------------- -TAGFILES = -GENERATE_TAGFILE = -ALLEXTERNALS = NO -EXTERNAL_GROUPS = YES -EXTERNAL_PAGES = YES -PERL_PATH = /usr/bin/perl -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES -MSCGEN_PATH = -DIA_PATH = -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = NO -DOT_NUM_THREADS = 0 -DOT_FONTNAME = Helvetica -DOT_FONTSIZE = 10 -DOT_FONTPATH = -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -GROUP_GRAPHS = YES -UML_LOOK = NO -UML_LIMIT_NUM_FIELDS = 10 -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -CALLER_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = png -INTERACTIVE_SVG = NO -DOT_PATH = -DOTFILE_DIRS = -MSCFILE_DIRS = -DIAFILE_DIRS = -PLANTUML_JAR_PATH = -PLANTUML_CFG_FILE = -PLANTUML_INCLUDE_PATH = -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 0 -DOT_TRANSPARENT = NO -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES diff --git a/include/nanocbor/config.h b/include/nanocbor/config.h deleted file mode 100644 index 84103e6..0000000 --- a/include/nanocbor/config.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -/** - * @defgroup nanocbor_config NanoCBOR configuration header - * @brief Provides compile-time configuration for nanocbor - * - * @{ - * - * @file - * - * @author Koen Zandberg - */ - -#ifndef NANOCBOR_CONFIG_H -#define NANOCBOR_CONFIG_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Recursion limit when using @ref nanocbor_skip. - */ -#ifndef NANOCBOR_RECURSION_MAX -#define NANOCBOR_RECURSION_MAX 10 -#endif - -/** - * @brief library providing htonll, be64toh or equivalent. Must also provide - * the reverse operation (ntohll, htobe64 or equivalent) - */ -#ifndef NANOCBOR_BYTEORDER_HEADER -#define NANOCBOR_BYTEORDER_HEADER "endian.h" -#endif - -/** - * @brief call providing htonll or be64toh or equivalent functionality - * - * must take a uint64_t big endian and return it in host endianess - */ -#ifndef NANOCBOR_BE64TOH_FUNC -#define NANOCBOR_BE64TOH_FUNC(be) (be64toh(be)) -#endif - -/** - * @brief call providing htonll or htobe64 or equivalent functionality - * - * must take a uint64_t in host endianess and return the big endian value - */ -#ifndef NANOCBOR_HTOBE64_FUNC -#define NANOCBOR_HTOBE64_FUNC(he) htobe64(he) -#endif - -/** - * @brief call providing htonl or htobe32 or equivalent functionality - * - * must take a uint32_t in host endianess and return the big endian value - */ -#ifndef NANOCBOR_HTOBE32_FUNC -#define NANOCBOR_HTOBE32_FUNC(he) htobe32(he) -#endif - -/** - * @brief configuration for size_t SIZE_MAX equivalent - */ -#ifndef NANOCBOR_SIZE_SIZET -#if (SIZE_MAX == UINT16_MAX) -#define NANOCBOR_SIZE_SIZET NANOCBOR_SIZE_SHORT -#elif (SIZE_MAX == UINT32_MAX) -#define NANOCBOR_SIZE_SIZET NANOCBOR_SIZE_WORD -#elif (SIZE_MAX == UINT64_MAX) -#define NANOCBOR_SIZE_SIZET NANOCBOR_SIZE_LONG -#else -#error ERROR: unable to determine maximum size of size_t -#endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* NANOCBOR_CONFIG_H */ -/** @} */ diff --git a/include/nanocbor/nanocbor.h b/include/nanocbor/nanocbor.h deleted file mode 100644 index 79d3ac1..0000000 --- a/include/nanocbor/nanocbor.h +++ /dev/null @@ -1,823 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -/** - * @defgroup NanoCBOR minimalistic CBOR library - * @brief Provides a minimal CBOR library - * - * NanoCBOR is a minimal CBOR encoder. For protocols such as CoAP, OSCORE, - * SenML and CORECONF a well defined and thus predictable CBOR structure is - * required. NanoCBOR tries to fill this requirement by providing a very - * minimal CBOR encoder. Supported is: - * - All major types - * - Arrays including indefinite length arrays - * - Maps including indefinite length maps - * - Safe for decoding untrusted input - * - * Not included: - * - Date and time - * - Big numbers (numbers encoded as byte strings) - * - * @{ - * - * @file - * @see [rfc 7049](https://tools.ietf.org/html/rfc7049) - * - * @author Koen Zandberg - */ - -#ifndef NANOCBOR_H -#define NANOCBOR_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define NANOCBOR_TYPE_OFFSET (5U) /**< Bit shift for CBOR major types */ -#define NANOCBOR_TYPE_MASK 0xE0U /**< Mask for CBOR major types */ -#define NANOCBOR_VALUE_MASK 0x1FU /**< Mask for CBOR values */ - -/** - * @name CBOR type numbers - * @{ - */ -#define NANOCBOR_TYPE_UINT (0x00U) /**< positive integer type */ -#define NANOCBOR_TYPE_NINT (0x01U) /**< negative integer type */ -#define NANOCBOR_TYPE_BSTR (0x02U) /**< byte string type */ -#define NANOCBOR_TYPE_TSTR (0x03U) /**< text string type */ -#define NANOCBOR_TYPE_ARR (0x04U) /**< array type */ -#define NANOCBOR_TYPE_MAP (0x05U) /**< map type */ -#define NANOCBOR_TYPE_TAG (0x06U) /**< tag type */ -#define NANOCBOR_TYPE_FLOAT (0x07U) /**< float type */ -/** @} */ - -/** - * @name CBOR major types including the bit shift - * @{ - */ -#define NANOCBOR_MASK_UINT (NANOCBOR_TYPE_UINT << NANOCBOR_TYPE_OFFSET) -#define NANOCBOR_MASK_NINT (NANOCBOR_TYPE_NINT << NANOCBOR_TYPE_OFFSET) -#define NANOCBOR_MASK_BSTR (NANOCBOR_TYPE_BSTR << NANOCBOR_TYPE_OFFSET) -#define NANOCBOR_MASK_TSTR (NANOCBOR_TYPE_TSTR << NANOCBOR_TYPE_OFFSET) -#define NANOCBOR_MASK_ARR (NANOCBOR_TYPE_ARR << NANOCBOR_TYPE_OFFSET) -#define NANOCBOR_MASK_MAP (NANOCBOR_TYPE_MAP << NANOCBOR_TYPE_OFFSET) -#define NANOCBOR_MASK_TAG (NANOCBOR_TYPE_TAG << NANOCBOR_TYPE_OFFSET) -#define NANOCBOR_MASK_FLOAT (NANOCBOR_TYPE_FLOAT << NANOCBOR_TYPE_OFFSET) -/** @} */ - -/** - * @name CBOR simple data types - * @{ - */ -#define NANOCBOR_SIMPLE_FALSE 20U /**< False */ -#define NANOCBOR_SIMPLE_TRUE 21U /**< True */ -#define NANOCBOR_SIMPLE_NULL 22U /**< NULL */ -#define NANOCBOR_SIMPLE_UNDEF 23U /**< Undefined */ -/** @} */ - -/** - * @name CBOR data sizes - * @{ - */ -#define NANOCBOR_SIZE_BYTE 24U /**< Value contained in a byte */ -#define NANOCBOR_SIZE_SHORT 25U /**< Value contained in a short */ -#define NANOCBOR_SIZE_WORD 26U /**< Value contained in a word */ -#define NANOCBOR_SIZE_LONG 27U /**< Value contained in a long */ -#define NANOCBOR_SIZE_INDEFINITE 31U /**< Indefinite sized container */ -/** @} */ - -/** - * @name CBOR Tag values - * @{ - */ -#define NANOCBOR_TAG_DATE_TIME (0x0) /**< Standard date/time string */ -#define NANOCBOR_TAG_EPOCH (0x1) /**< Epoch-based date/time */ -#define NANOCBOR_TAG_BIGNUMS_P (0x2) /**< Positive bignum */ -#define NANOCBOR_TAG_BIGNUMS_N (0x3) /**< Negative bignum */ -#define NANOCBOR_TAG_DEC_FRAC (0x4) /**< Decimal Fraction */ -#define NANOCBOR_TAG_BIGFLOATS (0x5) /**< Bigfloat */ -#define NANOCBOR_TAG_OBJECT (27) /**< Generic Object */ -/** @} */ - -/** - * @brief NanoCBOR decoder errors - */ -typedef enum { - /** - * @brief No error - */ - NANOCBOR_OK = 0, - - /** - * @brief Overflow in the getter. This can happen due to retrieving a - * number size larger than the function provides - */ - NANOCBOR_ERR_OVERFLOW = -1, - - /** - * Decoder get function attempts to retrieve the wrong type - */ - NANOCBOR_ERR_INVALID_TYPE = -2, - - /** - * @brief decoder is beyond the end of the buffer - */ - NANOCBOR_ERR_END = -3, - - /** - * @brief Decoder hits the recursion limit - */ - NANOCBOR_ERR_RECURSION = -4, - - /** - * @brief Decoder could not find the requested entry - */ - NANOCBOR_NOT_FOUND = -5, -} nanocbor_error_t; - - -/** - * @brief decoder context - */ -typedef struct nanocbor_value { - const uint_least8_t *cur; /**< Current position in the buffer */ - const uint_least8_t *end; /**< End of the buffer */ - uint32_t remaining; /**< Number of items remaining in the container */ - uint_least8_t flags; /**< Flags for decoding hints */ -} nanocbor_value_t; - -/** - * @name stream interface definition - * @{ - */ - -/** - * @brief Length in bytes of supplied cbor data. - * Incremented separate from the buffer check. - * - * @param[in] stream the private data of the stream implementation - * - * @return length in bytes of supplied cbor data. - */ -typedef size_t (*FnStreamLength)(void *stream); - -/** - * @brief Reserve bytes within the stream. - * - * @param[in] stream the private data of the stream implementation - * @param[in] len the number of bytes to reserve - * - * @return len input is provided back on success, - * and NANOCBOR_ERR_END if not able to reserve - */ -typedef int (*FnStreamReserve)(void *stream, size_t len); - -/** - * @brief Copy the given cbor data into the stream. - * - * @param[in] stream the private data of the stream implementation - * @param[in] src pointer to the data to copy - * @param[in] n the number of bytes to copy - */ -typedef void (*FnStreamInsert)(void *stream, const void *src, size_t n); - -/** @} */ - -/** - * @brief encoder context - */ -typedef struct nanocbor_encoder { - FnStreamLength len; /**< Length in bytes of supplied cbor data. */ - FnStreamReserve reserve; /**< Allocate/ensure the next 'len' bytes can fit */ - FnStreamInsert insert; /**< Insert cbor data into the stream */ - void *stream; /**< The private data to give back to stream functions */ - -} nanocbor_encoder_t; - -/** - * @name decoder flags - * @{ - */ - -/** - * @brief decoder value is inside a container - */ -#define NANOCBOR_DECODER_FLAG_CONTAINER (0x01U) - -/** - * @brief decoder value is inside an indefinite length container - */ -#define NANOCBOR_DECODER_FLAG_INDEFINITE (0x02U) -/** @} */ - -/** - * @name NanoCBOR parser functions - * @{ - */ - -/** - * @brief Initialize a decoder context decoding the CBOR structure from @p buf - * with @p len bytes - * - * The decoder will attempt to decode CBOR types until the buffer is exhausted - * - * @param[in] value decoder value context - * @param[in] buf Buffer to decode from - * @param[in] len Length in bytes of the buffer - */ -void nanocbor_decoder_init(nanocbor_value_t *value, - const uint_least8_t *buf, size_t len); - -/** - * @brief Retrieve the type of the CBOR value at the current position - * - * @param[in] value decoder value context - * - * @return major type - * @return NANOCBOR_ERR_END if the buffer is exhausted - */ -int nanocbor_get_type(const nanocbor_value_t *value); - -/** - * @brief Check if the current buffer or container is exhausted - * - * @param[in] it decoder value context - * - * @return true if it is exhausted - * @return false if there are more items - */ -bool nanocbor_at_end(const nanocbor_value_t *it); - -/** - * @brief Retrieve a positive integer as uint_least8_t from the stream - * - * If the value at `cvalue` is greater than 8 bit (> 255), error is returned. - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] value returned positive integer - * - * @return number of bytes read - * @return negative on error - */ -int nanocbor_get_uint8(nanocbor_value_t *cvalue, uint_least8_t *value); - -/** - * @brief Retrieve a positive integer as uint16_t from the stream - * - * If the value at `cvalue` is greater than 16 bit (> 65535), error is returned. - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] value returned positive integer - * - * @return number of bytes read - * @return negative on error - */ -int nanocbor_get_uint16(nanocbor_value_t *cvalue, uint16_t *value); - -/** - * @brief Retrieve a positive integer as uint32_t from the stream - * - * If the value at `cvalue` is greater than 32 bit, error is returned. - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] value returned positive integer - * - * @return number of bytes read - * @return negative on error - */ -int nanocbor_get_uint32(nanocbor_value_t *cvalue, uint32_t *value); - -/** - * @brief Retrieve a signed integer as int_least8_t from the stream - * - * If the value at `cvalue` is greater than 8 bit (< -128 or > 127), - * error is returned. - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] value returned signed integer - * - * @return number of bytes read - * @return negative on error - */ -int nanocbor_get_int8(nanocbor_value_t *cvalue, int_least8_t *value); - -/** - * @brief Retrieve a signed integer as int16_t from the stream - * - * If the value at `cvalue` is greater than 16 bit (< -32768 or > 32767), - * error is returned. - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] value returned signed integer - * - * @return number of bytes read - * @return negative on error - */ -int nanocbor_get_int16(nanocbor_value_t *cvalue, int16_t *value); - -/** - * @brief Retrieve a signed integer as int32_t from the stream - * - * If the value at `cvalue` is greater than 32 bit, error is returned. - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] value returned signed integer - * - * @return number of bytes read - * @return negative on error - */ -int nanocbor_get_int32(nanocbor_value_t *cvalue, int32_t *value); - -/** - * @brief Retrieve a decimal fraction from the stream as a int32_t mantisa and - * int32_t exponent - * - * If the value at `cvalue` is greater than 32 bit, error is returned. - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] m returned mantisa - * @param[out] e returned exponent - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_get_decimal_frac(nanocbor_value_t *cvalue, int32_t *e, int32_t *m); - -/** - * @brief Retrieve a byte string from the stream - * - * The resulting @p buf and @p len are undefined if the result is an error - * condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] buf pointer to the byte string - * @param[out] len length of the byte string - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_get_bstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len); - -/** - * @brief Retrieve a text string from the stream - * - * The resulting @p buf and @p len are undefined if the result is an error - * condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] buf pointer to the text string - * @param[out] len length of the text string - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_get_tstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len); - -/** - * @brief Search for a tstr key in a map. - * - * The resulting @p value is undefined if @p key was not found. - * - * @pre @p start is inside a map - * - * @param[in] start pointer to the map to search - * @param[in] key pointer to the text string key - * @param[out] value pointer to the tstr value containing @p key if found - * - * @return NANOCBOR_OK if @p key was found - * @return negative on error / not found - */ -int nanocbor_get_key_tstr(nanocbor_value_t *start, const char *key, - nanocbor_value_t *value); - -/** - * @brief Enter a array type - * - * @param[in] it CBOR value to decode from - * @param[out] array CBOR value to decode the array members with - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_enter_array(const nanocbor_value_t *it, nanocbor_value_t *array); - -/** - * @brief Enter a map type - * - * @param[in] it CBOR value to decode from - * @param[out] map CBOR value to decode the map members with - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_enter_map(const nanocbor_value_t *it, nanocbor_value_t *map); - -/** - * @brief leave the container - * - * This must be called with the same @ref nanocbor_value_t struct that was used - * to enter the container. Furthermore, the @p container must be at the end of - * the container. - * - * @param[in] it parent CBOR structure - * @param[in] container exhausted CBOR container - */ -void nanocbor_leave_container(nanocbor_value_t *it, nanocbor_value_t *container); - -/** - * @brief Retrieve a tag as positive uint32_t from the stream - * - * The resulting @p value is undefined if the result is an error condition - * - * @param[in] cvalue CBOR value to decode from - * @param[out] tag returned tag as positive integer - * - * @return NANOCBOR_OK on success - */ -int nanocbor_get_tag(nanocbor_value_t *cvalue, uint32_t *tag); - -/** - * @brief Retrieve a null value from the stream - * - * This function checks if the next CBOR value is a NULL value and advances to - * the next value if no error is detected - * - * @param[in] cvalue CBOR value to decode from - * - * @return NANOCBOR_OK on success - */ -int nanocbor_get_null(nanocbor_value_t *cvalue); - -/** - * @brief Retrieve a boolean value from the stream - * - * @param[in] cvalue CBOR value to decode from - * @param[out] value Boolean value retrieved from the stream - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_get_bool(nanocbor_value_t *cvalue, bool *value); - -/** - * @brief Skip to the next value in the CBOR stream - * - * This function is able to skip over nested structures in the CBOR stream - * such as (nested) arrays and maps. It uses limited recursion to do so. - * - * Recursion is limited with @ref NANOCBOR_RECURSION_MAX - * - * @param[in] it CBOR stream to skip a value from - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_skip(nanocbor_value_t *it); - -/** - * @brief Skip a single simple value in the CBOR stream - * - * This is a cheaper version of @ref nanocbor_skip, the downside is that this - * function is unable to skip nested structures. - * - * @param[in] it CBOR value to skip - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_skip_simple(nanocbor_value_t *it); - -/** - * @brief Retrieve part of the CBOR stream for separate parsing - * - * This function retrieves the pointer and length of a single CBOR item. This - * item can be stored for later processing. - * - * @param[in] it CBOR value to retrieve - * @param[out] start start of the CBOR item - * @param[out] len length of the CBOR item - * - * @return NANOCBOR_OK on success - * @return negative on error - */ -int nanocbor_get_subcbor(nanocbor_value_t *it, const uint_least8_t **start, - size_t *len); - -/** - * @brief Retrieve the number of remaining values is a CBOR container - * - * The returned value is undefined when not inside a container or when the - * container is of indefinite length. For a map, the number is the full number - * of CBOR items remaining (twice the number of key/value pairs). - * - * @param[in] value value inside a CBOR container - * - * @return number of items remaining - */ -static inline uint32_t nanocbor_container_remaining(const nanocbor_value_t *value) -{ - return value->remaining; -} - -/** - * @brief Check whether a container is an indefinite-length container - * - * @param[in] container value inside a CBOR container - * - * @return True when the container is indefinite in length - * @return False when not indefinite-length or not in a - * container - */ -static inline bool nanocbor_container_indefinite(const nanocbor_value_t *container) -{ - return (container->flags == - (NANOCBOR_DECODER_FLAG_INDEFINITE | NANOCBOR_DECODER_FLAG_CONTAINER)); -} - -static inline bool nanocbor_in_container(const nanocbor_value_t *container) -{ - return container->flags & (NANOCBOR_DECODER_FLAG_CONTAINER); -} - -/** @} */ - -/** - * @name NanoCBOR encoder functions - * @{ - */ - -/** - * @brief Declare a nanocbor_encoder_t - */ -#define NANOCBOR_ENCODER(priv_data, lenfn, resfn, insfn) \ - (const nanocbor_encoder_t) { \ - .len = lenfn, \ - .reserve = resfn, \ - .insert = insfn, \ - .stream = priv_data, \ - } - -/** - * @brief Retrieve the encoded length of the CBOR structure - * - * This function doesn't take the length of the stream into account, - * it only returns the number of bytes the current CBOR structure would take up. - * - * @param[in] enc Encoder context - * - * @return Length of the encoded structure - */ -size_t nanocbor_encoded_len(nanocbor_encoder_t *enc); - -/** - * @brief Write a CBOR boolean value into a buffer - * - * @param[in] enc Encoder context - * @param[in] content Boolean value to write - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_bool(nanocbor_encoder_t *enc, bool content); - -/** - * @brief Write an unsigned integer of at most sizeof uint64_t into the buffer - * - * @param[in] enc Encoder context - * @param[in] num unsigned integer to write - * - * @return number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_uint(nanocbor_encoder_t *enc, uint64_t num); - -/** - * @brief Write a CBOR tag of at most sizeof uint64_t into the buffer - * - * @param[in] enc Encoder context - * @param[in] num tag value to write into the buffer - * - * @return number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_tag(nanocbor_encoder_t *enc, uint64_t num); - -/** - * @brief Write a CBOR Object tag (27) to the buffer - * - * @param[in] enc Encoder context - * @param[in] num_params Number of parameters needed to construct this object. - * - * @return number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_object(nanocbor_encoder_t *enc, size_t num_params); - -/** - * @brief Write a signed integer of at most sizeof int32_t into the buffer - * - * If it is not certain if the data is signed, use this function. - * - * @param[in] enc Encoder context - * @param[in] num unsigned integer to write - * - * @return number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_int(nanocbor_encoder_t *enc, int64_t num); - -/** - * @brief Write a byte string indicator for a byte string with specific length - * into the encoder buffer - * - * This doesn't write any byte string into the encoder buffer, only the type - * and length indicator for the byte string - * - * @param[in] enc Encoder context - * @param[in] len Length of the byte string - * - * @return number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_bstr(nanocbor_encoder_t *enc, size_t len); - -/** - * @brief Write a text string indicator for a string with specific length - * into the encoder buffer - * - * This doesn't write any text string into the encoder buffer, only the type - * and length indicator for the text string - * - * @param[in] enc Encoder context - * @param[in] len Length of the text string - * - * @return number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_tstr(nanocbor_encoder_t *enc, size_t len); - -/** - * @brief Copy a byte string with indicator into the encoder buffer - * - * @param[in] enc Encoder context - * @param[in] str byte string to encode - * @param[in] len Length of the string - * - * @return NANOCBOR_OK if the string fits - * @return Negative on error - */ -int nanocbor_put_bstr(nanocbor_encoder_t *enc, const uint_least8_t *str, size_t len); - -/** - * @brief Copy a text string with indicator into the encoder buffer - * - * @param[in] enc Encoder context - * @param[in] str null terminated text string to encode - * - * @return NANOCBOR_OK if the string fits - * @return Negative on error - */ -int nanocbor_put_tstr(nanocbor_encoder_t *enc, const char *str); - -/** - * @brief Copy n bytes of a text string with indicator into the encoder buffer - * - * @param[in] enc Encoder context - * @param[in] str text string to encode - * @param[in] len number of string bytes to copy - * - * @return NANOCBOR_OK if the string fits - * @return Negative on error - */ -int nanocbor_put_tstrn(nanocbor_encoder_t *enc, const char *str, size_t len); - -/** - * @brief Write an array indicator with @p len items - * - * It is assumed that the calling code will encode @p len items after calling - * this function. The array automatically terminates after @p len items are - * added, no function to close the container is necessary. - * - * @param[in] enc Encoder context - * @param[in] len Number of items in the array - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_array(nanocbor_encoder_t *enc, size_t len); - -/** - * @brief Write a map indicator with @p len pairs - * - * It is assumed that the calling code will encode @p len item pairs after - * calling this function. The array automatically terminates after @p len item - * pairs are added, no function to close the container is necessary. - * - * @param[in] enc Encoder context - * @param[in] len Number of pairs in the map - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_map(nanocbor_encoder_t *enc, size_t len); - -/** - * @brief Write an indefinite-length array indicator - * - * @param[in] enc Encoder context - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_array_indefinite(nanocbor_encoder_t *enc); - -/** - * @brief Write an indefinite-length map indicator - * - * @param[in] enc Encoder context - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_map_indefinite(nanocbor_encoder_t *enc); - -/** - * @brief Write a stop code for indefinite length containers - * - * @param[in] enc Encoder context - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_end_indefinite(nanocbor_encoder_t *enc); - -/** - * @brief Write a Null value into the encoder buffer - * - * @param[in] enc Encoder context - * - * @return NANOCBOR_OK - * @return Negative on error - */ -int nanocbor_fmt_null(nanocbor_encoder_t *enc); - -/** - * @brief Write a float value into the encoder buffer - * - * @param[in] enc Encoder context - * @param[in] num Floating point to encode - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_float(nanocbor_encoder_t *enc, float num); - -/** - * @brief Write a double floating point value into the encoder buffer - * - * @param[in] enc Encoder context - * @param[in] num Floating point to encode - * - * @return Number of bytes written - * @return Negative on error - */ -int nanocbor_fmt_double(nanocbor_encoder_t *enc, double num); - -/** - * @brief Write a decimal fraction into the encoder buffer - * - * @param[in] enc Encoder context - * @param[in] m Mantisa - * @param[in] e Exponent - * - * @return Number of bytes written - */ -int nanocbor_fmt_decimal_frac(nanocbor_encoder_t *enc, int32_t e, int32_t m); - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* NANOCBOR_H */ -/** @} */ diff --git a/include/nanocbor/stream_encoders/memory_buffer.h b/include/nanocbor/stream_encoders/memory_buffer.h deleted file mode 100644 index c0535ae..0000000 --- a/include/nanocbor/stream_encoders/memory_buffer.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -#ifndef ENCODER_MEMORY_BUFFER_H -#define ENCODER_MEMORY_BUFFER_H - -#include -#include - -typedef struct memory_encoder { - uint_least8_t *cur; /**< Current position in the buffer */ - uint_least8_t *end; /**< end of the buffer */ - size_t len; /**< Length in bytes of supplied cbor data. Incremented - * separate from the buffer check */ -} memory_encoder; - -void MemoryStream_Init(memory_encoder *self, uint_least8_t *buf, size_t len); -size_t MemoryStream_Length(memory_encoder *self); -int MemoryStream_Reserve(memory_encoder *self, size_t len); -void MemoryStream_Insert(memory_encoder *self, const void *src, size_t n); - -#endif diff --git a/src/decoder.c b/src/decoder.c deleted file mode 100644 index e578d55..0000000 --- a/src/decoder.c +++ /dev/null @@ -1,455 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -/** - * @ingroup nanocbor - * @{ - * @file - * @brief Minimalistic CBOR decoder implementation - * - * @author Koen Zandberg - * @} - */ - -#include -#include -#include -#include - -#include "nanocbor/config.h" -#include "nanocbor/nanocbor.h" - -#include NANOCBOR_BYTEORDER_HEADER - - -void nanocbor_decoder_init(nanocbor_value_t *value, - const uint_least8_t *buf, size_t len) -{ - value->cur = buf; - value->end = buf + len; - value->flags = 0; -} - -static void _advance(nanocbor_value_t *cvalue, unsigned int res) -{ - cvalue->cur += res; - cvalue->remaining--; -} - -static int _advance_if(nanocbor_value_t *cvalue, int res) -{ - if (res > 0) { - _advance(cvalue, (unsigned int)res); - } - return res; -} - -static inline bool _over_end(const nanocbor_value_t *it) -{ - return it->cur >= it->end; -} - -static inline uint_least8_t _get_type(const nanocbor_value_t *value) -{ - return (*value->cur & NANOCBOR_TYPE_MASK); -} - -static int _value_match_exact(nanocbor_value_t *cvalue, uint_least8_t val) -{ - int res = NANOCBOR_ERR_INVALID_TYPE; - - if (_over_end(cvalue)) { - res = NANOCBOR_ERR_END; - } - else if (*cvalue->cur == val) { - _advance(cvalue, 1U); - res = NANOCBOR_OK; - } - return res; -} - -bool nanocbor_at_end(const nanocbor_value_t *it) -{ - bool end = false; - /* The container is at the end when */ - if (_over_end(it) || /* Number of items exhausted */ - /* Indefinite container and the current item is the end marker */ - ((nanocbor_container_indefinite(it) && - *it->cur == (NANOCBOR_TYPE_FLOAT << NANOCBOR_TYPE_OFFSET | NANOCBOR_VALUE_MASK))) || - /* Or the remaining number of items is zero */ - (!nanocbor_container_indefinite(it) && nanocbor_in_container(it) && it->remaining == 0) - ) { - end = true; - } - return end; -} - -int nanocbor_get_type(const nanocbor_value_t *value) -{ - if (nanocbor_at_end(value)) { - return NANOCBOR_ERR_END; - } - return (_get_type(value) >> NANOCBOR_TYPE_OFFSET); -} - -static int _get_uint64(const nanocbor_value_t *cvalue, uint32_t *value, uint_least8_t max, int type) -{ - int ctype = nanocbor_get_type(cvalue); - - if (ctype < 0) { - return ctype; - } - - if (type != ctype) { - return NANOCBOR_ERR_INVALID_TYPE; - } - unsigned bytelen = *cvalue->cur & NANOCBOR_VALUE_MASK; - - if (bytelen < NANOCBOR_SIZE_BYTE) { - *value = bytelen; - /* Ptr should advance 1 pos */ - return 1; - } - if (bytelen > max) { - return NANOCBOR_ERR_OVERFLOW; - } - - unsigned bytes = 1U << (bytelen - NANOCBOR_SIZE_BYTE); - - if ((cvalue->cur + bytes) >= cvalue->end) { - return NANOCBOR_ERR_END; - } - uint64_t tmp = 0; - uint_fast64_t val64; - /* Copy the value from cbor to the least significant bytes */ - /* for-loop to handle excotic cpu arthitectures, eg. no 8-bit support*/ - for (uint_fast8_t i = 0; i < bytes; i++) { - val64 = 0x00000000000000ffull & *((uint_fast8_t*)(cvalue->cur + 1 + i)); - tmp |= ( val64<<(8 * (bytes - i - 1)) ); - } - *value = tmp; - - return (int)(1 + bytes); -} - -static int _get_and_advance_uint8(nanocbor_value_t *cvalue, uint_least8_t *value, - int type) -{ - uint32_t tmp = 0; - int res = _get_uint64(cvalue, &tmp, NANOCBOR_SIZE_BYTE, - type); - *value = (uint_least8_t)tmp; - - return _advance_if(cvalue, res); -} - -static int _get_and_advance_uint16(nanocbor_value_t *cvalue, uint16_t *value, - int type) -{ - uint32_t tmp = 0; - int res = _get_uint64(cvalue, &tmp, NANOCBOR_SIZE_SHORT, - type); - *value = (uint16_t)tmp; - - return _advance_if(cvalue, res); -} - -static int _get_and_advance_uint32(nanocbor_value_t *cvalue, uint32_t *value, - int type) -{ - uint32_t tmp = 0; - int res = _get_uint64(cvalue, &tmp, NANOCBOR_SIZE_WORD, - type); - *value = tmp; - - return _advance_if(cvalue, res); -} - -int nanocbor_get_uint8(nanocbor_value_t *cvalue, uint_least8_t *value) -{ - return _get_and_advance_uint8(cvalue, value, NANOCBOR_TYPE_UINT); -} - -int nanocbor_get_uint16(nanocbor_value_t *cvalue, uint16_t *value) -{ - return _get_and_advance_uint16(cvalue, value, NANOCBOR_TYPE_UINT); -} - -int nanocbor_get_uint32(nanocbor_value_t *cvalue, uint32_t *value) -{ - return _get_and_advance_uint32(cvalue, value, NANOCBOR_TYPE_UINT); -} - -static int _get_and_advance_int32(nanocbor_value_t *cvalue, int32_t *value, uint_least8_t max, - uint32_t bound) -{ - int type = nanocbor_get_type(cvalue); - if (type < 0) { - return type; - } - int res = NANOCBOR_ERR_INVALID_TYPE; - if (type == NANOCBOR_TYPE_NINT || type == NANOCBOR_TYPE_UINT) { - uint32_t intermediate = 0; - res = _get_uint64(cvalue, &intermediate, max, type); - if (intermediate > bound) { - res = NANOCBOR_ERR_OVERFLOW; - } - if (type == NANOCBOR_TYPE_NINT) { - *value = (-(int32_t)intermediate) - 1; - } - else { - *value = (int32_t)intermediate; - } - } - return _advance_if(cvalue, res); -} - -int nanocbor_get_int8(nanocbor_value_t *cvalue, int_least8_t *value) -{ -#define C2000_INT8_MAX 127 // C2000 does not define INT8_MAX, because it doesnt implement int8_t - int32_t tmp = 0; - int res = _get_and_advance_int32(cvalue, &tmp, NANOCBOR_SIZE_BYTE, C2000_INT8_MAX); - - *value = (int_least8_t)tmp; - - return res; -} - -int nanocbor_get_int16(nanocbor_value_t *cvalue, int16_t *value) -{ - int32_t tmp = 0; - int res = _get_and_advance_int32(cvalue, &tmp, NANOCBOR_SIZE_SHORT, INT16_MAX); - - *value = (int16_t)tmp; - - return res; -} - -int nanocbor_get_int32(nanocbor_value_t *cvalue, int32_t *value) -{ - return _get_and_advance_int32(cvalue, value, NANOCBOR_SIZE_WORD, INT32_MAX); -} - -int nanocbor_get_tag(nanocbor_value_t *cvalue, uint32_t *tag) -{ - int res = _get_uint64(cvalue, tag, NANOCBOR_SIZE_WORD, NANOCBOR_TYPE_TAG); - - if (res >= 0) { - cvalue->cur += res; - res = NANOCBOR_OK; - } - - return res; -} - -int nanocbor_get_decimal_frac(nanocbor_value_t *cvalue, int32_t *e, int32_t *m) -{ - int res = NANOCBOR_NOT_FOUND; - uint32_t tag = UINT32_MAX; - if(nanocbor_get_tag(cvalue, &tag) == NANOCBOR_OK) { - if (tag == NANOCBOR_TAG_DEC_FRAC) { - nanocbor_value_t arr; - if (nanocbor_enter_array(cvalue, &arr) == NANOCBOR_OK) { - res = nanocbor_get_int32(&arr, e); - if (res >= 0) { - res = nanocbor_get_int32(&arr, m); - if (res >= 0) { - res = NANOCBOR_OK; - } - } - nanocbor_leave_container(cvalue, &arr); - } - } - } - - return res; -} - -static int _get_str(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len, uint_least8_t type) -{ - *len = 0; - int res = _get_uint64(cvalue, (uint32_t*)len, NANOCBOR_SIZE_SIZET, type); - - if (cvalue->end - cvalue->cur < 0 || (size_t)(cvalue->end - cvalue->cur) < *len) { - return NANOCBOR_ERR_END; - } - if (res >= 0) { - *buf = (cvalue->cur) + res; - _advance(cvalue, (unsigned int)((size_t)res + *len)); - res = NANOCBOR_OK; - } - return res; -} - -int nanocbor_get_bstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len) -{ - return _get_str(cvalue, buf, len, NANOCBOR_TYPE_BSTR); -} - -int nanocbor_get_tstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len) -{ - return _get_str(cvalue, buf, len, NANOCBOR_TYPE_TSTR); -} - -int nanocbor_get_null(nanocbor_value_t *cvalue) -{ - return _value_match_exact(cvalue, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_NULL); -} - -int nanocbor_get_bool(nanocbor_value_t *cvalue, bool *value) -{ - *value = false; - int res = _value_match_exact(cvalue, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_FALSE); - if (res < 0) { - *value = true; - res = _value_match_exact(cvalue, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_TRUE); - } - return res; -} - -static int _enter_container(const nanocbor_value_t *it, nanocbor_value_t *container, - uint_least8_t type) -{ - container->end = it->end; - container->remaining = 0; - - uint_least8_t value_match = (uint_least8_t)(((unsigned)type << NANOCBOR_TYPE_OFFSET) | NANOCBOR_SIZE_INDEFINITE); - - /* Not using _value_match_exact here to keep *it const */ - if (!_over_end(it) && *it->cur == value_match) { - container->flags = NANOCBOR_DECODER_FLAG_INDEFINITE | - NANOCBOR_DECODER_FLAG_CONTAINER; - container->cur = it->cur + 1; - return NANOCBOR_OK; - } - - int res = _get_uint64(it, &container->remaining, - NANOCBOR_SIZE_WORD, type); - if (res < 0) { - return res; - } - container->flags = NANOCBOR_DECODER_FLAG_CONTAINER; - container->cur = it->cur + res; - return NANOCBOR_OK; -} - -int nanocbor_enter_array(const nanocbor_value_t *it, nanocbor_value_t *array) -{ - return _enter_container(it, array, NANOCBOR_TYPE_ARR); -} - -int nanocbor_enter_map(const nanocbor_value_t *it, nanocbor_value_t *map) -{ - int res = _enter_container(it, map, NANOCBOR_TYPE_MAP); - - if (map->remaining > UINT32_MAX / 2) { - return NANOCBOR_ERR_OVERFLOW; - } - map->remaining = map->remaining * 2; - return res; -} - -void nanocbor_leave_container(nanocbor_value_t *it, nanocbor_value_t *container) -{ - if (it->remaining) { - it->remaining--; - } - if (nanocbor_container_indefinite(container)) { - it->cur = container->cur + 1; - } - else { - it->cur = container->cur; - } -} - -static int _skip_simple(nanocbor_value_t *it) -{ - uint64_t tmp = 0; - int res = _get_uint64(it, (uint32_t*)&tmp, NANOCBOR_SIZE_LONG, - nanocbor_get_type(it)); - return _advance_if(it, res); -} - -int nanocbor_get_subcbor(nanocbor_value_t *it, const uint_least8_t **start, - size_t *len) -{ - *start = it->cur; - int res = nanocbor_skip(it); - *len = (size_t)(it->cur - *start); - return res; -} - -int nanocbor_skip_simple(nanocbor_value_t *it) -{ - return _skip_simple(it); -} - -/* NOLINTNEXTLINE(misc-no-recursion): Recursion is limited by design */ -static int _skip_limited(nanocbor_value_t *it, uint_least8_t limit) -{ - if (limit == 0) { - return NANOCBOR_ERR_RECURSION; - } - int type = nanocbor_get_type(it); - int res = type; - - if (type == NANOCBOR_TYPE_BSTR || type == NANOCBOR_TYPE_TSTR) { - const uint_least8_t *tmp = NULL; - size_t len = 0; - res = _get_str(it, &tmp, &len, (uint_least8_t)type); - } - /* map or array */ - else if (type == NANOCBOR_TYPE_ARR || type == NANOCBOR_TYPE_MAP) { - nanocbor_value_t recurse; - res = (type == NANOCBOR_TYPE_MAP - ? nanocbor_enter_map(it, &recurse) - : nanocbor_enter_array(it, &recurse)); - if (res == NANOCBOR_OK) { - while (!nanocbor_at_end(&recurse)) { - res = _skip_limited(&recurse, limit - 1); - if (res < 0) { - break; - } - } - nanocbor_leave_container(it, &recurse); - } - } - else if (type >= 0) { - res = _skip_simple(it); - } - return res < 0 ? res : NANOCBOR_OK; -} - -int nanocbor_skip(nanocbor_value_t *it) -{ - return _skip_limited(it, NANOCBOR_RECURSION_MAX); -} - -int nanocbor_get_key_tstr(nanocbor_value_t *start, const char *key, - nanocbor_value_t *value) -{ - int res = NANOCBOR_NOT_FOUND; - size_t len = strlen(key); - *value = *start; - - while (!nanocbor_at_end(value)) { - const uint_least8_t *s = NULL; - size_t s_len = 0; - - if ((res = nanocbor_get_tstr(value, &s, &s_len)) < 0) { - break; - } - - if (s_len == len && !strncmp(key, (const char *)s, len)) { - res = NANOCBOR_OK; - break; - } - - if ((res = nanocbor_skip(value)) < 0) { - break; - } - } - - return res; -} diff --git a/src/encoder.c b/src/encoder.c deleted file mode 100644 index d63fcd5..0000000 --- a/src/encoder.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -/** - * @ingroup nanocbor - * @{ - * @file - * @brief Minimalistic CBOR encoder implementation - * - * @author Koen Zandberg - * @} - */ - -#include -#include -#include -#include - -#include "nanocbor/config.h" -#include "nanocbor/nanocbor.h" - -#include NANOCBOR_BYTEORDER_HEADER - -size_t nanocbor_encoded_len(nanocbor_encoder_t *enc) -{ - return enc->len(enc->stream); -} - -static int _fmt_single(nanocbor_encoder_t *enc, uint_least8_t single) -{ - const int res = enc->reserve(enc->stream, 1); - - if (res == 1) { - enc->insert(enc->stream, &single, 1); - } - return res; -} - -int nanocbor_fmt_bool(nanocbor_encoder_t *enc, bool content) -{ - uint_least8_t single = NANOCBOR_MASK_FLOAT | (content ? NANOCBOR_SIMPLE_TRUE - : NANOCBOR_SIMPLE_FALSE); - - return _fmt_single(enc, single); -} - -static int _fmt_uint64(nanocbor_encoder_t *enc, uint64_t num, uint_least8_t type) -{ - unsigned extrabytes = 0; - uint_least8_t buf[8]; - - if (num < NANOCBOR_SIZE_BYTE) { - type |= num; - } else { - if (num > UINT32_MAX) { - /* Requires long size */ - type |= NANOCBOR_SIZE_LONG; - /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ - extrabytes = 8; - buf[0] = (num & 0xff00000000000000ull) >> 56; - buf[1] = (num & 0x00ff000000000000ull) >> 48; - buf[2] = (num & 0x0000ff0000000000ull) >> 40; - buf[3] = (num & 0x000000ff00000000ull) >> 32; - buf[4] = (num & 0x00000000ff000000ull) >> 24; - buf[5] = (num & 0x0000000000ff0000ull) >> 16; - buf[6] = (num & 0x000000000000ff00ull) >> 8; - buf[7] = (num & 0x00000000000000ffull); - } else if (num > UINT16_MAX) { - /* At least word size */ - type |= NANOCBOR_SIZE_WORD; - /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ - extrabytes = 4; - buf[0] = (num & 0x00000000ff000000ull) >> 24; - buf[1] = (num & 0x0000000000ff0000ull) >> 16; - buf[2] = (num & 0x000000000000ff00ull) >> 8; - buf[3] = (num & 0x00000000000000ffull); - } else if (num > 255/* UINT8_MAX - C2000 does not define this*/) { - type |= NANOCBOR_SIZE_SHORT; - /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ - extrabytes = 2; - buf[0] = (num & 0x000000000000ff00ull) >> 8; - buf[1] = (num & 0x00000000000000ffull); - } else { - type |= NANOCBOR_SIZE_BYTE; - /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ - extrabytes = 1; - buf[0] = (num & 0x00000000000000ffull); - } - } - - int res = enc->reserve(enc->stream, extrabytes + 1); - - if (res > 0) { - enc->insert(enc->stream, &type, 1); - enc->insert(enc->stream, buf, extrabytes); - } - - return res; -} - -int nanocbor_fmt_uint(nanocbor_encoder_t *enc, uint64_t num) -{ - return _fmt_uint64(enc, num, NANOCBOR_MASK_UINT); -} - -int nanocbor_fmt_tag(nanocbor_encoder_t *enc, uint64_t num) -{ - return _fmt_uint64(enc, num, NANOCBOR_MASK_TAG); -} - -int nanocbor_fmt_object(nanocbor_encoder_t *enc, size_t num_params) -{ - int array_res = 0; - int tag_res = nanocbor_fmt_tag(enc, NANOCBOR_TAG_OBJECT); - - if (tag_res < 0) { - return tag_res; - } - - array_res = nanocbor_fmt_array(enc, num_params); - - if (array_res < 0) { - return array_res; - } - - return tag_res + array_res; -} - -int nanocbor_fmt_int(nanocbor_encoder_t *enc, int64_t num) -{ - if (num < 0) { - /* Always negative at this point */ - num = -(num + 1); - return _fmt_uint64(enc, (uint64_t)num, NANOCBOR_MASK_NINT); - } - return nanocbor_fmt_uint(enc, (uint64_t)num); -} - -int nanocbor_fmt_bstr(nanocbor_encoder_t *enc, size_t len) -{ - return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_BSTR); -} - -int nanocbor_fmt_tstr(nanocbor_encoder_t *enc, size_t len) -{ - return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_TSTR); -} - -static int _put_bytes(nanocbor_encoder_t *enc, const uint_least8_t *str, size_t len) -{ - const int res = enc->reserve(enc->stream, len); - - if (res >= 0) { - enc->insert(enc->stream, str, len); - return NANOCBOR_OK; - } - return res; -} - -int nanocbor_put_tstr(nanocbor_encoder_t *enc, const char *str) -{ - size_t len = strlen(str); - - nanocbor_fmt_tstr(enc, len); - return _put_bytes(enc, (const uint_least8_t *)str, len); -} - -int nanocbor_put_tstrn(nanocbor_encoder_t *enc, const char *str, size_t len) -{ - nanocbor_fmt_tstr(enc, len); - return _put_bytes(enc, (const uint_least8_t *)str, len); -} - -int nanocbor_put_bstr(nanocbor_encoder_t *enc, const uint_least8_t *str, size_t len) -{ - nanocbor_fmt_bstr(enc, len); - return _put_bytes(enc, str, len); -} - -int nanocbor_fmt_array(nanocbor_encoder_t *enc, size_t len) -{ - return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_ARR); -} - -int nanocbor_fmt_map(nanocbor_encoder_t *enc, size_t len) -{ - return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_MAP); -} - -int nanocbor_fmt_array_indefinite(nanocbor_encoder_t *enc) -{ - return _fmt_single(enc, NANOCBOR_MASK_ARR | NANOCBOR_SIZE_INDEFINITE); -} - -int nanocbor_fmt_map_indefinite(nanocbor_encoder_t *enc) -{ - return _fmt_single(enc, NANOCBOR_MASK_MAP | NANOCBOR_SIZE_INDEFINITE); -} - -int nanocbor_fmt_end_indefinite(nanocbor_encoder_t *enc) -{ - /* End is marked with float major and indefinite minor number */ - return _fmt_single(enc, NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_INDEFINITE); -} - -int nanocbor_fmt_null(nanocbor_encoder_t *enc) -{ - return _fmt_single(enc, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_NULL); -} - -/* Double bit mask related defines */ -#define DOUBLE_EXP_OFFSET (1023U) -#define DOUBLE_SIZE (64U) -#define DOUBLE_EXP_POS (52U) -#define DOUBLE_SIGN_POS (63U) -#define DOUBLE_EXP_MASK ((uint64_t)0x7FFU) -#define DOUBLE_SIGN_MASK ((uint64_t)1U << DOUBLE_SIGN_POS) -#define DOUBLE_EXP_IS_NAN (0x7FFU) -#define DOUBLE_IS_ZERO (~(DOUBLE_SIGN_MASK)) -#define DOUBLE_FLOAT_LOSS (0x1FFFFFFFU) - -/* float bit mask related defines */ -#define FLOAT_EXP_OFFSET (127U) -#define FLOAT_SIZE (32U) -#define FLOAT_EXP_POS (23U) -#define FLOAT_EXP_MASK ((uint32_t)0xFFU) -#define FLOAT_SIGN_POS (31U) -#define FLOAT_FRAC_MASK (0x7FFFFFU) -#define FLOAT_SIGN_MASK ((uint32_t)1U << FLOAT_SIGN_POS) -#define FLOAT_EXP_IS_NAN (0xFFU) -#define FLOAT_IS_ZERO (~(FLOAT_SIGN_MASK)) -/* Part where a float to halffloat leads to precision loss */ -#define FLOAT_HALF_LOSS (0x1FFFU) - -/* halffloat bit mask related defines */ -#define HALF_EXP_OFFSET (15U) -#define HALF_SIZE (16U) -#define HALF_EXP_POS (10U) -#define HALF_EXP_MASK (0x1FU) -#define HALF_SIGN_POS (15U) -#define HALF_FRAC_MASK (0x3FFU) -#define HALF_SIGN_MASK ((uint16_t)(1U << HALF_SIGN_POS)) -#define HALF_MASK_HALF (0xFFU) - -/* Check special cases for single precision floats */ -static bool _single_is_inf_nan(uint_least8_t exp) -{ - return exp == FLOAT_EXP_IS_NAN; -} - -static bool _single_is_zero(uint32_t num) -{ - return (num & FLOAT_IS_ZERO) == 0; -} - -static bool _single_in_range(uint_least8_t exp, uint32_t num) -{ - /* Check if lower 13 bits of fraction are zero, if so we might be able to - * convert without precision loss */ - if (exp <= (HALF_EXP_OFFSET + FLOAT_EXP_OFFSET) && - exp >= ((-HALF_EXP_OFFSET + 1) + FLOAT_EXP_OFFSET) && - ((num & FLOAT_HALF_LOSS) == 0)) { - return true; - } - return false; -} - -static int _fmt_halffloat(nanocbor_encoder_t *enc, uint16_t half) -{ - int res = enc->reserve(enc->stream, sizeof(uint16_t) + 1); - if (res > 0) { - const uint_least8_t id = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_SHORT; - const uint_least8_t part1 = (half >> HALF_SIZE/2); - const uint_least8_t part2 = half & HALF_MASK_HALF; - - enc->insert(enc->stream, &id, 1); - enc->insert(enc->stream, &part1, 1); - enc->insert(enc->stream, &part2, 1); - - res = sizeof(uint16_t) + 1; - } - return res; -} - -/* Check special cases for single precision floats */ -static bool _double_is_inf_nan(uint16_t exp) -{ - return (exp == DOUBLE_EXP_IS_NAN); -} - -static bool _double_is_zero(uint64_t num) -{ - return (num & DOUBLE_IS_ZERO) == 0; -} - -static bool _double_in_range(uint16_t exp, uint64_t num) -{ - /* Check if lower 13 bits of fraction are zero, if so we might be able to - * convert without precision loss */ - if (exp <= (DOUBLE_EXP_OFFSET + FLOAT_EXP_OFFSET) && - exp >= (DOUBLE_EXP_OFFSET - FLOAT_EXP_OFFSET + 1) && - ((num & DOUBLE_FLOAT_LOSS) == 0)) { /* First 29 bits must be zero */ - return true; - } - return false; -} - -int nanocbor_fmt_float(nanocbor_encoder_t *enc, float num) -{ - /* Allow bitwise access to float */ - uint32_t *unum = (uint32_t *)# - - /* Retrieve exponent */ - uint_least8_t exp = (*unum >> FLOAT_EXP_POS) & FLOAT_EXP_MASK; - if (_single_is_inf_nan(exp) || - _single_is_zero(*unum) || - _single_in_range(exp, *unum)) { - /* Copy sign bit */ - uint16_t half = ((*unum >> (FLOAT_SIZE - HALF_SIZE)) & HALF_SIGN_MASK); - /* Shift exponent */ - if (exp != FLOAT_EXP_IS_NAN && exp != 0) { - exp = exp + (uint_least8_t)(HALF_EXP_OFFSET - FLOAT_EXP_OFFSET); - } - /* Add exponent */ - half |= ((exp & HALF_EXP_MASK) << HALF_EXP_POS) | - ((*unum >> (FLOAT_EXP_POS - HALF_EXP_POS)) & HALF_FRAC_MASK); - return _fmt_halffloat(enc, half); - } - /* normal float */ - int res = enc->reserve(enc->stream, 1 + sizeof(float)); - if (res > 0) { - const uint_least8_t id = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_WORD; - - /* NOLINTNEXTLINE: user supplied function */ - const uint32_t bnum = NANOCBOR_HTOBE32_FUNC(*unum); - - enc->insert(enc->stream, &id, 1); - enc->insert(enc->stream, &bnum, sizeof(bnum)); - } - return res; -} - -int nanocbor_fmt_double(nanocbor_encoder_t *enc, double num) -{ - uint64_t *unum = (uint64_t *)# - uint16_t exp = (*unum >> DOUBLE_EXP_POS) & DOUBLE_EXP_MASK; - if (_double_is_inf_nan(exp) || - _double_is_zero(*unum) || - _double_in_range(exp, *unum)) { - /* copy sign bit over */ - uint32_t single = (*unum >> (DOUBLE_SIZE - FLOAT_SIZE)) & (FLOAT_SIGN_MASK); - /* Shift exponent */ - if (exp != DOUBLE_EXP_IS_NAN && exp != 0) { - exp = exp + FLOAT_EXP_OFFSET - DOUBLE_EXP_OFFSET; - } - single |= ((exp & FLOAT_EXP_MASK) << FLOAT_EXP_POS) | - ((*unum >> (DOUBLE_EXP_POS - FLOAT_EXP_POS)) & FLOAT_FRAC_MASK); - float *fsingle = (float*)&single; - return nanocbor_fmt_float(enc, *fsingle); - } - int res = enc->reserve(enc->stream, 1 + sizeof(double)); - if (res > 0) { - const uint_least8_t id = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_LONG; - - /* NOLINTNEXTLINE: user supplied function */ - uint64_t bnum = NANOCBOR_HTOBE64_FUNC(*unum); - - enc->insert(enc->stream, &id, 1); - enc->insert(enc->stream, &bnum, sizeof(bnum)); - } - return res; -} - -int nanocbor_fmt_decimal_frac(nanocbor_encoder_t *enc, int32_t e, int32_t m) -{ - int res = nanocbor_fmt_tag(enc, NANOCBOR_TAG_DEC_FRAC); - res += nanocbor_fmt_array(enc, 2); - res += nanocbor_fmt_int(enc, e); - res += nanocbor_fmt_int(enc, m); - return res; -} diff --git a/src/memory_buffer.c b/src/memory_buffer.c deleted file mode 100644 index 2f3e296..0000000 --- a/src/memory_buffer.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ -#include "nanocbor/nanocbor.h" -#include "nanocbor/stream_encoders/memory_buffer.h" -#include - -void MemoryStream_Init(memory_encoder *self, uint_least8_t *buf, size_t len) -{ - self->len = 0; - self->cur = buf; - self->end = buf + len; -} - -size_t MemoryStream_Length(memory_encoder *self) -{ - return self->len; -} - -int MemoryStream_Reserve(memory_encoder *self, size_t len) -{ - const int fits = ((size_t)(self->end - self->cur) >= len); - self->len += len; - return fits ? (int)len : NANOCBOR_ERR_END; -} - -void MemoryStream_Insert(memory_encoder *self, const void *src, size_t n) -{ - memcpy(self->cur, src, n); - self->cur += n; -} diff --git a/tests/automated/Makefile b/tests/automated/Makefile deleted file mode 100644 index 5a15f84..0000000 --- a/tests/automated/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -NANOCBOR_DIR = ../.. - -include ../../Makefile - -SRCS += main.c test_decoder.c test_encoder.c -LDFLAGS += -Wl,$(shell pkg-config --libs cunit || echo -lcunit) -CFLAGS += $(shell pkg-config --cflags cunit) - -$(CURDIR)/bin/test: $(OBJS) - $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@ - -test : CFLAGS += -g3 - -test: $(CURDIR)/bin/test - $(CURDIR)/bin/test - diff --git a/tests/automated/main.c b/tests/automated/main.c deleted file mode 100644 index 9b2c705..0000000 --- a/tests/automated/main.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include - -#include "CUnit/CUnit.h" -#include "CUnit/Basic.h" -#include "test.h" - -extern const test_t tests_decoder[]; -extern const test_t tests_encoder[]; - -static int add_tests(CU_pSuite pSuite, const test_t* tests) -{ - /* add the tests to the suite */ - for(int i = 0; tests[i].n !=NULL; i++) { - if(!(CU_add_test(pSuite, tests[i].n, tests[i].f))) { - printf("Error adding function %s\n",tests[i].n); - CU_cleanup_registry(); - return CU_get_error(); - } - } - return 0; -} - -int main() -{ - CU_pSuite pSuite = NULL; - if (CUE_SUCCESS != CU_initialize_registry()) - return CU_get_error(); - pSuite = CU_add_suite("Nanocbor decode", NULL, NULL); - if (NULL == pSuite) { - CU_cleanup_registry(); - return CU_get_error(); - } - add_tests(pSuite, tests_decoder); - - pSuite = CU_add_suite("Nanocbor encode", NULL, NULL); - if (NULL == pSuite) { - CU_cleanup_registry(); - return CU_get_error(); - } - add_tests(pSuite, tests_encoder); - - CU_basic_set_mode(CU_BRM_VERBOSE); - CU_basic_run_tests(); - printf("\n"); - - if (CU_get_number_of_failure_records()) { - exit(2); - } - return CU_get_error(); -} diff --git a/tests/automated/test.h b/tests/automated/test.h deleted file mode 100644 index 889b22e..0000000 --- a/tests/automated/test.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -#ifndef TEST_H -#define TEST_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Function prototype for a test function - */ -typedef void (*test_func)(void); - -/** - * Struct to define a test - */ -typedef struct test { - const test_func f; /**< Function to run as test */ - const char *n; /**< Name or description of the test */ -} test_t; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tests/automated/test_decoder.c b/tests/automated/test_decoder.c deleted file mode 100644 index 0b4bb95..0000000 --- a/tests/automated/test_decoder.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -#include "test.h" -#include "nanocbor/nanocbor.h" -#include - -static void test_decode_indefinite(void) -{ - /* Test vector, 3 integers in an indefinite array */ - static const uint8_t indefinite[] = { - 0x9f, 0x01, 0x02, 0x03, 0xff - }; - - nanocbor_value_t val; - nanocbor_value_t cont; - - uint32_t tmp = 0; - - nanocbor_decoder_init(&val, indefinite, sizeof(indefinite)); - - CU_ASSERT_EQUAL(nanocbor_enter_array(&val, &cont), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_container_indefinite(&cont), true); - - /* Decode the three values */ - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - - CU_ASSERT_EQUAL(nanocbor_get_uint32(&cont, &tmp), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); -} - -static void test_decode_map(void) -{ - - static const uint8_t map_empty[] = { - 0xa0 - }; - - static const uint8_t map_one[] = { - 0xa1, 0x01, 0x02 - }; - - static const uint8_t complex_map_decode[] = { - 0xa5, 0x01, 0x02, 0x03, 0x80, 0x04, 0x9F, 0xFF, 0x05, 0x9F, 0xff, 0x06, 0xf6 - }; - - nanocbor_value_t val; - nanocbor_value_t cont; - - uint32_t tmp = 0; - - /* Init the decoder and assert the properties of the empty map */ - nanocbor_decoder_init(&val, map_empty, sizeof(map_empty)); - CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); - nanocbor_leave_container(&val, &cont); - CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); - - /* Init the decoder and verify the decoding of the map elements */ - nanocbor_decoder_init(&val, map_one, sizeof(map_one)); - CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_OK); - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 1); - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 2); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); - nanocbor_leave_container(&val, &cont); - CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); - - /* Init the decoder and skip over the empty map */ - nanocbor_decoder_init(&val, map_empty, sizeof(map_empty)); - CU_ASSERT_EQUAL(nanocbor_skip(&val), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); - - /* Init the decoder and skip over the non-empty map */ - nanocbor_decoder_init(&val, map_one, sizeof(map_one)); - CU_ASSERT_EQUAL(nanocbor_skip(&val), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); - - nanocbor_value_t array; - /* Init decoder and start decoding */ - nanocbor_decoder_init(&val, complex_map_decode, sizeof(complex_map_decode)); - CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_OK); - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 1); - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 2); - - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 3); - CU_ASSERT_EQUAL(nanocbor_enter_array(&cont, &array), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_at_end(&array), true); - nanocbor_leave_container(&cont, &array); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); - - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 4); - CU_ASSERT_EQUAL(nanocbor_enter_array(&cont, &array), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_at_end(&array), true); - nanocbor_leave_container(&cont, &array); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); - - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 5); - CU_ASSERT_EQUAL(nanocbor_enter_array(&cont, &array), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_at_end(&array), true); - nanocbor_leave_container(&cont, &array); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); - - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 6); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); - CU_ASSERT_EQUAL(nanocbor_get_null(&cont), NANOCBOR_OK); - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); -} - -static void test_tag(void) -{ - static const uint8_t arraytag[] = { - 0x82, 0xd8, 0x37, 0x01, 0x02 - }; - - nanocbor_value_t val; - nanocbor_value_t cont; - - uint32_t tmp = 0x12345678; - - nanocbor_decoder_init(&val, arraytag, sizeof(arraytag)); - - CU_ASSERT_EQUAL(nanocbor_enter_array(&val, &cont), NANOCBOR_OK); - - CU_ASSERT_EQUAL(nanocbor_get_tag(&cont, &tmp), NANOCBOR_OK); - CU_ASSERT_EQUAL(tmp, 0x37); - - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 1); - - CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); - CU_ASSERT_EQUAL(tmp, 2); - - CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); -} - -static void test_decode_none(void) -{ - nanocbor_value_t val; - nanocbor_value_t cont; - uint64_t tmp; - nanocbor_decoder_init(&val, NULL, 0); - - CU_ASSERT_EQUAL(nanocbor_get_type(&val), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_get_uint32(&val, (uint32_t*)&tmp), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_get_int32(&val, (int32_t*)&tmp), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_enter_array(&val, &cont), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_get_null(&val), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_get_bool(&val, (bool*)&tmp), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_skip(&val), NANOCBOR_ERR_END); - CU_ASSERT_EQUAL(nanocbor_skip_simple(&val), NANOCBOR_ERR_END); -} - -static void test_decode_basic(void) -{ - nanocbor_value_t decoder; - uint8_t byteval = 5; /* unsigned integer, value 5 */ - uint32_t value = 0; - - nanocbor_decoder_init(&decoder, &byteval, sizeof(byteval)); - CU_ASSERT_EQUAL(nanocbor_get_type(&decoder), NANOCBOR_TYPE_UINT); - printf("\"val: %u\"\n", value); - CU_ASSERT_EQUAL(nanocbor_get_uint32(&decoder, &value), 1); - printf("\"val: %u\"\n", value); - CU_ASSERT_EQUAL(5, value); - - int32_t intval = 0; - nanocbor_decoder_init(&decoder, &byteval, sizeof(byteval)); - CU_ASSERT_EQUAL(nanocbor_get_int32(&decoder, &intval), 1); - CU_ASSERT_EQUAL(5, intval); - - const uint8_t decimal_frac[] = { 0xC4, 0x82, 0x21, 0x19, 0x6a, 0xb3 }; - int32_t m; - int32_t e; - nanocbor_decoder_init(&decoder, decimal_frac, sizeof(decimal_frac)); - CU_ASSERT_EQUAL(nanocbor_get_decimal_frac(&decoder, &e, &m), 0); - CU_ASSERT_EQUAL(e, -2); - CU_ASSERT_EQUAL(m, 27315); -} - -const test_t tests_decoder[] = { - { - .f = test_decode_none, - .n = "get type on empty buffer", - }, - { - .f = test_decode_basic, - .n = "Simple CBOR integer tests", - }, - { - .f = test_decode_indefinite, - .n = "CBOR indefinite array decode tests", - }, - { - .f = test_decode_map, - .n = "CBOR map decode tests", - }, - { - .f = test_tag, - .n = "CBOR tag decode test", - }, - { - .f = NULL, - .n = NULL, - } -}; diff --git a/tests/automated/test_encoder.c b/tests/automated/test_encoder.c deleted file mode 100644 index c210f21..0000000 --- a/tests/automated/test_encoder.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -#include "test.h" -#include "nanocbor/nanocbor.h" -#include "nanocbor/stream_encoders/memory_buffer.h" -#include -#include -#include - -static void print_bytestr(const uint8_t *bytes, size_t len) -{ - printf("\n"); - for(unsigned int idx=0; idx < len; idx++) - { - printf("%02X", bytes[idx]); - } - printf("\n"); -} - -static void test_encode_float_specials(void) -{ - uint8_t buf[64]; - memory_encoder stream; - MemoryStream_Init(&stream, buf, sizeof(buf)); - - FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; - FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; - FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; - - nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); - - nanocbor_fmt_array_indefinite(&enc); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, NAN), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -NAN), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, INFINITY), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -INFINITY), 3); - - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, NAN), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -NAN), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, INFINITY), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -INFINITY), 3); - nanocbor_fmt_end_indefinite(&enc); - print_bytestr(buf, nanocbor_encoded_len(&enc)); -} - -static void test_encode_float_to_half(void) -{ - uint8_t buf[64]; - memory_encoder stream; - MemoryStream_Init(&stream, buf, sizeof(buf)); - - FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; - FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; - FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; - - nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); - - nanocbor_fmt_array_indefinite(&enc); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 1.75), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 1.9990234375), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 1.99951171875), 5); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 2.0009765625), 5); - - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -1.75), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -1.9990234375), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -1.99951171875), 5); - CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -2.0009765625), 5); - - nanocbor_fmt_end_indefinite(&enc); - print_bytestr(buf, nanocbor_encoded_len(&enc)); -} - -static void test_encode_double_to_float(void) -{ - uint8_t buf[128]; - memory_encoder stream; - MemoryStream_Init(&stream, buf, sizeof(buf)); - - FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; - FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; - FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; - - nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); - - nanocbor_fmt_array_indefinite(&enc); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.75), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.9990234375), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.99951171875), 5); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 2.0009765625), 5); - - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -1.75), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -1.9990234375), 3); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -1.99951171875), 5); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -2.0009765625), 5); - - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.00000011920928955078125), 5); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, DBL_MIN), 9); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, DBL_MAX), 9); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1e39), 9); - - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, FLT_MIN), 5); - CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, FLT_MAX), 5); - - nanocbor_fmt_end_indefinite(&enc); - print_bytestr(buf, nanocbor_encoded_len(&enc)); -} - -const test_t tests_encoder[] = { - { - .f = test_encode_float_specials, - .n = "Float reduction encoder test", - }, - { - .f = test_encode_float_to_half, - .n = "Float reduction encoder test", - }, - { - .f = test_encode_double_to_float, - .n = "Double reduction encoder test", - }, - { - .f = NULL, - .n = NULL, - } -}; diff --git a/tests/encode/Makefile b/tests/encode/Makefile deleted file mode 100644 index 581364d..0000000 --- a/tests/encode/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -NANOCBOR_DIR = $(CURDIR)/../.. - -include ../../Makefile - -SRCS += main.c - -bin/encode: $(OBJS) - $(CC) $(CFLAGS) $(OBJS) -o $@ - -test : CFLAGS += -g3 - -test: bin/encode diff --git a/tests/encode/main.c b/tests/encode/main.c deleted file mode 100644 index 364e9bd..0000000 --- a/tests/encode/main.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include -#include -#include - -#include "nanocbor/nanocbor.h" -#include "nanocbor/stream_encoders/memory_buffer.h" - -static void _encode(nanocbor_encoder_t *enc) -{ - nanocbor_fmt_array_indefinite(enc); - nanocbor_fmt_bool(enc, true); - nanocbor_fmt_bool(enc, false); - nanocbor_fmt_uint(enc, UINT32_MAX); - nanocbor_fmt_int(enc, INT32_MIN); - nanocbor_fmt_map(enc, 4); - nanocbor_fmt_uint(enc, 8); - nanocbor_fmt_int(enc, 30); - nanocbor_fmt_int(enc, -30); - nanocbor_fmt_int(enc, 500); - nanocbor_fmt_int(enc, -500); - nanocbor_put_tstr(enc, "this is a long string"); - nanocbor_fmt_float(enc, 0.34); - nanocbor_put_bstr(enc, (uint8_t*)"bytez", sizeof("bytez")); - nanocbor_fmt_null(enc); - nanocbor_fmt_decimal_frac(enc, -2, 27315); - nanocbor_fmt_end_indefinite(enc); -} - -int main(void) -{ - memory_encoder stream; - - MemoryStream_Init(&stream, NULL, 0); - - FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; - FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; - FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; - - nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); - - _encode(&enc); - - size_t required = nanocbor_encoded_len(&enc); - - uint8_t *buf = malloc(required); - if (!buf) { - return -1; - } - - MemoryStream_Init(&stream, buf, required); - - _encode(&enc); - - //printf("Bytes: %u\n", (unsigned)nanocbor_encoded_len(&enc)); - fwrite(buf, 1, nanocbor_encoded_len(&enc), stdout); - - return 0; -} diff --git a/tests/encode/out.txt b/tests/encode/out.txt deleted file mode 100644 index 15a5d744f8fd1c5bebfbd10f92181aba200bf762..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmYk0F-`+96hukK0VoJ4=pDgcd+jv^C8wbAE_Mu`0 zz}M(&{%B_OLnhZ`i;iUBdzgsda}lFOHE2P?$i7AglrBNDBL-jNEv~@@yrEM6i2gY{ zT*f7wFQ<6K@2g_li8*2tF`-3ou?|<^rA=z3JfalY(OH@b-Tr<1`*tA`57Zse6vN~D z=42~#elP3!@c6754F{Vrc)RT<(}Eb@*)OK^@#n?M(cNXc>eKq*LqCjP!WcB#LLVf{i2g6;&B;geMnx& zYgb(d&I^nuML(gY%`c_^k9JPwHU)e!^Bhj=1T`v(W)^Ed_5iS4i7;%zwSDDI^Y)>b z(FV}AH?(N?t@io`muyazd)gh#4CQ_15dH-QfxTePh; vfS1Pcv|q^uCS -#include -#include -#include -#include - -#include "nanocbor/nanocbor.h" - -char buffer[256]; - -static int _parse_type(nanocbor_value_t *value, unsigned indent); - -static void _indent(unsigned indent) -{ - for(unsigned i = 0; i < indent; i++) { - printf(" "); - } -} - -void _parse_cbor(nanocbor_value_t *it, unsigned indent) -{ - while (!nanocbor_at_end(it)) { - _indent(indent); - int res = _parse_type(it, indent); - printf(",\n"); - if (res < 0) { - printf("Err\n"); - break; - } - } -} - -void _parse_map(nanocbor_value_t *it, unsigned indent) -{ - while (!nanocbor_at_end(it)) { - _indent(indent); - int res = _parse_type(it, indent); - printf(": "); - if (res < 0) { - printf("Err\n"); - break; - } - res = _parse_type(it, indent); - if (res < 0) { - printf("Err\n"); - break; - } - printf(",\n"); - } -} - -static int _parse_type(nanocbor_value_t *value, unsigned indent) -{ - uint8_t type = nanocbor_get_type(value); - if (indent > 10) { - return -2; - } - switch (type) { - case NANOCBOR_TYPE_UINT: - { - uint32_t uint; - if (nanocbor_get_uint32(value, &uint) >= 0) { - printf("%lu", (long unsigned)uint); - } - else { - return -1; - } - } - break; - case NANOCBOR_TYPE_NINT: - { - int32_t int32; - if (nanocbor_get_int32(value, &int32) >= 0) { - printf("%ld", (long unsigned)int32); - } - else { - return -1; - } - } - break; - case NANOCBOR_TYPE_BSTR: - { - const uint8_t *buf = NULL; - size_t len; - if (nanocbor_get_bstr(value, &buf, &len) >= 0 && buf) { - size_t iter = 0; - printf("\""); - while(iter < len) { - printf("0x%.2x, ", buf[iter]); - iter++; - } - printf("\""); - } - else { - return -1; - } - } - break; - case NANOCBOR_TYPE_TSTR: - { - const uint8_t *buf; - size_t len; - if (nanocbor_get_tstr(value, &buf, &len) >= 0) { - printf("\"%.*s\"", (int)len, buf); - } - else { - return -1; - } - } - break; - case NANOCBOR_TYPE_ARR: - { - nanocbor_value_t arr; - if (nanocbor_enter_array(value, &arr) >= 0) { - printf("[\n"); - _parse_cbor(&arr, indent + 1); - nanocbor_leave_container(value, &arr); - _indent(indent); - printf("]"); - } - else { - return -1; - } - } - break; - case NANOCBOR_TYPE_MAP: - { - nanocbor_value_t map; - if (nanocbor_enter_map(value, &map) >= NANOCBOR_OK) {; - printf("{\n"); - _parse_map(&map, indent + 1); - nanocbor_leave_container(value, &map); - _indent(indent); - printf("}"); - } - else { - return -1; - } - } - break; - case NANOCBOR_TYPE_FLOAT: - { - bool test; - if (nanocbor_get_bool(value, &test) >= NANOCBOR_OK) { - test ? printf("True") : printf("False"); - } - else if (nanocbor_get_null(value) >= NANOCBOR_OK) { - printf("NULL"); - } - else if (nanocbor_skip_simple(value) >= 0) { - printf("Unsupported float"); - } - else { - return -1; - } - break; - } - default: - printf("Unsupported type\n"); - return -1; - - } - return 1; -} - -int main(void) -{ - ssize_t len = read(STDIN_FILENO, buffer, sizeof(buffer)); - printf("Reading %ld bytes from stdin\n", (long signed)len); - if (len < 0) { - return -1; - } - - nanocbor_value_t it; - nanocbor_decoder_init(&it, (uint8_t*)buffer, len); - while (!nanocbor_at_end(&it)) { - printf("advancing\n"); - if(nanocbor_skip(&it) < 0) { - break; - } - } - - nanocbor_decoder_init(&it, (uint8_t*)buffer, len); - printf("parsing cbor\n"); - _parse_cbor(&it, 0); - printf("Done parsing cbor\n"); - - return 0; -} diff --git a/uncrustify.cfg b/uncrustify.cfg deleted file mode 100644 index d1a8bae..0000000 --- a/uncrustify.cfg +++ /dev/null @@ -1,69 +0,0 @@ -indent_with_tabs = 0 # 1=indent to level only, 2=indent with tabs -input_tab_size = 4 # original tab size -output_tab_size = 4 # new tab size -indent_columns = output_tab_size -indent_label = 1 # pos: absolute col, neg: relative column -indent_switch_case = 4 # number - -# -# inter-symbol newlines -# - -nl_enum_brace = remove # "enum {" vs "enum \n {" -nl_union_brace = remove # "union {" vs "union \n {" -nl_struct_brace = remove # "struct {" vs "struct \n {" -nl_do_brace = remove # "do {" vs "do \n {" -nl_if_brace = remove # "if () {" vs "if () \n {" -nl_for_brace = remove # "for () {" vs "for () \n {" -nl_else_brace = remove # "else {" vs "else \n {" -nl_while_brace = remove # "while () {" vs "while () \n {" -nl_switch_brace = remove # "switch () {" vs "switch () \n {" -nl_brace_while = remove # "} while" vs "} \n while" - cuddle while -nl_brace_else = add # "} \n else" vs "} else" -nl_func_var_def_blk = 1 -nl_fcall_brace = remove # "list_for_each() {" vs "list_for_each()\n{" -nl_fdef_brace = add # "int foo() {" vs "int foo()\n{" - -# -# Source code modifications -# - -mod_paren_on_return = ignore # "return 1;" vs "return (1);" -mod_full_brace_if = add # "if() { } else { }" vs "if() else" - -# -# inter-character spacing options -# - -sp_sizeof_paren = remove # "sizeof (int)" vs "sizeof(int)" -sp_before_sparen = force # "if (" vs "if(" -sp_after_sparen = force # "if () {" vs "if (){" -sp_inside_braces = add # "{ 1 }" vs "{1}" -sp_inside_braces_struct = add # "{ 1 }" vs "{1}" -sp_inside_braces_enum = add # "{ 1 }" vs "{1}" -sp_assign = add -sp_arith = add -sp_bool = add -sp_compare = add -sp_assign = add -sp_after_comma = add -sp_func_def_paren = remove # "int foo (){" vs "int foo(){" -sp_func_call_paren = remove # "foo (" vs "foo(" -sp_func_proto_paren = remove # "int foo ();" vs "int foo();" -sp_else_brace = add # ignore/add/remove/force -sp_before_ptr_star = add # ignore/add/remove/force -sp_after_ptr_star = remove # ignore/add/remove/force -sp_between_ptr_star = remove # ignore/add/remove/force -sp_inside_paren = remove # remove spaces inside parens -sp_paren_paren = remove # remove spaces between nested parens -sp_inside_sparen = remove # remove spaces inside parens for if, while and the like - -# -# Aligning stuff -# - -align_with_tabs = FALSE # use tabs to align -align_on_tabstop = TRUE # align on tabstops -align_enum_equ_span = 4 # '=' in enum definition -align_struct_init_span = 0 # align stuff in a structure init '= { }' -align_right_cmt_span = 3 From 78a36fc086e230582e30e88df68b6ab98fc0f78e Mon Sep 17 00:00:00 2001 From: ainsworthkohler <122799479+ainsworthkohler@users.noreply.github.com> Date: Mon, 16 Jan 2023 10:57:22 -0500 Subject: [PATCH 3/4] Revert "addingSubmod" This reverts commit 29fe59a001e44ae09caa1956984294c8c4172e96. --- .cirrus.yml | 50 ++ .gitignore | 17 + LICENSE | 121 +++ Makefile | 48 + README.md | 191 ++++ doxyfile | 372 ++++++++ include/nanocbor/config.h | 87 ++ include/nanocbor/nanocbor.h | 823 ++++++++++++++++++ .../nanocbor/stream_encoders/memory_buffer.h | 23 + src/decoder.c | 455 ++++++++++ src/encoder.c | 382 ++++++++ src/memory_buffer.c | 31 + tests/automated/Makefile | 16 + tests/automated/main.c | 56 ++ tests/automated/test.h | 29 + tests/automated/test_decoder.c | 217 +++++ tests/automated/test_encoder.c | 127 +++ tests/encode/Makefile | 12 + tests/encode/main.c | 64 ++ tests/encode/out.txt | Bin 0 -> 300 bytes tests/fuzz/Makefile | 12 + tests/fuzz/README.md | 17 + tests/fuzz/inputs/array.cbor | 1 + tests/fuzz/inputs/complex.cbor | Bin 0 -> 37 bytes tests/fuzz/inputs/map.cbor | 1 + tests/fuzz/inputs/random.cbor | Bin 0 -> 193 bytes tests/fuzz/inputs/simle.cbor | 1 + tests/fuzz/main.c | 193 ++++ uncrustify.cfg | 69 ++ 29 files changed, 3415 insertions(+) create mode 100644 .cirrus.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 doxyfile create mode 100644 include/nanocbor/config.h create mode 100644 include/nanocbor/nanocbor.h create mode 100644 include/nanocbor/stream_encoders/memory_buffer.h create mode 100644 src/decoder.c create mode 100644 src/encoder.c create mode 100644 src/memory_buffer.c create mode 100644 tests/automated/Makefile create mode 100644 tests/automated/main.c create mode 100644 tests/automated/test.h create mode 100644 tests/automated/test_decoder.c create mode 100644 tests/automated/test_encoder.c create mode 100644 tests/encode/Makefile create mode 100644 tests/encode/main.c create mode 100644 tests/encode/out.txt create mode 100644 tests/fuzz/Makefile create mode 100644 tests/fuzz/README.md create mode 100644 tests/fuzz/inputs/array.cbor create mode 100644 tests/fuzz/inputs/complex.cbor create mode 100644 tests/fuzz/inputs/map.cbor create mode 100644 tests/fuzz/inputs/random.cbor create mode 100644 tests/fuzz/inputs/simle.cbor create mode 100644 tests/fuzz/main.c create mode 100644 uncrustify.cfg diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000..e52c59d --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,50 @@ +container: + cpu: 1 + memory: 128Mb + +lint_task: + container: + image: silkeh/clang:latest + test_script: make clang-tidy + +build_task: + depends_on: + - lint + install_script: + - apt update + - apt install -y libcunit1-dev + matrix: + - name: clang + env: + CC: clang + container: + matrix: + image: silkeh/clang:7 + image: silkeh/clang:8 + install_libgcc_script: + - apt install -y libgcc-9-dev + - name: gcc + env: + CC: gcc + container: + matrix: + image: gcc:8 + image: gcc:9 + test_script: + - make -C tests/automated clean test + - make bin/nanocbor.so + + +arm_task: + container: + image: hiberglobal/gcc-arm-none-eabi:8-2018-q4-major + env: + CFLAGS: -mno-thumb-interwork -mcpu=cortex-m0plus -mlittle-endian -mthumb -mfloat-abi=soft -march=armv6s-m -DNANOCBOR_BYTEORDER_HEADER=\"stddef.h\" -DNANOCBOR_BE64TOH_FUNC=__builtin_bswap64 -DNANOCBOR_HTOBE64_FUNC=__builtin_bswap64 -DNANOCBOR_HTOBE32_FUNC=__builtin_bswap32 + CC: arm-none-eabi-gcc + install_script: + - apk add make + test_script: + - make objs + - arm-none-eabi-size bin/objs/* > output.txt + size_artifacts: + path: output.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8463cac --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# C build files +*.o +build +bin + +# Editor related +tags +*.swp +*.swo + +# docs +docs + + +# other +outputs +core.* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6d569ef --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +NANOCBOR_DIR ?= $(CURDIR) + +# cannot simply use CC ?= gcc, as CC defaults to "cc" if undefined on GNU Make +ifeq ($(origin CC),default) + CC := gcc +endif +RM = rm -rf +TIDY ?= clang-tidy +CFLAGS ?= + +INC_GLOBAL ?= /usr/include +INC_DIR = $(NANOCBOR_DIR)/include +SRC_DIR = $(NANOCBOR_DIR)/src + +TEST_DIR=tests + +BIN_DIR ?= bin +OBJ_DIR ?= $(BIN_DIR)/objs + +# Only check at issues present for c99 compatible code +CFLAGS_TIDY ?= -std=c99 +TIDYFLAGS=-checks=*,-llvmlibc-restrict-system-libc-headers,-bugprone-reserved-identifier,-cert-* -warnings-as-errors=* + +CFLAGS_WARN += -Wall -Wextra -pedantic -Werror -Wshadow +CFLAGS += -fPIC $(CFLAGS_WARN) -I$(INC_DIR) -I$(INC_GLOBAL) -Og -g3 + +SRCS ?= $(wildcard $(SRC_DIR)/*.c) +OBJS ?= $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS)) + +lib: $(BIN_DIR)/nanocbor.so + +prepare: + @mkdir -p $(OBJ_DIR) + +# Build a binary +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c prepare + $(CC) $(CFLAGS) -c -o $@ $< + +objs: $(OBJS) + +$(BIN_DIR)/nanocbor.so: objs + $(CC) $(CFLAGS) $(OBJS) -o $@ -shared + +clang-tidy: + $(TIDY) $(TIDYFLAGS) $(SRCS) -- $(CFLAGS) $(CFLAGS_TIDY) + +clean: + $(RM) $(BIN_DIR) diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d73fa2 --- /dev/null +++ b/README.md @@ -0,0 +1,191 @@ +# NanoCBOR + +NanoCBOR is a tiny [CBOR](https://tools.ietf.org/html/rfc7049) library aimed at embedded and heavily constrained devices. +It is optimized for 32 bit architectures but should run fine on 8 bit and 16 bit architectures. +NanoCBOR is optimized for decoding known CBOR structures while optimizing the flash footprint of both NanoCBOR and the code using NanoCBOR. + +The decoder of NanoCBOR should compile to 600-800 bytes on a Cortex-M0+ MCU, depending on whether floating point decoding is required. + +## Usage + +To achieve the small code size, two patterns are used throughout the decode library. + + - Every decode call will first check the type and refuse to decode if the CBOR element is not of the required type. + - Every decode call will, on successful decode, advance the decode context to the next CBOR element. + +This allows using code to call decode functions and check the return code of the function without requiring an if value of type, decode value, advance to next item dance, and requiring only a single call to decode an expected type and advance to the next element. + +### Decoding + +Start the decoding of a buffer with: + +```C +nanocbor_value_t decoder; +nanocbor_decoder_init(&decoder, buffer, buffer_len); +``` + +Where `buffer` is an `const uint_least8_t` array containing an CBOR structure. + +To decode an `int32_t` from a cbor structure and bail out if the element is not of the integer type: + +```C +int32_t value = 0; +if (nanocbor_get_int32(&decoder, &value) < 0) { + return ERR_INVALID_STRUCTURE; +} +return use_value(value); +``` + +Iterating over an CBOR array and calling a function passing every element is as simple as: + +```C +nanocbor_value_t arr; /* Array value instance */ + +if (nanocbor_enter_array(&decoder, &arr) < 0) { + return ERR_INVALID_STRUCTURE; +} +while (!nanocbor_at_end(&arr)) { + handle_array_element(&arr); +} +``` + +Decoding a map is similar to an array, except that every map entry consists of two CBOR elements requiring separate decoding. +For example, a map using integers as keys and strings as values can be decoded with: + +```C +while (!nanocbor_at_end(&map)) { + int32_t key; + const char *value; + size_t value_len; + if (nanocbor_get_int32(&map, &integer_key) < 0) { + return ERR_INVALID_STRUCTURE; + } + if (nanocbor_get_tstr(&map, &value, &value_len) < 0) { + return ERR_INVALID_STRUCTURE; + } + handle_map_element(key, value, value_len); +} +``` + +### Encoding + +NanoCBOR supports encoding via a polymorphic stream interface via function +pointers. This methods on this interface are: + +``` +typedef size_t (*FnStreamLength)(void *stream); +typedef int (*FnStreamReserve)(void *stream, size_t len); +typedef void (*FnStreamInsert)(void *stream, const void *src, size_t n); +``` + +Where `FnStreamLength` queries the stream about how many CBOR bytes have been +provided, `FnStreamReserve` reserves the requested number of bytes within the +stream, and `FnStreamInsert` moves bytes into the stream. Each of these +functions accept a `stream` object, which contains the stream state if one is +needed. This is exactly like the "this" pointer in other object oriented +languages. + +The `memory_buffer.h/c` is the canonical stream encoder, it expected a large +memory buffer that it can `memcpy` CBOR data into. + +Construction of these objects looks like: + +``` +uint_least8_t buf[64]; +memory_encoder stream; +MemoryStream_Init(&stream, buf, sizeof(buf)); + +FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; +FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; +FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; + +nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); +``` + +With a valid `nanocbor_encoder_t` you can pass it to the `fmt` functions: + +``` +nanocbor_fmt_array_indefinite(&enc); +nanocbor_fmt_float(&enc, 1.75); +nanocbor_fmt_float(&enc, 1.9990234375); +nanocbor_fmt_float(&enc, 1.99951171875); +nanocbor_fmt_float(&enc, 2.0009765625); +nanocbor_fmt_float(&enc, -1.75); +nanocbor_fmt_float(&enc, -1.9990234375); +nanocbor_fmt_float(&enc, -1.99951171875); +nanocbor_fmt_float(&enc, -2.0009765625); +nanocbor_fmt_end_indefinite(&enc); +``` + +The encoder places bytes in the stream on demand, so after you are done you can +find your encoded CBOR data within the buffer you created: + +``` +size_t len = nanocbor_encoded_len(&enc); + +printf("\n"); +for(unsigned int idx=0; idx < len; idx++) +{ + printf("%02X", buf[idx]); +} +printf("\n"); +``` + +#### Implementing a new stream + +Here is an example of a `stdout` stream and how to use it: + +``` +typedef struct stdout_stream { + int len; +} stdout_stream; + +void Stdout_Init(stdout_stream *self) { + self->len = 0; +} + +size_t Stdout_Length(stdout_stream *self) { + return self->len; +} + +int Stdout_Reserve(stdout_stream *self, size_t len) { + self->len += len; + return (int)len; +} + +void Stdout_Insert(stdout_stream *self, const void *src, size_t n) { + const uint_least8_t *bytes = (const uint_least8_t *)src; + for (size_t i = 0; i < n; ++i) { + printf("%02X ", bytes[i]); + } +} + +void encode(void) { + stdout_stream stream; + Stdout_Init(&stream); + + FnStreamLength len_fn = (FnStreamLength)Stdout_Length; + FnStreamReserve res_fn = (FnStreamReserve)Stdout_Reserve; + FnStreamInsert ins_fn = (FnStreamInsert)Stdout_Insert; + + nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); + nanocbor_fmt_array_indefinite(&enc); + nanocbor_fmt_float(&enc, 1.75); + nanocbor_fmt_float(&enc, 1.9990234375); + nanocbor_fmt_float(&enc, 1.99951171875); + nanocbor_fmt_float(&enc, 2.0009765625); + nanocbor_fmt_end_indefinite(&enc); +} +``` + + +### Dependencies + +Only dependency are two functions to provide endian conversion. +These are not provided by the library and have to be configured in the header file. +On a bare metal ARM platform, `__builtin_bswap64` and `__builtin_bswap32` can be used for this conversion. + +### Contributing + +Open an issue, PR, the usual. + diff --git a/doxyfile b/doxyfile new file mode 100644 index 0000000..3a85d3a --- /dev/null +++ b/doxyfile @@ -0,0 +1,372 @@ +# Doxyfile 1.8.13 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "NanoCBOR" +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = docs +CREATE_SUBDIRS = YES +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = YES +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 0 +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = include/ README.md +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = README.md +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = . +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = NO +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/include/nanocbor/config.h b/include/nanocbor/config.h new file mode 100644 index 0000000..84103e6 --- /dev/null +++ b/include/nanocbor/config.h @@ -0,0 +1,87 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +/** + * @defgroup nanocbor_config NanoCBOR configuration header + * @brief Provides compile-time configuration for nanocbor + * + * @{ + * + * @file + * + * @author Koen Zandberg + */ + +#ifndef NANOCBOR_CONFIG_H +#define NANOCBOR_CONFIG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Recursion limit when using @ref nanocbor_skip. + */ +#ifndef NANOCBOR_RECURSION_MAX +#define NANOCBOR_RECURSION_MAX 10 +#endif + +/** + * @brief library providing htonll, be64toh or equivalent. Must also provide + * the reverse operation (ntohll, htobe64 or equivalent) + */ +#ifndef NANOCBOR_BYTEORDER_HEADER +#define NANOCBOR_BYTEORDER_HEADER "endian.h" +#endif + +/** + * @brief call providing htonll or be64toh or equivalent functionality + * + * must take a uint64_t big endian and return it in host endianess + */ +#ifndef NANOCBOR_BE64TOH_FUNC +#define NANOCBOR_BE64TOH_FUNC(be) (be64toh(be)) +#endif + +/** + * @brief call providing htonll or htobe64 or equivalent functionality + * + * must take a uint64_t in host endianess and return the big endian value + */ +#ifndef NANOCBOR_HTOBE64_FUNC +#define NANOCBOR_HTOBE64_FUNC(he) htobe64(he) +#endif + +/** + * @brief call providing htonl or htobe32 or equivalent functionality + * + * must take a uint32_t in host endianess and return the big endian value + */ +#ifndef NANOCBOR_HTOBE32_FUNC +#define NANOCBOR_HTOBE32_FUNC(he) htobe32(he) +#endif + +/** + * @brief configuration for size_t SIZE_MAX equivalent + */ +#ifndef NANOCBOR_SIZE_SIZET +#if (SIZE_MAX == UINT16_MAX) +#define NANOCBOR_SIZE_SIZET NANOCBOR_SIZE_SHORT +#elif (SIZE_MAX == UINT32_MAX) +#define NANOCBOR_SIZE_SIZET NANOCBOR_SIZE_WORD +#elif (SIZE_MAX == UINT64_MAX) +#define NANOCBOR_SIZE_SIZET NANOCBOR_SIZE_LONG +#else +#error ERROR: unable to determine maximum size of size_t +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NANOCBOR_CONFIG_H */ +/** @} */ diff --git a/include/nanocbor/nanocbor.h b/include/nanocbor/nanocbor.h new file mode 100644 index 0000000..79d3ac1 --- /dev/null +++ b/include/nanocbor/nanocbor.h @@ -0,0 +1,823 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +/** + * @defgroup NanoCBOR minimalistic CBOR library + * @brief Provides a minimal CBOR library + * + * NanoCBOR is a minimal CBOR encoder. For protocols such as CoAP, OSCORE, + * SenML and CORECONF a well defined and thus predictable CBOR structure is + * required. NanoCBOR tries to fill this requirement by providing a very + * minimal CBOR encoder. Supported is: + * - All major types + * - Arrays including indefinite length arrays + * - Maps including indefinite length maps + * - Safe for decoding untrusted input + * + * Not included: + * - Date and time + * - Big numbers (numbers encoded as byte strings) + * + * @{ + * + * @file + * @see [rfc 7049](https://tools.ietf.org/html/rfc7049) + * + * @author Koen Zandberg + */ + +#ifndef NANOCBOR_H +#define NANOCBOR_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NANOCBOR_TYPE_OFFSET (5U) /**< Bit shift for CBOR major types */ +#define NANOCBOR_TYPE_MASK 0xE0U /**< Mask for CBOR major types */ +#define NANOCBOR_VALUE_MASK 0x1FU /**< Mask for CBOR values */ + +/** + * @name CBOR type numbers + * @{ + */ +#define NANOCBOR_TYPE_UINT (0x00U) /**< positive integer type */ +#define NANOCBOR_TYPE_NINT (0x01U) /**< negative integer type */ +#define NANOCBOR_TYPE_BSTR (0x02U) /**< byte string type */ +#define NANOCBOR_TYPE_TSTR (0x03U) /**< text string type */ +#define NANOCBOR_TYPE_ARR (0x04U) /**< array type */ +#define NANOCBOR_TYPE_MAP (0x05U) /**< map type */ +#define NANOCBOR_TYPE_TAG (0x06U) /**< tag type */ +#define NANOCBOR_TYPE_FLOAT (0x07U) /**< float type */ +/** @} */ + +/** + * @name CBOR major types including the bit shift + * @{ + */ +#define NANOCBOR_MASK_UINT (NANOCBOR_TYPE_UINT << NANOCBOR_TYPE_OFFSET) +#define NANOCBOR_MASK_NINT (NANOCBOR_TYPE_NINT << NANOCBOR_TYPE_OFFSET) +#define NANOCBOR_MASK_BSTR (NANOCBOR_TYPE_BSTR << NANOCBOR_TYPE_OFFSET) +#define NANOCBOR_MASK_TSTR (NANOCBOR_TYPE_TSTR << NANOCBOR_TYPE_OFFSET) +#define NANOCBOR_MASK_ARR (NANOCBOR_TYPE_ARR << NANOCBOR_TYPE_OFFSET) +#define NANOCBOR_MASK_MAP (NANOCBOR_TYPE_MAP << NANOCBOR_TYPE_OFFSET) +#define NANOCBOR_MASK_TAG (NANOCBOR_TYPE_TAG << NANOCBOR_TYPE_OFFSET) +#define NANOCBOR_MASK_FLOAT (NANOCBOR_TYPE_FLOAT << NANOCBOR_TYPE_OFFSET) +/** @} */ + +/** + * @name CBOR simple data types + * @{ + */ +#define NANOCBOR_SIMPLE_FALSE 20U /**< False */ +#define NANOCBOR_SIMPLE_TRUE 21U /**< True */ +#define NANOCBOR_SIMPLE_NULL 22U /**< NULL */ +#define NANOCBOR_SIMPLE_UNDEF 23U /**< Undefined */ +/** @} */ + +/** + * @name CBOR data sizes + * @{ + */ +#define NANOCBOR_SIZE_BYTE 24U /**< Value contained in a byte */ +#define NANOCBOR_SIZE_SHORT 25U /**< Value contained in a short */ +#define NANOCBOR_SIZE_WORD 26U /**< Value contained in a word */ +#define NANOCBOR_SIZE_LONG 27U /**< Value contained in a long */ +#define NANOCBOR_SIZE_INDEFINITE 31U /**< Indefinite sized container */ +/** @} */ + +/** + * @name CBOR Tag values + * @{ + */ +#define NANOCBOR_TAG_DATE_TIME (0x0) /**< Standard date/time string */ +#define NANOCBOR_TAG_EPOCH (0x1) /**< Epoch-based date/time */ +#define NANOCBOR_TAG_BIGNUMS_P (0x2) /**< Positive bignum */ +#define NANOCBOR_TAG_BIGNUMS_N (0x3) /**< Negative bignum */ +#define NANOCBOR_TAG_DEC_FRAC (0x4) /**< Decimal Fraction */ +#define NANOCBOR_TAG_BIGFLOATS (0x5) /**< Bigfloat */ +#define NANOCBOR_TAG_OBJECT (27) /**< Generic Object */ +/** @} */ + +/** + * @brief NanoCBOR decoder errors + */ +typedef enum { + /** + * @brief No error + */ + NANOCBOR_OK = 0, + + /** + * @brief Overflow in the getter. This can happen due to retrieving a + * number size larger than the function provides + */ + NANOCBOR_ERR_OVERFLOW = -1, + + /** + * Decoder get function attempts to retrieve the wrong type + */ + NANOCBOR_ERR_INVALID_TYPE = -2, + + /** + * @brief decoder is beyond the end of the buffer + */ + NANOCBOR_ERR_END = -3, + + /** + * @brief Decoder hits the recursion limit + */ + NANOCBOR_ERR_RECURSION = -4, + + /** + * @brief Decoder could not find the requested entry + */ + NANOCBOR_NOT_FOUND = -5, +} nanocbor_error_t; + + +/** + * @brief decoder context + */ +typedef struct nanocbor_value { + const uint_least8_t *cur; /**< Current position in the buffer */ + const uint_least8_t *end; /**< End of the buffer */ + uint32_t remaining; /**< Number of items remaining in the container */ + uint_least8_t flags; /**< Flags for decoding hints */ +} nanocbor_value_t; + +/** + * @name stream interface definition + * @{ + */ + +/** + * @brief Length in bytes of supplied cbor data. + * Incremented separate from the buffer check. + * + * @param[in] stream the private data of the stream implementation + * + * @return length in bytes of supplied cbor data. + */ +typedef size_t (*FnStreamLength)(void *stream); + +/** + * @brief Reserve bytes within the stream. + * + * @param[in] stream the private data of the stream implementation + * @param[in] len the number of bytes to reserve + * + * @return len input is provided back on success, + * and NANOCBOR_ERR_END if not able to reserve + */ +typedef int (*FnStreamReserve)(void *stream, size_t len); + +/** + * @brief Copy the given cbor data into the stream. + * + * @param[in] stream the private data of the stream implementation + * @param[in] src pointer to the data to copy + * @param[in] n the number of bytes to copy + */ +typedef void (*FnStreamInsert)(void *stream, const void *src, size_t n); + +/** @} */ + +/** + * @brief encoder context + */ +typedef struct nanocbor_encoder { + FnStreamLength len; /**< Length in bytes of supplied cbor data. */ + FnStreamReserve reserve; /**< Allocate/ensure the next 'len' bytes can fit */ + FnStreamInsert insert; /**< Insert cbor data into the stream */ + void *stream; /**< The private data to give back to stream functions */ + +} nanocbor_encoder_t; + +/** + * @name decoder flags + * @{ + */ + +/** + * @brief decoder value is inside a container + */ +#define NANOCBOR_DECODER_FLAG_CONTAINER (0x01U) + +/** + * @brief decoder value is inside an indefinite length container + */ +#define NANOCBOR_DECODER_FLAG_INDEFINITE (0x02U) +/** @} */ + +/** + * @name NanoCBOR parser functions + * @{ + */ + +/** + * @brief Initialize a decoder context decoding the CBOR structure from @p buf + * with @p len bytes + * + * The decoder will attempt to decode CBOR types until the buffer is exhausted + * + * @param[in] value decoder value context + * @param[in] buf Buffer to decode from + * @param[in] len Length in bytes of the buffer + */ +void nanocbor_decoder_init(nanocbor_value_t *value, + const uint_least8_t *buf, size_t len); + +/** + * @brief Retrieve the type of the CBOR value at the current position + * + * @param[in] value decoder value context + * + * @return major type + * @return NANOCBOR_ERR_END if the buffer is exhausted + */ +int nanocbor_get_type(const nanocbor_value_t *value); + +/** + * @brief Check if the current buffer or container is exhausted + * + * @param[in] it decoder value context + * + * @return true if it is exhausted + * @return false if there are more items + */ +bool nanocbor_at_end(const nanocbor_value_t *it); + +/** + * @brief Retrieve a positive integer as uint_least8_t from the stream + * + * If the value at `cvalue` is greater than 8 bit (> 255), error is returned. + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] value returned positive integer + * + * @return number of bytes read + * @return negative on error + */ +int nanocbor_get_uint8(nanocbor_value_t *cvalue, uint_least8_t *value); + +/** + * @brief Retrieve a positive integer as uint16_t from the stream + * + * If the value at `cvalue` is greater than 16 bit (> 65535), error is returned. + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] value returned positive integer + * + * @return number of bytes read + * @return negative on error + */ +int nanocbor_get_uint16(nanocbor_value_t *cvalue, uint16_t *value); + +/** + * @brief Retrieve a positive integer as uint32_t from the stream + * + * If the value at `cvalue` is greater than 32 bit, error is returned. + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] value returned positive integer + * + * @return number of bytes read + * @return negative on error + */ +int nanocbor_get_uint32(nanocbor_value_t *cvalue, uint32_t *value); + +/** + * @brief Retrieve a signed integer as int_least8_t from the stream + * + * If the value at `cvalue` is greater than 8 bit (< -128 or > 127), + * error is returned. + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] value returned signed integer + * + * @return number of bytes read + * @return negative on error + */ +int nanocbor_get_int8(nanocbor_value_t *cvalue, int_least8_t *value); + +/** + * @brief Retrieve a signed integer as int16_t from the stream + * + * If the value at `cvalue` is greater than 16 bit (< -32768 or > 32767), + * error is returned. + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] value returned signed integer + * + * @return number of bytes read + * @return negative on error + */ +int nanocbor_get_int16(nanocbor_value_t *cvalue, int16_t *value); + +/** + * @brief Retrieve a signed integer as int32_t from the stream + * + * If the value at `cvalue` is greater than 32 bit, error is returned. + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] value returned signed integer + * + * @return number of bytes read + * @return negative on error + */ +int nanocbor_get_int32(nanocbor_value_t *cvalue, int32_t *value); + +/** + * @brief Retrieve a decimal fraction from the stream as a int32_t mantisa and + * int32_t exponent + * + * If the value at `cvalue` is greater than 32 bit, error is returned. + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] m returned mantisa + * @param[out] e returned exponent + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_get_decimal_frac(nanocbor_value_t *cvalue, int32_t *e, int32_t *m); + +/** + * @brief Retrieve a byte string from the stream + * + * The resulting @p buf and @p len are undefined if the result is an error + * condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] buf pointer to the byte string + * @param[out] len length of the byte string + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_get_bstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len); + +/** + * @brief Retrieve a text string from the stream + * + * The resulting @p buf and @p len are undefined if the result is an error + * condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] buf pointer to the text string + * @param[out] len length of the text string + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_get_tstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len); + +/** + * @brief Search for a tstr key in a map. + * + * The resulting @p value is undefined if @p key was not found. + * + * @pre @p start is inside a map + * + * @param[in] start pointer to the map to search + * @param[in] key pointer to the text string key + * @param[out] value pointer to the tstr value containing @p key if found + * + * @return NANOCBOR_OK if @p key was found + * @return negative on error / not found + */ +int nanocbor_get_key_tstr(nanocbor_value_t *start, const char *key, + nanocbor_value_t *value); + +/** + * @brief Enter a array type + * + * @param[in] it CBOR value to decode from + * @param[out] array CBOR value to decode the array members with + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_enter_array(const nanocbor_value_t *it, nanocbor_value_t *array); + +/** + * @brief Enter a map type + * + * @param[in] it CBOR value to decode from + * @param[out] map CBOR value to decode the map members with + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_enter_map(const nanocbor_value_t *it, nanocbor_value_t *map); + +/** + * @brief leave the container + * + * This must be called with the same @ref nanocbor_value_t struct that was used + * to enter the container. Furthermore, the @p container must be at the end of + * the container. + * + * @param[in] it parent CBOR structure + * @param[in] container exhausted CBOR container + */ +void nanocbor_leave_container(nanocbor_value_t *it, nanocbor_value_t *container); + +/** + * @brief Retrieve a tag as positive uint32_t from the stream + * + * The resulting @p value is undefined if the result is an error condition + * + * @param[in] cvalue CBOR value to decode from + * @param[out] tag returned tag as positive integer + * + * @return NANOCBOR_OK on success + */ +int nanocbor_get_tag(nanocbor_value_t *cvalue, uint32_t *tag); + +/** + * @brief Retrieve a null value from the stream + * + * This function checks if the next CBOR value is a NULL value and advances to + * the next value if no error is detected + * + * @param[in] cvalue CBOR value to decode from + * + * @return NANOCBOR_OK on success + */ +int nanocbor_get_null(nanocbor_value_t *cvalue); + +/** + * @brief Retrieve a boolean value from the stream + * + * @param[in] cvalue CBOR value to decode from + * @param[out] value Boolean value retrieved from the stream + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_get_bool(nanocbor_value_t *cvalue, bool *value); + +/** + * @brief Skip to the next value in the CBOR stream + * + * This function is able to skip over nested structures in the CBOR stream + * such as (nested) arrays and maps. It uses limited recursion to do so. + * + * Recursion is limited with @ref NANOCBOR_RECURSION_MAX + * + * @param[in] it CBOR stream to skip a value from + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_skip(nanocbor_value_t *it); + +/** + * @brief Skip a single simple value in the CBOR stream + * + * This is a cheaper version of @ref nanocbor_skip, the downside is that this + * function is unable to skip nested structures. + * + * @param[in] it CBOR value to skip + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_skip_simple(nanocbor_value_t *it); + +/** + * @brief Retrieve part of the CBOR stream for separate parsing + * + * This function retrieves the pointer and length of a single CBOR item. This + * item can be stored for later processing. + * + * @param[in] it CBOR value to retrieve + * @param[out] start start of the CBOR item + * @param[out] len length of the CBOR item + * + * @return NANOCBOR_OK on success + * @return negative on error + */ +int nanocbor_get_subcbor(nanocbor_value_t *it, const uint_least8_t **start, + size_t *len); + +/** + * @brief Retrieve the number of remaining values is a CBOR container + * + * The returned value is undefined when not inside a container or when the + * container is of indefinite length. For a map, the number is the full number + * of CBOR items remaining (twice the number of key/value pairs). + * + * @param[in] value value inside a CBOR container + * + * @return number of items remaining + */ +static inline uint32_t nanocbor_container_remaining(const nanocbor_value_t *value) +{ + return value->remaining; +} + +/** + * @brief Check whether a container is an indefinite-length container + * + * @param[in] container value inside a CBOR container + * + * @return True when the container is indefinite in length + * @return False when not indefinite-length or not in a + * container + */ +static inline bool nanocbor_container_indefinite(const nanocbor_value_t *container) +{ + return (container->flags == + (NANOCBOR_DECODER_FLAG_INDEFINITE | NANOCBOR_DECODER_FLAG_CONTAINER)); +} + +static inline bool nanocbor_in_container(const nanocbor_value_t *container) +{ + return container->flags & (NANOCBOR_DECODER_FLAG_CONTAINER); +} + +/** @} */ + +/** + * @name NanoCBOR encoder functions + * @{ + */ + +/** + * @brief Declare a nanocbor_encoder_t + */ +#define NANOCBOR_ENCODER(priv_data, lenfn, resfn, insfn) \ + (const nanocbor_encoder_t) { \ + .len = lenfn, \ + .reserve = resfn, \ + .insert = insfn, \ + .stream = priv_data, \ + } + +/** + * @brief Retrieve the encoded length of the CBOR structure + * + * This function doesn't take the length of the stream into account, + * it only returns the number of bytes the current CBOR structure would take up. + * + * @param[in] enc Encoder context + * + * @return Length of the encoded structure + */ +size_t nanocbor_encoded_len(nanocbor_encoder_t *enc); + +/** + * @brief Write a CBOR boolean value into a buffer + * + * @param[in] enc Encoder context + * @param[in] content Boolean value to write + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_bool(nanocbor_encoder_t *enc, bool content); + +/** + * @brief Write an unsigned integer of at most sizeof uint64_t into the buffer + * + * @param[in] enc Encoder context + * @param[in] num unsigned integer to write + * + * @return number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_uint(nanocbor_encoder_t *enc, uint64_t num); + +/** + * @brief Write a CBOR tag of at most sizeof uint64_t into the buffer + * + * @param[in] enc Encoder context + * @param[in] num tag value to write into the buffer + * + * @return number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_tag(nanocbor_encoder_t *enc, uint64_t num); + +/** + * @brief Write a CBOR Object tag (27) to the buffer + * + * @param[in] enc Encoder context + * @param[in] num_params Number of parameters needed to construct this object. + * + * @return number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_object(nanocbor_encoder_t *enc, size_t num_params); + +/** + * @brief Write a signed integer of at most sizeof int32_t into the buffer + * + * If it is not certain if the data is signed, use this function. + * + * @param[in] enc Encoder context + * @param[in] num unsigned integer to write + * + * @return number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_int(nanocbor_encoder_t *enc, int64_t num); + +/** + * @brief Write a byte string indicator for a byte string with specific length + * into the encoder buffer + * + * This doesn't write any byte string into the encoder buffer, only the type + * and length indicator for the byte string + * + * @param[in] enc Encoder context + * @param[in] len Length of the byte string + * + * @return number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_bstr(nanocbor_encoder_t *enc, size_t len); + +/** + * @brief Write a text string indicator for a string with specific length + * into the encoder buffer + * + * This doesn't write any text string into the encoder buffer, only the type + * and length indicator for the text string + * + * @param[in] enc Encoder context + * @param[in] len Length of the text string + * + * @return number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_tstr(nanocbor_encoder_t *enc, size_t len); + +/** + * @brief Copy a byte string with indicator into the encoder buffer + * + * @param[in] enc Encoder context + * @param[in] str byte string to encode + * @param[in] len Length of the string + * + * @return NANOCBOR_OK if the string fits + * @return Negative on error + */ +int nanocbor_put_bstr(nanocbor_encoder_t *enc, const uint_least8_t *str, size_t len); + +/** + * @brief Copy a text string with indicator into the encoder buffer + * + * @param[in] enc Encoder context + * @param[in] str null terminated text string to encode + * + * @return NANOCBOR_OK if the string fits + * @return Negative on error + */ +int nanocbor_put_tstr(nanocbor_encoder_t *enc, const char *str); + +/** + * @brief Copy n bytes of a text string with indicator into the encoder buffer + * + * @param[in] enc Encoder context + * @param[in] str text string to encode + * @param[in] len number of string bytes to copy + * + * @return NANOCBOR_OK if the string fits + * @return Negative on error + */ +int nanocbor_put_tstrn(nanocbor_encoder_t *enc, const char *str, size_t len); + +/** + * @brief Write an array indicator with @p len items + * + * It is assumed that the calling code will encode @p len items after calling + * this function. The array automatically terminates after @p len items are + * added, no function to close the container is necessary. + * + * @param[in] enc Encoder context + * @param[in] len Number of items in the array + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_array(nanocbor_encoder_t *enc, size_t len); + +/** + * @brief Write a map indicator with @p len pairs + * + * It is assumed that the calling code will encode @p len item pairs after + * calling this function. The array automatically terminates after @p len item + * pairs are added, no function to close the container is necessary. + * + * @param[in] enc Encoder context + * @param[in] len Number of pairs in the map + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_map(nanocbor_encoder_t *enc, size_t len); + +/** + * @brief Write an indefinite-length array indicator + * + * @param[in] enc Encoder context + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_array_indefinite(nanocbor_encoder_t *enc); + +/** + * @brief Write an indefinite-length map indicator + * + * @param[in] enc Encoder context + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_map_indefinite(nanocbor_encoder_t *enc); + +/** + * @brief Write a stop code for indefinite length containers + * + * @param[in] enc Encoder context + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_end_indefinite(nanocbor_encoder_t *enc); + +/** + * @brief Write a Null value into the encoder buffer + * + * @param[in] enc Encoder context + * + * @return NANOCBOR_OK + * @return Negative on error + */ +int nanocbor_fmt_null(nanocbor_encoder_t *enc); + +/** + * @brief Write a float value into the encoder buffer + * + * @param[in] enc Encoder context + * @param[in] num Floating point to encode + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_float(nanocbor_encoder_t *enc, float num); + +/** + * @brief Write a double floating point value into the encoder buffer + * + * @param[in] enc Encoder context + * @param[in] num Floating point to encode + * + * @return Number of bytes written + * @return Negative on error + */ +int nanocbor_fmt_double(nanocbor_encoder_t *enc, double num); + +/** + * @brief Write a decimal fraction into the encoder buffer + * + * @param[in] enc Encoder context + * @param[in] m Mantisa + * @param[in] e Exponent + * + * @return Number of bytes written + */ +int nanocbor_fmt_decimal_frac(nanocbor_encoder_t *enc, int32_t e, int32_t m); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NANOCBOR_H */ +/** @} */ diff --git a/include/nanocbor/stream_encoders/memory_buffer.h b/include/nanocbor/stream_encoders/memory_buffer.h new file mode 100644 index 0000000..c0535ae --- /dev/null +++ b/include/nanocbor/stream_encoders/memory_buffer.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +#ifndef ENCODER_MEMORY_BUFFER_H +#define ENCODER_MEMORY_BUFFER_H + +#include +#include + +typedef struct memory_encoder { + uint_least8_t *cur; /**< Current position in the buffer */ + uint_least8_t *end; /**< end of the buffer */ + size_t len; /**< Length in bytes of supplied cbor data. Incremented + * separate from the buffer check */ +} memory_encoder; + +void MemoryStream_Init(memory_encoder *self, uint_least8_t *buf, size_t len); +size_t MemoryStream_Length(memory_encoder *self); +int MemoryStream_Reserve(memory_encoder *self, size_t len); +void MemoryStream_Insert(memory_encoder *self, const void *src, size_t n); + +#endif diff --git a/src/decoder.c b/src/decoder.c new file mode 100644 index 0000000..e578d55 --- /dev/null +++ b/src/decoder.c @@ -0,0 +1,455 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +/** + * @ingroup nanocbor + * @{ + * @file + * @brief Minimalistic CBOR decoder implementation + * + * @author Koen Zandberg + * @} + */ + +#include +#include +#include +#include + +#include "nanocbor/config.h" +#include "nanocbor/nanocbor.h" + +#include NANOCBOR_BYTEORDER_HEADER + + +void nanocbor_decoder_init(nanocbor_value_t *value, + const uint_least8_t *buf, size_t len) +{ + value->cur = buf; + value->end = buf + len; + value->flags = 0; +} + +static void _advance(nanocbor_value_t *cvalue, unsigned int res) +{ + cvalue->cur += res; + cvalue->remaining--; +} + +static int _advance_if(nanocbor_value_t *cvalue, int res) +{ + if (res > 0) { + _advance(cvalue, (unsigned int)res); + } + return res; +} + +static inline bool _over_end(const nanocbor_value_t *it) +{ + return it->cur >= it->end; +} + +static inline uint_least8_t _get_type(const nanocbor_value_t *value) +{ + return (*value->cur & NANOCBOR_TYPE_MASK); +} + +static int _value_match_exact(nanocbor_value_t *cvalue, uint_least8_t val) +{ + int res = NANOCBOR_ERR_INVALID_TYPE; + + if (_over_end(cvalue)) { + res = NANOCBOR_ERR_END; + } + else if (*cvalue->cur == val) { + _advance(cvalue, 1U); + res = NANOCBOR_OK; + } + return res; +} + +bool nanocbor_at_end(const nanocbor_value_t *it) +{ + bool end = false; + /* The container is at the end when */ + if (_over_end(it) || /* Number of items exhausted */ + /* Indefinite container and the current item is the end marker */ + ((nanocbor_container_indefinite(it) && + *it->cur == (NANOCBOR_TYPE_FLOAT << NANOCBOR_TYPE_OFFSET | NANOCBOR_VALUE_MASK))) || + /* Or the remaining number of items is zero */ + (!nanocbor_container_indefinite(it) && nanocbor_in_container(it) && it->remaining == 0) + ) { + end = true; + } + return end; +} + +int nanocbor_get_type(const nanocbor_value_t *value) +{ + if (nanocbor_at_end(value)) { + return NANOCBOR_ERR_END; + } + return (_get_type(value) >> NANOCBOR_TYPE_OFFSET); +} + +static int _get_uint64(const nanocbor_value_t *cvalue, uint32_t *value, uint_least8_t max, int type) +{ + int ctype = nanocbor_get_type(cvalue); + + if (ctype < 0) { + return ctype; + } + + if (type != ctype) { + return NANOCBOR_ERR_INVALID_TYPE; + } + unsigned bytelen = *cvalue->cur & NANOCBOR_VALUE_MASK; + + if (bytelen < NANOCBOR_SIZE_BYTE) { + *value = bytelen; + /* Ptr should advance 1 pos */ + return 1; + } + if (bytelen > max) { + return NANOCBOR_ERR_OVERFLOW; + } + + unsigned bytes = 1U << (bytelen - NANOCBOR_SIZE_BYTE); + + if ((cvalue->cur + bytes) >= cvalue->end) { + return NANOCBOR_ERR_END; + } + uint64_t tmp = 0; + uint_fast64_t val64; + /* Copy the value from cbor to the least significant bytes */ + /* for-loop to handle excotic cpu arthitectures, eg. no 8-bit support*/ + for (uint_fast8_t i = 0; i < bytes; i++) { + val64 = 0x00000000000000ffull & *((uint_fast8_t*)(cvalue->cur + 1 + i)); + tmp |= ( val64<<(8 * (bytes - i - 1)) ); + } + *value = tmp; + + return (int)(1 + bytes); +} + +static int _get_and_advance_uint8(nanocbor_value_t *cvalue, uint_least8_t *value, + int type) +{ + uint32_t tmp = 0; + int res = _get_uint64(cvalue, &tmp, NANOCBOR_SIZE_BYTE, + type); + *value = (uint_least8_t)tmp; + + return _advance_if(cvalue, res); +} + +static int _get_and_advance_uint16(nanocbor_value_t *cvalue, uint16_t *value, + int type) +{ + uint32_t tmp = 0; + int res = _get_uint64(cvalue, &tmp, NANOCBOR_SIZE_SHORT, + type); + *value = (uint16_t)tmp; + + return _advance_if(cvalue, res); +} + +static int _get_and_advance_uint32(nanocbor_value_t *cvalue, uint32_t *value, + int type) +{ + uint32_t tmp = 0; + int res = _get_uint64(cvalue, &tmp, NANOCBOR_SIZE_WORD, + type); + *value = tmp; + + return _advance_if(cvalue, res); +} + +int nanocbor_get_uint8(nanocbor_value_t *cvalue, uint_least8_t *value) +{ + return _get_and_advance_uint8(cvalue, value, NANOCBOR_TYPE_UINT); +} + +int nanocbor_get_uint16(nanocbor_value_t *cvalue, uint16_t *value) +{ + return _get_and_advance_uint16(cvalue, value, NANOCBOR_TYPE_UINT); +} + +int nanocbor_get_uint32(nanocbor_value_t *cvalue, uint32_t *value) +{ + return _get_and_advance_uint32(cvalue, value, NANOCBOR_TYPE_UINT); +} + +static int _get_and_advance_int32(nanocbor_value_t *cvalue, int32_t *value, uint_least8_t max, + uint32_t bound) +{ + int type = nanocbor_get_type(cvalue); + if (type < 0) { + return type; + } + int res = NANOCBOR_ERR_INVALID_TYPE; + if (type == NANOCBOR_TYPE_NINT || type == NANOCBOR_TYPE_UINT) { + uint32_t intermediate = 0; + res = _get_uint64(cvalue, &intermediate, max, type); + if (intermediate > bound) { + res = NANOCBOR_ERR_OVERFLOW; + } + if (type == NANOCBOR_TYPE_NINT) { + *value = (-(int32_t)intermediate) - 1; + } + else { + *value = (int32_t)intermediate; + } + } + return _advance_if(cvalue, res); +} + +int nanocbor_get_int8(nanocbor_value_t *cvalue, int_least8_t *value) +{ +#define C2000_INT8_MAX 127 // C2000 does not define INT8_MAX, because it doesnt implement int8_t + int32_t tmp = 0; + int res = _get_and_advance_int32(cvalue, &tmp, NANOCBOR_SIZE_BYTE, C2000_INT8_MAX); + + *value = (int_least8_t)tmp; + + return res; +} + +int nanocbor_get_int16(nanocbor_value_t *cvalue, int16_t *value) +{ + int32_t tmp = 0; + int res = _get_and_advance_int32(cvalue, &tmp, NANOCBOR_SIZE_SHORT, INT16_MAX); + + *value = (int16_t)tmp; + + return res; +} + +int nanocbor_get_int32(nanocbor_value_t *cvalue, int32_t *value) +{ + return _get_and_advance_int32(cvalue, value, NANOCBOR_SIZE_WORD, INT32_MAX); +} + +int nanocbor_get_tag(nanocbor_value_t *cvalue, uint32_t *tag) +{ + int res = _get_uint64(cvalue, tag, NANOCBOR_SIZE_WORD, NANOCBOR_TYPE_TAG); + + if (res >= 0) { + cvalue->cur += res; + res = NANOCBOR_OK; + } + + return res; +} + +int nanocbor_get_decimal_frac(nanocbor_value_t *cvalue, int32_t *e, int32_t *m) +{ + int res = NANOCBOR_NOT_FOUND; + uint32_t tag = UINT32_MAX; + if(nanocbor_get_tag(cvalue, &tag) == NANOCBOR_OK) { + if (tag == NANOCBOR_TAG_DEC_FRAC) { + nanocbor_value_t arr; + if (nanocbor_enter_array(cvalue, &arr) == NANOCBOR_OK) { + res = nanocbor_get_int32(&arr, e); + if (res >= 0) { + res = nanocbor_get_int32(&arr, m); + if (res >= 0) { + res = NANOCBOR_OK; + } + } + nanocbor_leave_container(cvalue, &arr); + } + } + } + + return res; +} + +static int _get_str(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len, uint_least8_t type) +{ + *len = 0; + int res = _get_uint64(cvalue, (uint32_t*)len, NANOCBOR_SIZE_SIZET, type); + + if (cvalue->end - cvalue->cur < 0 || (size_t)(cvalue->end - cvalue->cur) < *len) { + return NANOCBOR_ERR_END; + } + if (res >= 0) { + *buf = (cvalue->cur) + res; + _advance(cvalue, (unsigned int)((size_t)res + *len)); + res = NANOCBOR_OK; + } + return res; +} + +int nanocbor_get_bstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len) +{ + return _get_str(cvalue, buf, len, NANOCBOR_TYPE_BSTR); +} + +int nanocbor_get_tstr(nanocbor_value_t *cvalue, const uint_least8_t **buf, size_t *len) +{ + return _get_str(cvalue, buf, len, NANOCBOR_TYPE_TSTR); +} + +int nanocbor_get_null(nanocbor_value_t *cvalue) +{ + return _value_match_exact(cvalue, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_NULL); +} + +int nanocbor_get_bool(nanocbor_value_t *cvalue, bool *value) +{ + *value = false; + int res = _value_match_exact(cvalue, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_FALSE); + if (res < 0) { + *value = true; + res = _value_match_exact(cvalue, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_TRUE); + } + return res; +} + +static int _enter_container(const nanocbor_value_t *it, nanocbor_value_t *container, + uint_least8_t type) +{ + container->end = it->end; + container->remaining = 0; + + uint_least8_t value_match = (uint_least8_t)(((unsigned)type << NANOCBOR_TYPE_OFFSET) | NANOCBOR_SIZE_INDEFINITE); + + /* Not using _value_match_exact here to keep *it const */ + if (!_over_end(it) && *it->cur == value_match) { + container->flags = NANOCBOR_DECODER_FLAG_INDEFINITE | + NANOCBOR_DECODER_FLAG_CONTAINER; + container->cur = it->cur + 1; + return NANOCBOR_OK; + } + + int res = _get_uint64(it, &container->remaining, + NANOCBOR_SIZE_WORD, type); + if (res < 0) { + return res; + } + container->flags = NANOCBOR_DECODER_FLAG_CONTAINER; + container->cur = it->cur + res; + return NANOCBOR_OK; +} + +int nanocbor_enter_array(const nanocbor_value_t *it, nanocbor_value_t *array) +{ + return _enter_container(it, array, NANOCBOR_TYPE_ARR); +} + +int nanocbor_enter_map(const nanocbor_value_t *it, nanocbor_value_t *map) +{ + int res = _enter_container(it, map, NANOCBOR_TYPE_MAP); + + if (map->remaining > UINT32_MAX / 2) { + return NANOCBOR_ERR_OVERFLOW; + } + map->remaining = map->remaining * 2; + return res; +} + +void nanocbor_leave_container(nanocbor_value_t *it, nanocbor_value_t *container) +{ + if (it->remaining) { + it->remaining--; + } + if (nanocbor_container_indefinite(container)) { + it->cur = container->cur + 1; + } + else { + it->cur = container->cur; + } +} + +static int _skip_simple(nanocbor_value_t *it) +{ + uint64_t tmp = 0; + int res = _get_uint64(it, (uint32_t*)&tmp, NANOCBOR_SIZE_LONG, + nanocbor_get_type(it)); + return _advance_if(it, res); +} + +int nanocbor_get_subcbor(nanocbor_value_t *it, const uint_least8_t **start, + size_t *len) +{ + *start = it->cur; + int res = nanocbor_skip(it); + *len = (size_t)(it->cur - *start); + return res; +} + +int nanocbor_skip_simple(nanocbor_value_t *it) +{ + return _skip_simple(it); +} + +/* NOLINTNEXTLINE(misc-no-recursion): Recursion is limited by design */ +static int _skip_limited(nanocbor_value_t *it, uint_least8_t limit) +{ + if (limit == 0) { + return NANOCBOR_ERR_RECURSION; + } + int type = nanocbor_get_type(it); + int res = type; + + if (type == NANOCBOR_TYPE_BSTR || type == NANOCBOR_TYPE_TSTR) { + const uint_least8_t *tmp = NULL; + size_t len = 0; + res = _get_str(it, &tmp, &len, (uint_least8_t)type); + } + /* map or array */ + else if (type == NANOCBOR_TYPE_ARR || type == NANOCBOR_TYPE_MAP) { + nanocbor_value_t recurse; + res = (type == NANOCBOR_TYPE_MAP + ? nanocbor_enter_map(it, &recurse) + : nanocbor_enter_array(it, &recurse)); + if (res == NANOCBOR_OK) { + while (!nanocbor_at_end(&recurse)) { + res = _skip_limited(&recurse, limit - 1); + if (res < 0) { + break; + } + } + nanocbor_leave_container(it, &recurse); + } + } + else if (type >= 0) { + res = _skip_simple(it); + } + return res < 0 ? res : NANOCBOR_OK; +} + +int nanocbor_skip(nanocbor_value_t *it) +{ + return _skip_limited(it, NANOCBOR_RECURSION_MAX); +} + +int nanocbor_get_key_tstr(nanocbor_value_t *start, const char *key, + nanocbor_value_t *value) +{ + int res = NANOCBOR_NOT_FOUND; + size_t len = strlen(key); + *value = *start; + + while (!nanocbor_at_end(value)) { + const uint_least8_t *s = NULL; + size_t s_len = 0; + + if ((res = nanocbor_get_tstr(value, &s, &s_len)) < 0) { + break; + } + + if (s_len == len && !strncmp(key, (const char *)s, len)) { + res = NANOCBOR_OK; + break; + } + + if ((res = nanocbor_skip(value)) < 0) { + break; + } + } + + return res; +} diff --git a/src/encoder.c b/src/encoder.c new file mode 100644 index 0000000..d63fcd5 --- /dev/null +++ b/src/encoder.c @@ -0,0 +1,382 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +/** + * @ingroup nanocbor + * @{ + * @file + * @brief Minimalistic CBOR encoder implementation + * + * @author Koen Zandberg + * @} + */ + +#include +#include +#include +#include + +#include "nanocbor/config.h" +#include "nanocbor/nanocbor.h" + +#include NANOCBOR_BYTEORDER_HEADER + +size_t nanocbor_encoded_len(nanocbor_encoder_t *enc) +{ + return enc->len(enc->stream); +} + +static int _fmt_single(nanocbor_encoder_t *enc, uint_least8_t single) +{ + const int res = enc->reserve(enc->stream, 1); + + if (res == 1) { + enc->insert(enc->stream, &single, 1); + } + return res; +} + +int nanocbor_fmt_bool(nanocbor_encoder_t *enc, bool content) +{ + uint_least8_t single = NANOCBOR_MASK_FLOAT | (content ? NANOCBOR_SIMPLE_TRUE + : NANOCBOR_SIMPLE_FALSE); + + return _fmt_single(enc, single); +} + +static int _fmt_uint64(nanocbor_encoder_t *enc, uint64_t num, uint_least8_t type) +{ + unsigned extrabytes = 0; + uint_least8_t buf[8]; + + if (num < NANOCBOR_SIZE_BYTE) { + type |= num; + } else { + if (num > UINT32_MAX) { + /* Requires long size */ + type |= NANOCBOR_SIZE_LONG; + /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ + extrabytes = 8; + buf[0] = (num & 0xff00000000000000ull) >> 56; + buf[1] = (num & 0x00ff000000000000ull) >> 48; + buf[2] = (num & 0x0000ff0000000000ull) >> 40; + buf[3] = (num & 0x000000ff00000000ull) >> 32; + buf[4] = (num & 0x00000000ff000000ull) >> 24; + buf[5] = (num & 0x0000000000ff0000ull) >> 16; + buf[6] = (num & 0x000000000000ff00ull) >> 8; + buf[7] = (num & 0x00000000000000ffull); + } else if (num > UINT16_MAX) { + /* At least word size */ + type |= NANOCBOR_SIZE_WORD; + /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ + extrabytes = 4; + buf[0] = (num & 0x00000000ff000000ull) >> 24; + buf[1] = (num & 0x0000000000ff0000ull) >> 16; + buf[2] = (num & 0x000000000000ff00ull) >> 8; + buf[3] = (num & 0x00000000000000ffull); + } else if (num > 255/* UINT8_MAX - C2000 does not define this*/) { + type |= NANOCBOR_SIZE_SHORT; + /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ + extrabytes = 2; + buf[0] = (num & 0x000000000000ff00ull) >> 8; + buf[1] = (num & 0x00000000000000ffull); + } else { + type |= NANOCBOR_SIZE_BYTE; + /* The exact size is known using sizeof() breaks arches that aren't 8-bit words */ + extrabytes = 1; + buf[0] = (num & 0x00000000000000ffull); + } + } + + int res = enc->reserve(enc->stream, extrabytes + 1); + + if (res > 0) { + enc->insert(enc->stream, &type, 1); + enc->insert(enc->stream, buf, extrabytes); + } + + return res; +} + +int nanocbor_fmt_uint(nanocbor_encoder_t *enc, uint64_t num) +{ + return _fmt_uint64(enc, num, NANOCBOR_MASK_UINT); +} + +int nanocbor_fmt_tag(nanocbor_encoder_t *enc, uint64_t num) +{ + return _fmt_uint64(enc, num, NANOCBOR_MASK_TAG); +} + +int nanocbor_fmt_object(nanocbor_encoder_t *enc, size_t num_params) +{ + int array_res = 0; + int tag_res = nanocbor_fmt_tag(enc, NANOCBOR_TAG_OBJECT); + + if (tag_res < 0) { + return tag_res; + } + + array_res = nanocbor_fmt_array(enc, num_params); + + if (array_res < 0) { + return array_res; + } + + return tag_res + array_res; +} + +int nanocbor_fmt_int(nanocbor_encoder_t *enc, int64_t num) +{ + if (num < 0) { + /* Always negative at this point */ + num = -(num + 1); + return _fmt_uint64(enc, (uint64_t)num, NANOCBOR_MASK_NINT); + } + return nanocbor_fmt_uint(enc, (uint64_t)num); +} + +int nanocbor_fmt_bstr(nanocbor_encoder_t *enc, size_t len) +{ + return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_BSTR); +} + +int nanocbor_fmt_tstr(nanocbor_encoder_t *enc, size_t len) +{ + return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_TSTR); +} + +static int _put_bytes(nanocbor_encoder_t *enc, const uint_least8_t *str, size_t len) +{ + const int res = enc->reserve(enc->stream, len); + + if (res >= 0) { + enc->insert(enc->stream, str, len); + return NANOCBOR_OK; + } + return res; +} + +int nanocbor_put_tstr(nanocbor_encoder_t *enc, const char *str) +{ + size_t len = strlen(str); + + nanocbor_fmt_tstr(enc, len); + return _put_bytes(enc, (const uint_least8_t *)str, len); +} + +int nanocbor_put_tstrn(nanocbor_encoder_t *enc, const char *str, size_t len) +{ + nanocbor_fmt_tstr(enc, len); + return _put_bytes(enc, (const uint_least8_t *)str, len); +} + +int nanocbor_put_bstr(nanocbor_encoder_t *enc, const uint_least8_t *str, size_t len) +{ + nanocbor_fmt_bstr(enc, len); + return _put_bytes(enc, str, len); +} + +int nanocbor_fmt_array(nanocbor_encoder_t *enc, size_t len) +{ + return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_ARR); +} + +int nanocbor_fmt_map(nanocbor_encoder_t *enc, size_t len) +{ + return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_MAP); +} + +int nanocbor_fmt_array_indefinite(nanocbor_encoder_t *enc) +{ + return _fmt_single(enc, NANOCBOR_MASK_ARR | NANOCBOR_SIZE_INDEFINITE); +} + +int nanocbor_fmt_map_indefinite(nanocbor_encoder_t *enc) +{ + return _fmt_single(enc, NANOCBOR_MASK_MAP | NANOCBOR_SIZE_INDEFINITE); +} + +int nanocbor_fmt_end_indefinite(nanocbor_encoder_t *enc) +{ + /* End is marked with float major and indefinite minor number */ + return _fmt_single(enc, NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_INDEFINITE); +} + +int nanocbor_fmt_null(nanocbor_encoder_t *enc) +{ + return _fmt_single(enc, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_NULL); +} + +/* Double bit mask related defines */ +#define DOUBLE_EXP_OFFSET (1023U) +#define DOUBLE_SIZE (64U) +#define DOUBLE_EXP_POS (52U) +#define DOUBLE_SIGN_POS (63U) +#define DOUBLE_EXP_MASK ((uint64_t)0x7FFU) +#define DOUBLE_SIGN_MASK ((uint64_t)1U << DOUBLE_SIGN_POS) +#define DOUBLE_EXP_IS_NAN (0x7FFU) +#define DOUBLE_IS_ZERO (~(DOUBLE_SIGN_MASK)) +#define DOUBLE_FLOAT_LOSS (0x1FFFFFFFU) + +/* float bit mask related defines */ +#define FLOAT_EXP_OFFSET (127U) +#define FLOAT_SIZE (32U) +#define FLOAT_EXP_POS (23U) +#define FLOAT_EXP_MASK ((uint32_t)0xFFU) +#define FLOAT_SIGN_POS (31U) +#define FLOAT_FRAC_MASK (0x7FFFFFU) +#define FLOAT_SIGN_MASK ((uint32_t)1U << FLOAT_SIGN_POS) +#define FLOAT_EXP_IS_NAN (0xFFU) +#define FLOAT_IS_ZERO (~(FLOAT_SIGN_MASK)) +/* Part where a float to halffloat leads to precision loss */ +#define FLOAT_HALF_LOSS (0x1FFFU) + +/* halffloat bit mask related defines */ +#define HALF_EXP_OFFSET (15U) +#define HALF_SIZE (16U) +#define HALF_EXP_POS (10U) +#define HALF_EXP_MASK (0x1FU) +#define HALF_SIGN_POS (15U) +#define HALF_FRAC_MASK (0x3FFU) +#define HALF_SIGN_MASK ((uint16_t)(1U << HALF_SIGN_POS)) +#define HALF_MASK_HALF (0xFFU) + +/* Check special cases for single precision floats */ +static bool _single_is_inf_nan(uint_least8_t exp) +{ + return exp == FLOAT_EXP_IS_NAN; +} + +static bool _single_is_zero(uint32_t num) +{ + return (num & FLOAT_IS_ZERO) == 0; +} + +static bool _single_in_range(uint_least8_t exp, uint32_t num) +{ + /* Check if lower 13 bits of fraction are zero, if so we might be able to + * convert without precision loss */ + if (exp <= (HALF_EXP_OFFSET + FLOAT_EXP_OFFSET) && + exp >= ((-HALF_EXP_OFFSET + 1) + FLOAT_EXP_OFFSET) && + ((num & FLOAT_HALF_LOSS) == 0)) { + return true; + } + return false; +} + +static int _fmt_halffloat(nanocbor_encoder_t *enc, uint16_t half) +{ + int res = enc->reserve(enc->stream, sizeof(uint16_t) + 1); + if (res > 0) { + const uint_least8_t id = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_SHORT; + const uint_least8_t part1 = (half >> HALF_SIZE/2); + const uint_least8_t part2 = half & HALF_MASK_HALF; + + enc->insert(enc->stream, &id, 1); + enc->insert(enc->stream, &part1, 1); + enc->insert(enc->stream, &part2, 1); + + res = sizeof(uint16_t) + 1; + } + return res; +} + +/* Check special cases for single precision floats */ +static bool _double_is_inf_nan(uint16_t exp) +{ + return (exp == DOUBLE_EXP_IS_NAN); +} + +static bool _double_is_zero(uint64_t num) +{ + return (num & DOUBLE_IS_ZERO) == 0; +} + +static bool _double_in_range(uint16_t exp, uint64_t num) +{ + /* Check if lower 13 bits of fraction are zero, if so we might be able to + * convert without precision loss */ + if (exp <= (DOUBLE_EXP_OFFSET + FLOAT_EXP_OFFSET) && + exp >= (DOUBLE_EXP_OFFSET - FLOAT_EXP_OFFSET + 1) && + ((num & DOUBLE_FLOAT_LOSS) == 0)) { /* First 29 bits must be zero */ + return true; + } + return false; +} + +int nanocbor_fmt_float(nanocbor_encoder_t *enc, float num) +{ + /* Allow bitwise access to float */ + uint32_t *unum = (uint32_t *)# + + /* Retrieve exponent */ + uint_least8_t exp = (*unum >> FLOAT_EXP_POS) & FLOAT_EXP_MASK; + if (_single_is_inf_nan(exp) || + _single_is_zero(*unum) || + _single_in_range(exp, *unum)) { + /* Copy sign bit */ + uint16_t half = ((*unum >> (FLOAT_SIZE - HALF_SIZE)) & HALF_SIGN_MASK); + /* Shift exponent */ + if (exp != FLOAT_EXP_IS_NAN && exp != 0) { + exp = exp + (uint_least8_t)(HALF_EXP_OFFSET - FLOAT_EXP_OFFSET); + } + /* Add exponent */ + half |= ((exp & HALF_EXP_MASK) << HALF_EXP_POS) | + ((*unum >> (FLOAT_EXP_POS - HALF_EXP_POS)) & HALF_FRAC_MASK); + return _fmt_halffloat(enc, half); + } + /* normal float */ + int res = enc->reserve(enc->stream, 1 + sizeof(float)); + if (res > 0) { + const uint_least8_t id = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_WORD; + + /* NOLINTNEXTLINE: user supplied function */ + const uint32_t bnum = NANOCBOR_HTOBE32_FUNC(*unum); + + enc->insert(enc->stream, &id, 1); + enc->insert(enc->stream, &bnum, sizeof(bnum)); + } + return res; +} + +int nanocbor_fmt_double(nanocbor_encoder_t *enc, double num) +{ + uint64_t *unum = (uint64_t *)# + uint16_t exp = (*unum >> DOUBLE_EXP_POS) & DOUBLE_EXP_MASK; + if (_double_is_inf_nan(exp) || + _double_is_zero(*unum) || + _double_in_range(exp, *unum)) { + /* copy sign bit over */ + uint32_t single = (*unum >> (DOUBLE_SIZE - FLOAT_SIZE)) & (FLOAT_SIGN_MASK); + /* Shift exponent */ + if (exp != DOUBLE_EXP_IS_NAN && exp != 0) { + exp = exp + FLOAT_EXP_OFFSET - DOUBLE_EXP_OFFSET; + } + single |= ((exp & FLOAT_EXP_MASK) << FLOAT_EXP_POS) | + ((*unum >> (DOUBLE_EXP_POS - FLOAT_EXP_POS)) & FLOAT_FRAC_MASK); + float *fsingle = (float*)&single; + return nanocbor_fmt_float(enc, *fsingle); + } + int res = enc->reserve(enc->stream, 1 + sizeof(double)); + if (res > 0) { + const uint_least8_t id = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_LONG; + + /* NOLINTNEXTLINE: user supplied function */ + uint64_t bnum = NANOCBOR_HTOBE64_FUNC(*unum); + + enc->insert(enc->stream, &id, 1); + enc->insert(enc->stream, &bnum, sizeof(bnum)); + } + return res; +} + +int nanocbor_fmt_decimal_frac(nanocbor_encoder_t *enc, int32_t e, int32_t m) +{ + int res = nanocbor_fmt_tag(enc, NANOCBOR_TAG_DEC_FRAC); + res += nanocbor_fmt_array(enc, 2); + res += nanocbor_fmt_int(enc, e); + res += nanocbor_fmt_int(enc, m); + return res; +} diff --git a/src/memory_buffer.c b/src/memory_buffer.c new file mode 100644 index 0000000..2f3e296 --- /dev/null +++ b/src/memory_buffer.c @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ +#include "nanocbor/nanocbor.h" +#include "nanocbor/stream_encoders/memory_buffer.h" +#include + +void MemoryStream_Init(memory_encoder *self, uint_least8_t *buf, size_t len) +{ + self->len = 0; + self->cur = buf; + self->end = buf + len; +} + +size_t MemoryStream_Length(memory_encoder *self) +{ + return self->len; +} + +int MemoryStream_Reserve(memory_encoder *self, size_t len) +{ + const int fits = ((size_t)(self->end - self->cur) >= len); + self->len += len; + return fits ? (int)len : NANOCBOR_ERR_END; +} + +void MemoryStream_Insert(memory_encoder *self, const void *src, size_t n) +{ + memcpy(self->cur, src, n); + self->cur += n; +} diff --git a/tests/automated/Makefile b/tests/automated/Makefile new file mode 100644 index 0000000..5a15f84 --- /dev/null +++ b/tests/automated/Makefile @@ -0,0 +1,16 @@ +NANOCBOR_DIR = ../.. + +include ../../Makefile + +SRCS += main.c test_decoder.c test_encoder.c +LDFLAGS += -Wl,$(shell pkg-config --libs cunit || echo -lcunit) +CFLAGS += $(shell pkg-config --cflags cunit) + +$(CURDIR)/bin/test: $(OBJS) + $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@ + +test : CFLAGS += -g3 + +test: $(CURDIR)/bin/test + $(CURDIR)/bin/test + diff --git a/tests/automated/main.c b/tests/automated/main.c new file mode 100644 index 0000000..9b2c705 --- /dev/null +++ b/tests/automated/main.c @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include + +#include "CUnit/CUnit.h" +#include "CUnit/Basic.h" +#include "test.h" + +extern const test_t tests_decoder[]; +extern const test_t tests_encoder[]; + +static int add_tests(CU_pSuite pSuite, const test_t* tests) +{ + /* add the tests to the suite */ + for(int i = 0; tests[i].n !=NULL; i++) { + if(!(CU_add_test(pSuite, tests[i].n, tests[i].f))) { + printf("Error adding function %s\n",tests[i].n); + CU_cleanup_registry(); + return CU_get_error(); + } + } + return 0; +} + +int main() +{ + CU_pSuite pSuite = NULL; + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + pSuite = CU_add_suite("Nanocbor decode", NULL, NULL); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + add_tests(pSuite, tests_decoder); + + pSuite = CU_add_suite("Nanocbor encode", NULL, NULL); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + add_tests(pSuite, tests_encoder); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + printf("\n"); + + if (CU_get_number_of_failure_records()) { + exit(2); + } + return CU_get_error(); +} diff --git a/tests/automated/test.h b/tests/automated/test.h new file mode 100644 index 0000000..889b22e --- /dev/null +++ b/tests/automated/test.h @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +#ifndef TEST_H +#define TEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Function prototype for a test function + */ +typedef void (*test_func)(void); + +/** + * Struct to define a test + */ +typedef struct test { + const test_func f; /**< Function to run as test */ + const char *n; /**< Name or description of the test */ +} test_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tests/automated/test_decoder.c b/tests/automated/test_decoder.c new file mode 100644 index 0000000..0b4bb95 --- /dev/null +++ b/tests/automated/test_decoder.c @@ -0,0 +1,217 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "test.h" +#include "nanocbor/nanocbor.h" +#include + +static void test_decode_indefinite(void) +{ + /* Test vector, 3 integers in an indefinite array */ + static const uint8_t indefinite[] = { + 0x9f, 0x01, 0x02, 0x03, 0xff + }; + + nanocbor_value_t val; + nanocbor_value_t cont; + + uint32_t tmp = 0; + + nanocbor_decoder_init(&val, indefinite, sizeof(indefinite)); + + CU_ASSERT_EQUAL(nanocbor_enter_array(&val, &cont), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_container_indefinite(&cont), true); + + /* Decode the three values */ + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + + CU_ASSERT_EQUAL(nanocbor_get_uint32(&cont, &tmp), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); +} + +static void test_decode_map(void) +{ + + static const uint8_t map_empty[] = { + 0xa0 + }; + + static const uint8_t map_one[] = { + 0xa1, 0x01, 0x02 + }; + + static const uint8_t complex_map_decode[] = { + 0xa5, 0x01, 0x02, 0x03, 0x80, 0x04, 0x9F, 0xFF, 0x05, 0x9F, 0xff, 0x06, 0xf6 + }; + + nanocbor_value_t val; + nanocbor_value_t cont; + + uint32_t tmp = 0; + + /* Init the decoder and assert the properties of the empty map */ + nanocbor_decoder_init(&val, map_empty, sizeof(map_empty)); + CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); + nanocbor_leave_container(&val, &cont); + CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); + + /* Init the decoder and verify the decoding of the map elements */ + nanocbor_decoder_init(&val, map_one, sizeof(map_one)); + CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_OK); + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 1); + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 2); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); + nanocbor_leave_container(&val, &cont); + CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); + + /* Init the decoder and skip over the empty map */ + nanocbor_decoder_init(&val, map_empty, sizeof(map_empty)); + CU_ASSERT_EQUAL(nanocbor_skip(&val), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); + + /* Init the decoder and skip over the non-empty map */ + nanocbor_decoder_init(&val, map_one, sizeof(map_one)); + CU_ASSERT_EQUAL(nanocbor_skip(&val), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_at_end(&val), true); + + nanocbor_value_t array; + /* Init decoder and start decoding */ + nanocbor_decoder_init(&val, complex_map_decode, sizeof(complex_map_decode)); + CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_OK); + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 1); + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 2); + + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 3); + CU_ASSERT_EQUAL(nanocbor_enter_array(&cont, &array), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_at_end(&array), true); + nanocbor_leave_container(&cont, &array); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); + + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 4); + CU_ASSERT_EQUAL(nanocbor_enter_array(&cont, &array), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_at_end(&array), true); + nanocbor_leave_container(&cont, &array); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); + + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 5); + CU_ASSERT_EQUAL(nanocbor_enter_array(&cont, &array), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_at_end(&array), true); + nanocbor_leave_container(&cont, &array); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); + + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 6); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), false); + CU_ASSERT_EQUAL(nanocbor_get_null(&cont), NANOCBOR_OK); + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); +} + +static void test_tag(void) +{ + static const uint8_t arraytag[] = { + 0x82, 0xd8, 0x37, 0x01, 0x02 + }; + + nanocbor_value_t val; + nanocbor_value_t cont; + + uint32_t tmp = 0x12345678; + + nanocbor_decoder_init(&val, arraytag, sizeof(arraytag)); + + CU_ASSERT_EQUAL(nanocbor_enter_array(&val, &cont), NANOCBOR_OK); + + CU_ASSERT_EQUAL(nanocbor_get_tag(&cont, &tmp), NANOCBOR_OK); + CU_ASSERT_EQUAL(tmp, 0x37); + + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 1); + + CU_ASSERT(nanocbor_get_uint32(&cont, &tmp) > 0); + CU_ASSERT_EQUAL(tmp, 2); + + CU_ASSERT_EQUAL(nanocbor_at_end(&cont), true); +} + +static void test_decode_none(void) +{ + nanocbor_value_t val; + nanocbor_value_t cont; + uint64_t tmp; + nanocbor_decoder_init(&val, NULL, 0); + + CU_ASSERT_EQUAL(nanocbor_get_type(&val), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_get_uint32(&val, (uint32_t*)&tmp), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_get_int32(&val, (int32_t*)&tmp), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_enter_array(&val, &cont), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_enter_map(&val, &cont), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_get_null(&val), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_get_bool(&val, (bool*)&tmp), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_skip(&val), NANOCBOR_ERR_END); + CU_ASSERT_EQUAL(nanocbor_skip_simple(&val), NANOCBOR_ERR_END); +} + +static void test_decode_basic(void) +{ + nanocbor_value_t decoder; + uint8_t byteval = 5; /* unsigned integer, value 5 */ + uint32_t value = 0; + + nanocbor_decoder_init(&decoder, &byteval, sizeof(byteval)); + CU_ASSERT_EQUAL(nanocbor_get_type(&decoder), NANOCBOR_TYPE_UINT); + printf("\"val: %u\"\n", value); + CU_ASSERT_EQUAL(nanocbor_get_uint32(&decoder, &value), 1); + printf("\"val: %u\"\n", value); + CU_ASSERT_EQUAL(5, value); + + int32_t intval = 0; + nanocbor_decoder_init(&decoder, &byteval, sizeof(byteval)); + CU_ASSERT_EQUAL(nanocbor_get_int32(&decoder, &intval), 1); + CU_ASSERT_EQUAL(5, intval); + + const uint8_t decimal_frac[] = { 0xC4, 0x82, 0x21, 0x19, 0x6a, 0xb3 }; + int32_t m; + int32_t e; + nanocbor_decoder_init(&decoder, decimal_frac, sizeof(decimal_frac)); + CU_ASSERT_EQUAL(nanocbor_get_decimal_frac(&decoder, &e, &m), 0); + CU_ASSERT_EQUAL(e, -2); + CU_ASSERT_EQUAL(m, 27315); +} + +const test_t tests_decoder[] = { + { + .f = test_decode_none, + .n = "get type on empty buffer", + }, + { + .f = test_decode_basic, + .n = "Simple CBOR integer tests", + }, + { + .f = test_decode_indefinite, + .n = "CBOR indefinite array decode tests", + }, + { + .f = test_decode_map, + .n = "CBOR map decode tests", + }, + { + .f = test_tag, + .n = "CBOR tag decode test", + }, + { + .f = NULL, + .n = NULL, + } +}; diff --git a/tests/automated/test_encoder.c b/tests/automated/test_encoder.c new file mode 100644 index 0000000..c210f21 --- /dev/null +++ b/tests/automated/test_encoder.c @@ -0,0 +1,127 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "test.h" +#include "nanocbor/nanocbor.h" +#include "nanocbor/stream_encoders/memory_buffer.h" +#include +#include +#include + +static void print_bytestr(const uint8_t *bytes, size_t len) +{ + printf("\n"); + for(unsigned int idx=0; idx < len; idx++) + { + printf("%02X", bytes[idx]); + } + printf("\n"); +} + +static void test_encode_float_specials(void) +{ + uint8_t buf[64]; + memory_encoder stream; + MemoryStream_Init(&stream, buf, sizeof(buf)); + + FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; + FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; + FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; + + nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); + + nanocbor_fmt_array_indefinite(&enc); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, NAN), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -NAN), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, INFINITY), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -INFINITY), 3); + + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, NAN), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -NAN), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, INFINITY), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -INFINITY), 3); + nanocbor_fmt_end_indefinite(&enc); + print_bytestr(buf, nanocbor_encoded_len(&enc)); +} + +static void test_encode_float_to_half(void) +{ + uint8_t buf[64]; + memory_encoder stream; + MemoryStream_Init(&stream, buf, sizeof(buf)); + + FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; + FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; + FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; + + nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); + + nanocbor_fmt_array_indefinite(&enc); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 1.75), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 1.9990234375), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 1.99951171875), 5); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, 2.0009765625), 5); + + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -1.75), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -1.9990234375), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -1.99951171875), 5); + CU_ASSERT_EQUAL(nanocbor_fmt_float(&enc, -2.0009765625), 5); + + nanocbor_fmt_end_indefinite(&enc); + print_bytestr(buf, nanocbor_encoded_len(&enc)); +} + +static void test_encode_double_to_float(void) +{ + uint8_t buf[128]; + memory_encoder stream; + MemoryStream_Init(&stream, buf, sizeof(buf)); + + FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; + FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; + FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; + + nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); + + nanocbor_fmt_array_indefinite(&enc); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.75), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.9990234375), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.99951171875), 5); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 2.0009765625), 5); + + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -1.75), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -1.9990234375), 3); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -1.99951171875), 5); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, -2.0009765625), 5); + + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1.00000011920928955078125), 5); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, DBL_MIN), 9); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, DBL_MAX), 9); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, 1e39), 9); + + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, FLT_MIN), 5); + CU_ASSERT_EQUAL(nanocbor_fmt_double(&enc, FLT_MAX), 5); + + nanocbor_fmt_end_indefinite(&enc); + print_bytestr(buf, nanocbor_encoded_len(&enc)); +} + +const test_t tests_encoder[] = { + { + .f = test_encode_float_specials, + .n = "Float reduction encoder test", + }, + { + .f = test_encode_float_to_half, + .n = "Float reduction encoder test", + }, + { + .f = test_encode_double_to_float, + .n = "Double reduction encoder test", + }, + { + .f = NULL, + .n = NULL, + } +}; diff --git a/tests/encode/Makefile b/tests/encode/Makefile new file mode 100644 index 0000000..581364d --- /dev/null +++ b/tests/encode/Makefile @@ -0,0 +1,12 @@ +NANOCBOR_DIR = $(CURDIR)/../.. + +include ../../Makefile + +SRCS += main.c + +bin/encode: $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o $@ + +test : CFLAGS += -g3 + +test: bin/encode diff --git a/tests/encode/main.c b/tests/encode/main.c new file mode 100644 index 0000000..364e9bd --- /dev/null +++ b/tests/encode/main.c @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include +#include + +#include "nanocbor/nanocbor.h" +#include "nanocbor/stream_encoders/memory_buffer.h" + +static void _encode(nanocbor_encoder_t *enc) +{ + nanocbor_fmt_array_indefinite(enc); + nanocbor_fmt_bool(enc, true); + nanocbor_fmt_bool(enc, false); + nanocbor_fmt_uint(enc, UINT32_MAX); + nanocbor_fmt_int(enc, INT32_MIN); + nanocbor_fmt_map(enc, 4); + nanocbor_fmt_uint(enc, 8); + nanocbor_fmt_int(enc, 30); + nanocbor_fmt_int(enc, -30); + nanocbor_fmt_int(enc, 500); + nanocbor_fmt_int(enc, -500); + nanocbor_put_tstr(enc, "this is a long string"); + nanocbor_fmt_float(enc, 0.34); + nanocbor_put_bstr(enc, (uint8_t*)"bytez", sizeof("bytez")); + nanocbor_fmt_null(enc); + nanocbor_fmt_decimal_frac(enc, -2, 27315); + nanocbor_fmt_end_indefinite(enc); +} + +int main(void) +{ + memory_encoder stream; + + MemoryStream_Init(&stream, NULL, 0); + + FnStreamLength len_fn = (FnStreamLength)MemoryStream_Length; + FnStreamReserve res_fn = (FnStreamReserve)MemoryStream_Reserve; + FnStreamInsert ins_fn = (FnStreamInsert)MemoryStream_Insert; + + nanocbor_encoder_t enc = NANOCBOR_ENCODER(&stream, len_fn, res_fn, ins_fn); + + _encode(&enc); + + size_t required = nanocbor_encoded_len(&enc); + + uint8_t *buf = malloc(required); + if (!buf) { + return -1; + } + + MemoryStream_Init(&stream, buf, required); + + _encode(&enc); + + //printf("Bytes: %u\n", (unsigned)nanocbor_encoded_len(&enc)); + fwrite(buf, 1, nanocbor_encoded_len(&enc), stdout); + + return 0; +} diff --git a/tests/encode/out.txt b/tests/encode/out.txt new file mode 100644 index 0000000000000000000000000000000000000000..15a5d744f8fd1c5bebfbd10f92181aba200bf762 GIT binary patch literal 300 zcmYk0F-`+96hukK0VoJ4=pDgcd+jv^C8wbAE_Mu`0 zz}M(&{%B_OLnhZ`i;iUBdzgsda}lFOHE2P?$i7AglrBNDBL-jNEv~@@yrEM6i2gY{ zT*f7wFQ<6K@2g_li8*2tF`-3ou?|<^rA=z3JfalY(OH@b-Tr<1`*tA`57Zse6vN~D z=42~#elP3!@c6754F{Vrc)RT<(}Eb@*)OK^@#n?M(cNXc>eKq*LqCjP!WcB#LLVf{i2g6;&B;geMnx& zYgb(d&I^nuML(gY%`c_^k9JPwHU)e!^Bhj=1T`v(W)^Ed_5iS4i7;%zwSDDI^Y)>b z(FV}AH?(N?t@io`muyazd)gh#4CQ_15dH-QfxTePh; vfS1Pcv|q^uCS +#include +#include +#include +#include + +#include "nanocbor/nanocbor.h" + +char buffer[256]; + +static int _parse_type(nanocbor_value_t *value, unsigned indent); + +static void _indent(unsigned indent) +{ + for(unsigned i = 0; i < indent; i++) { + printf(" "); + } +} + +void _parse_cbor(nanocbor_value_t *it, unsigned indent) +{ + while (!nanocbor_at_end(it)) { + _indent(indent); + int res = _parse_type(it, indent); + printf(",\n"); + if (res < 0) { + printf("Err\n"); + break; + } + } +} + +void _parse_map(nanocbor_value_t *it, unsigned indent) +{ + while (!nanocbor_at_end(it)) { + _indent(indent); + int res = _parse_type(it, indent); + printf(": "); + if (res < 0) { + printf("Err\n"); + break; + } + res = _parse_type(it, indent); + if (res < 0) { + printf("Err\n"); + break; + } + printf(",\n"); + } +} + +static int _parse_type(nanocbor_value_t *value, unsigned indent) +{ + uint8_t type = nanocbor_get_type(value); + if (indent > 10) { + return -2; + } + switch (type) { + case NANOCBOR_TYPE_UINT: + { + uint32_t uint; + if (nanocbor_get_uint32(value, &uint) >= 0) { + printf("%lu", (long unsigned)uint); + } + else { + return -1; + } + } + break; + case NANOCBOR_TYPE_NINT: + { + int32_t int32; + if (nanocbor_get_int32(value, &int32) >= 0) { + printf("%ld", (long unsigned)int32); + } + else { + return -1; + } + } + break; + case NANOCBOR_TYPE_BSTR: + { + const uint8_t *buf = NULL; + size_t len; + if (nanocbor_get_bstr(value, &buf, &len) >= 0 && buf) { + size_t iter = 0; + printf("\""); + while(iter < len) { + printf("0x%.2x, ", buf[iter]); + iter++; + } + printf("\""); + } + else { + return -1; + } + } + break; + case NANOCBOR_TYPE_TSTR: + { + const uint8_t *buf; + size_t len; + if (nanocbor_get_tstr(value, &buf, &len) >= 0) { + printf("\"%.*s\"", (int)len, buf); + } + else { + return -1; + } + } + break; + case NANOCBOR_TYPE_ARR: + { + nanocbor_value_t arr; + if (nanocbor_enter_array(value, &arr) >= 0) { + printf("[\n"); + _parse_cbor(&arr, indent + 1); + nanocbor_leave_container(value, &arr); + _indent(indent); + printf("]"); + } + else { + return -1; + } + } + break; + case NANOCBOR_TYPE_MAP: + { + nanocbor_value_t map; + if (nanocbor_enter_map(value, &map) >= NANOCBOR_OK) {; + printf("{\n"); + _parse_map(&map, indent + 1); + nanocbor_leave_container(value, &map); + _indent(indent); + printf("}"); + } + else { + return -1; + } + } + break; + case NANOCBOR_TYPE_FLOAT: + { + bool test; + if (nanocbor_get_bool(value, &test) >= NANOCBOR_OK) { + test ? printf("True") : printf("False"); + } + else if (nanocbor_get_null(value) >= NANOCBOR_OK) { + printf("NULL"); + } + else if (nanocbor_skip_simple(value) >= 0) { + printf("Unsupported float"); + } + else { + return -1; + } + break; + } + default: + printf("Unsupported type\n"); + return -1; + + } + return 1; +} + +int main(void) +{ + ssize_t len = read(STDIN_FILENO, buffer, sizeof(buffer)); + printf("Reading %ld bytes from stdin\n", (long signed)len); + if (len < 0) { + return -1; + } + + nanocbor_value_t it; + nanocbor_decoder_init(&it, (uint8_t*)buffer, len); + while (!nanocbor_at_end(&it)) { + printf("advancing\n"); + if(nanocbor_skip(&it) < 0) { + break; + } + } + + nanocbor_decoder_init(&it, (uint8_t*)buffer, len); + printf("parsing cbor\n"); + _parse_cbor(&it, 0); + printf("Done parsing cbor\n"); + + return 0; +} diff --git a/uncrustify.cfg b/uncrustify.cfg new file mode 100644 index 0000000..d1a8bae --- /dev/null +++ b/uncrustify.cfg @@ -0,0 +1,69 @@ +indent_with_tabs = 0 # 1=indent to level only, 2=indent with tabs +input_tab_size = 4 # original tab size +output_tab_size = 4 # new tab size +indent_columns = output_tab_size +indent_label = 1 # pos: absolute col, neg: relative column +indent_switch_case = 4 # number + +# +# inter-symbol newlines +# + +nl_enum_brace = remove # "enum {" vs "enum \n {" +nl_union_brace = remove # "union {" vs "union \n {" +nl_struct_brace = remove # "struct {" vs "struct \n {" +nl_do_brace = remove # "do {" vs "do \n {" +nl_if_brace = remove # "if () {" vs "if () \n {" +nl_for_brace = remove # "for () {" vs "for () \n {" +nl_else_brace = remove # "else {" vs "else \n {" +nl_while_brace = remove # "while () {" vs "while () \n {" +nl_switch_brace = remove # "switch () {" vs "switch () \n {" +nl_brace_while = remove # "} while" vs "} \n while" - cuddle while +nl_brace_else = add # "} \n else" vs "} else" +nl_func_var_def_blk = 1 +nl_fcall_brace = remove # "list_for_each() {" vs "list_for_each()\n{" +nl_fdef_brace = add # "int foo() {" vs "int foo()\n{" + +# +# Source code modifications +# + +mod_paren_on_return = ignore # "return 1;" vs "return (1);" +mod_full_brace_if = add # "if() { } else { }" vs "if() else" + +# +# inter-character spacing options +# + +sp_sizeof_paren = remove # "sizeof (int)" vs "sizeof(int)" +sp_before_sparen = force # "if (" vs "if(" +sp_after_sparen = force # "if () {" vs "if (){" +sp_inside_braces = add # "{ 1 }" vs "{1}" +sp_inside_braces_struct = add # "{ 1 }" vs "{1}" +sp_inside_braces_enum = add # "{ 1 }" vs "{1}" +sp_assign = add +sp_arith = add +sp_bool = add +sp_compare = add +sp_assign = add +sp_after_comma = add +sp_func_def_paren = remove # "int foo (){" vs "int foo(){" +sp_func_call_paren = remove # "foo (" vs "foo(" +sp_func_proto_paren = remove # "int foo ();" vs "int foo();" +sp_else_brace = add # ignore/add/remove/force +sp_before_ptr_star = add # ignore/add/remove/force +sp_after_ptr_star = remove # ignore/add/remove/force +sp_between_ptr_star = remove # ignore/add/remove/force +sp_inside_paren = remove # remove spaces inside parens +sp_paren_paren = remove # remove spaces between nested parens +sp_inside_sparen = remove # remove spaces inside parens for if, while and the like + +# +# Aligning stuff +# + +align_with_tabs = FALSE # use tabs to align +align_on_tabstop = TRUE # align on tabstops +align_enum_equ_span = 4 # '=' in enum definition +align_struct_init_span = 0 # align stuff in a structure init '= { }' +align_right_cmt_span = 3 From b7cb4e9df42be48689005dd3dc3857274b7be74c Mon Sep 17 00:00:00 2001 From: Paul Thomas Date: Tue, 3 Oct 2023 14:03:57 -0400 Subject: [PATCH 4/4] size_t, use size_t instead of int --- include/nanocbor/nanocbor.h | 2 +- src/decoder.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nanocbor/nanocbor.h b/include/nanocbor/nanocbor.h index 79d3ac1..ae03aa7 100644 --- a/include/nanocbor/nanocbor.h +++ b/include/nanocbor/nanocbor.h @@ -241,7 +241,7 @@ void nanocbor_decoder_init(nanocbor_value_t *value, * @return major type * @return NANOCBOR_ERR_END if the buffer is exhausted */ -int nanocbor_get_type(const nanocbor_value_t *value); +size_t nanocbor_get_type(const nanocbor_value_t *value); /** * @brief Check if the current buffer or container is exhausted diff --git a/src/decoder.c b/src/decoder.c index e578d55..087ffd5 100644 --- a/src/decoder.c +++ b/src/decoder.c @@ -85,7 +85,7 @@ bool nanocbor_at_end(const nanocbor_value_t *it) return end; } -int nanocbor_get_type(const nanocbor_value_t *value) +size_t nanocbor_get_type(const nanocbor_value_t *value) { if (nanocbor_at_end(value)) { return NANOCBOR_ERR_END;