zcbor
Advanced tools
| /* | ||
| * Copyright (c) 2022 Nordic Semiconductor ASA | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #ifndef ZCBOR_TAGS_H__ | ||
| #define ZCBOR_TAGS_H__ | ||
| #include <stdint.h> | ||
| #include <stdbool.h> | ||
| #include <stddef.h> | ||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
| /** Values defined by RFCs via www.iana.org/assignments/cbor-tags/cbor-tags.xhtml */ | ||
| enum zcbor_tag { | ||
| ZCBOR_TAG_TIME_TSTR = 0, ///! text string [RFC8949] Standard date/time string | ||
| ZCBOR_TAG_TIME_NUM = 1, ///! integer or float [RFC8949] Epoch-based date/time | ||
| ZCBOR_TAG_UBIGNUM_BSTR = 2, ///! byte string [RFC8949] Unsigned bignum | ||
| ZCBOR_TAG_BIGNUM_BSTR = 3, ///! byte string [RFC8949] Negative bignum | ||
| ZCBOR_TAG_DECFRAC_ARR = 4, ///! array [RFC8949] Decimal fraction | ||
| ZCBOR_TAG_BIGFLOAT_ARR = 5, ///! array [RFC8949] Bigfloat | ||
| ZCBOR_TAG_COSE_ENCRYPT0 = 16, ///! COSE_Encrypt0 [RFC9052] COSE Single Recipient Encrypted Data Object | ||
| ZCBOR_TAG_COSE_MAC0 = 17, ///! COSE_Mac0 [RFC9052] COSE MAC w/o Recipients Object | ||
| ZCBOR_TAG_COSE_SIGN1 = 18, ///! COSE_Sign1 [RFC9052] COSE Single Signer Data Object | ||
| ZCBOR_TAG_2BASE64URL = 21, ///! (any) [RFC8949] Expected conversion to base64url encoding | ||
| ZCBOR_TAG_2BASE64 = 22, ///! (any) [RFC8949] Expected conversion to base64 encoding | ||
| ZCBOR_TAG_2BASE16 = 23, ///! (any) [RFC8949] Expected conversion to base16 encoding | ||
| ZCBOR_TAG_BSTR = 24, ///! byte string [RFC8949] Encoded CBOR data item | ||
| ZCBOR_TAG_URI_TSTR = 32, ///! text string [RFC8949] URI | ||
| ZCBOR_TAG_BASE64URL_TSTR = 33, ///! text string [RFC8949] base64url | ||
| ZCBOR_TAG_BASE64_TSTR = 34, ///! text string [RFC8949] base64 | ||
| ZCBOR_TAG_REGEX = 35, ///! text string [RFC7049] Regular expression (UTF-8) | ||
| ZCBOR_TAG_MIME_TSTR = 36, ///! text string [RFC8949] MIME message | ||
| ZCBOR_TAG_LANG_TSTR = 38, ///! array [RFC9290] Text string with language tag | ||
| ZCBOR_TAG_MULTI_DIM_ARR_R = 40, ///! array of arrays [RFC8746] Multi-dimensional array, row-major order | ||
| ZCBOR_TAG_HOMOG_ARR = 41, ///! array [RFC8746] Homogeneous array | ||
| ZCBOR_TAG_YANG_BITS = 42, ///! text string [RFC9254] YANG bits datatype; see Section 6.7. | ||
| ZCBOR_TAG_YANG_ENUM = 43, ///! text string [RFC9254] YANG enumeration datatype; see Section 6.6. | ||
| ZCBOR_TAG_YANG_IDENTITYREF = 44, ///! uint/tstr [RFC9254] YANG identityref datatype; see Section 6.10. | ||
| ZCBOR_TAG_YANK_INSTANCE_ID = 45, ///! uint/tstr/array [RFC9254] YANG instance-identifier datatype; see Section 6.13. | ||
| ZCBOR_TAG_SID = 46, ///! uint [RFC9254] YANG Schema Item iDentifier (sid); see Section 3.2. | ||
| ZCBOR_TAG_IPV4 = 52, ///! bstr or array [RFC9164] IPv4 | ||
| ZCBOR_TAG_IPV6 = 54, ///! bstr or array [RFC9164] IPv6 | ||
| ZCBOR_TAG_CWT = 61, ///! CWT [RFC8392] CBOR Web Token | ||
| ZCBOR_TAG_TYPED_ARR_U8 = 64, ///! byte string [RFC8746] uint8 Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_U16_BE = 65, ///! byte string [RFC8746] uint16, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_U32_BE = 66, ///! byte string [RFC8746] uint32, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_U64_BE = 67, ///! byte string [RFC8746] uint64, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_U8_CA = 68, ///! byte string [RFC8746] uint8 Typed Array, clamped arithmetic | ||
| ZCBOR_TAG_TYPED_ARR_U16_LE = 69, ///! byte string [RFC8746] uint16, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_U32_LE = 70, ///! byte string [RFC8746] uint32, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_U64_LE = 71, ///! byte string [RFC8746] uint64, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_S8 = 72, ///! byte string [RFC8746] sint8 Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_S16_BE = 73, ///! byte string [RFC8746] sint16, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_S32_BE = 74, ///! byte string [RFC8746] sint32, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_S64_BE = 75, ///! byte string [RFC8746] sint64, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_S16_LE = 77, ///! byte string [RFC8746] sint16, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_S32_LE = 78, ///! byte string [RFC8746] sint32, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_S64_LE = 79, ///! byte string [RFC8746] sint64, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F16_BE = 80, ///! byte string [RFC8746] IEEE 754 binary16, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F32_BE = 81, ///! byte string [RFC8746] IEEE 754 binary32, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F64_BE = 82, ///! byte string [RFC8746] IEEE 754 binary64, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F128_BE = 83, ///! byte string [RFC8746] IEEE 754 binary128, big endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F16_LE = 84, ///! byte string [RFC8746] IEEE 754 binary16, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F32_LE = 85, ///! byte string [RFC8746] IEEE 754 binary32, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F64_LE = 86, ///! byte string [RFC8746] IEEE 754 binary64, little endian, Typed Array | ||
| ZCBOR_TAG_TYPED_ARR_F128_LE = 87, ///! byte string [RFC8746] IEEE 754 binary128, little endian, Typed Array | ||
| ZCBOR_TAG_COSE_ENCRYPT = 96, ///! COSE_Encrypt [RFC9052] COSE Encrypted Data Object | ||
| ZCBOR_TAG_COSE_MAC = 97, ///! COSE_Mac [RFC9052] COSE MACed Data Object | ||
| ZCBOR_TAG_COSE_SIGN = 98, ///! COSE_Sign [RFC9052] COSE Signed Data Object | ||
| ZCBOR_TAG_EPOCH_DAYS = 100, ///! integer [RFC8943] Number of days since the epoch date 1970-01-01 | ||
| ZCBOR_TAG_REL_OID_BER_SDNV = 110, ///! bstr/array/map [RFC9090] relative object identifier (BER encoding); SDNV [RFC6256] sequence | ||
| ZCBOR_TAG_OID_BER = 111, ///! bstr/array/map [RFC9090] object identifier (BER encoding) | ||
| ZCBOR_TAG_PEN_REL_OID_BER = 112, ///! bstr/array/map [RFC9090] object identifier (BER encoding), relative to 1.3.6.1.4.1 | ||
| ZCBOR_TAG_DOTS_SIG_CHAN_OBJ = 271, ///! DOTS sig chan obj [RFC9132] DDoS Open Threat Signaling (DOTS) signal channel object | ||
| ZCBOR_TAG_FULL_DATE_STR = 1004, ///! tstr (UTF-8) [RFC8943] Full-date string | ||
| ZCBOR_TAG_MULTI_DIM_ARR_C = 1040, ///! array of arrays [RFC8746] Multi-dimensional array, column-major order | ||
| ZCBOR_TAG_CBOR = 55799, ///! (any) [RFC8949] Self-described CBOR | ||
| ZCBOR_TAG_CBOR_SEQ_FILE = 55800, ///! tagged bstr [RFC9277] indicates that the file contains CBOR Sequences | ||
| ZCBOR_TAG_CBOR_FILE_LABEL = 55801, ///! tagged bstr [RFC9277] indicates that the file starts with a CBOR-Labeled Non-CBOR Data label. | ||
| ZCBOR_TAG_COAP_CT = 1668546817, ///! bstr or (any) [RFC9277] Start of range: the representation of content-format ct < 65025 is indicated by tag number TN(ct) = 0x63740101 + (ct / 255) * 256 + ct % 255 | ||
| ZCBOR_TAG_COAP_CT_END = 1668612095, ///! bstr or (any) [RFC9277] End of range: the representation of content-format ct < 65025 is indicated by tag number TN(ct) = 0x63740101 + (ct / 255) * 256 + ct % 255 | ||
| }; | ||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
| #endif /* ZCBOR_TAGS_H__ */ |
+62
-45
@@ -13,2 +13,4 @@ /* | ||
| #include <stddef.h> | ||
| #include <string.h> | ||
| #include "zcbor_tags.h" | ||
@@ -109,7 +111,7 @@ #ifdef __cplusplus | ||
| uint8_t const *payload_bak; /**< Temporary backup of payload. */ | ||
| uint_fast32_t elem_count; /**< The current element is part of a LIST or a MAP, | ||
| and this keeps count of how many elements are | ||
| expected. This will be checked before processing | ||
| and decremented if the element is correctly | ||
| processed. */ | ||
| size_t elem_count; /**< The current element is part of a LIST or a MAP, | ||
| and this keeps count of how many elements are | ||
| expected. This will be checked before processing | ||
| and decremented if the element is correctly | ||
| processed. */ | ||
| uint8_t const *payload_end; /**< The end of the payload. This will be | ||
@@ -130,4 +132,4 @@ checked against payload before | ||
| zcbor_state_t *backup_list; | ||
| uint_fast32_t current_backup; | ||
| uint_fast32_t num_backups; | ||
| size_t current_backup; | ||
| size_t num_backups; | ||
| int error; | ||
@@ -153,10 +155,10 @@ #ifdef ZCBOR_STOP_ON_ERROR | ||
| { | ||
| ZCBOR_MAJOR_TYPE_PINT = 0, ///! Positive Integer | ||
| ZCBOR_MAJOR_TYPE_NINT = 1, ///! Negative Integer | ||
| ZCBOR_MAJOR_TYPE_BSTR = 2, ///! Byte String | ||
| ZCBOR_MAJOR_TYPE_TSTR = 3, ///! Text String | ||
| ZCBOR_MAJOR_TYPE_LIST = 4, ///! List | ||
| ZCBOR_MAJOR_TYPE_MAP = 5, ///! Map | ||
| ZCBOR_MAJOR_TYPE_TAG = 6, ///! Semantic Tag | ||
| ZCBOR_MAJOR_TYPE_PRIM = 7, ///! Primitive Type | ||
| ZCBOR_MAJOR_TYPE_PINT = 0, ///! Positive Integer | ||
| ZCBOR_MAJOR_TYPE_NINT = 1, ///! Negative Integer | ||
| ZCBOR_MAJOR_TYPE_BSTR = 2, ///! Byte String | ||
| ZCBOR_MAJOR_TYPE_TSTR = 3, ///! Text String | ||
| ZCBOR_MAJOR_TYPE_LIST = 4, ///! List | ||
| ZCBOR_MAJOR_TYPE_MAP = 5, ///! Map | ||
| ZCBOR_MAJOR_TYPE_TAG = 6, ///! Semantic Tag | ||
| ZCBOR_MAJOR_TYPE_SIMPLE = 7, ///! Simple values and floats | ||
| } zcbor_major_type_t; | ||
@@ -173,2 +175,9 @@ | ||
| #define ZCBOR_FAIL_IF(expr) \ | ||
| do {\ | ||
| if (expr) { \ | ||
| ZCBOR_FAIL(); \ | ||
| } \ | ||
| } while(0) | ||
| #define ZCBOR_ERR(err) \ | ||
@@ -208,3 +217,3 @@ do { \ | ||
| #define ZCBOR_BOOL_TO_PRIM ((uint8_t)20) ///! In CBOR, false/true have the values 20/21 | ||
| #define ZCBOR_BOOL_TO_SIMPLE ((uint8_t)20) ///! In CBOR, false/true have the values 20/21 | ||
@@ -236,3 +245,3 @@ #define ZCBOR_FLAG_RESTORE 1UL ///! Restore from the backup. Overwrite the current state with the state from the backup. | ||
| #else | ||
| #define ZCBOR_MAX_ELEM_COUNT ((uint_fast32_t)(-1L)) | ||
| #define ZCBOR_MAX_ELEM_COUNT ((size_t)(-1L)) | ||
| #endif | ||
@@ -244,24 +253,4 @@ | ||
| /** Values defined by RFC8949 via www.iana.org/assignments/cbor-tags/cbor-tags.xhtml */ | ||
| enum zcbor_rfc8949_tag { | ||
| ZCBOR_TAG_TIME_TSTR = 0, ///! text string Standard date/time string | ||
| ZCBOR_TAG_TIME_NUM = 1, ///! integer or float Epoch-based date/time | ||
| ZCBOR_TAG_UBIGNUM_BSTR = 2, ///! byte string Unsigned bignum | ||
| ZCBOR_TAG_BIGNUM_BSTR = 3, ///! byte string Negative bignum | ||
| ZCBOR_TAG_DECFRAC_ARR = 4, ///! array Decimal fraction | ||
| ZCBOR_TAG_BIGFLOAT_ARR = 5, ///! array Bigfloat | ||
| ZCBOR_TAG_2BASE64URL = 21, ///! (any) Expected conversion to base64url encoding | ||
| ZCBOR_TAG_2BASE64 = 22, ///! (any) Expected conversion to base64 encoding | ||
| ZCBOR_TAG_2BASE16 = 23, ///! (any) Expected conversion to base16 encoding | ||
| ZCBOR_TAG_BSTR = 24, ///! byte string Encoded CBOR data item | ||
| ZCBOR_TAG_URI_TSTR = 32, ///! text string URI | ||
| ZCBOR_TAG_BASE64URL_TSTR = 33, ///! text string base64url | ||
| ZCBOR_TAG_BASE64_TSTR = 34, ///! text string base64 | ||
| ZCBOR_TAG_MIME_TSTR = 36, ///! text string MIME message | ||
| ZCBOR_TAG_CBOR = 55799, ///! (any) Self-described CBOR | ||
| }; | ||
| /** Take a backup of the current state. Overwrite the current elem_count. */ | ||
| bool zcbor_new_backup(zcbor_state_t *state, uint_fast32_t new_elem_count); | ||
| bool zcbor_new_backup(zcbor_state_t *state, size_t new_elem_count); | ||
@@ -272,3 +261,3 @@ /** Consult the most recent backup. In doing so, check whether elem_count is | ||
| */ | ||
| bool zcbor_process_backup(zcbor_state_t *state, uint32_t flags, uint_fast32_t max_elem_count); | ||
| bool zcbor_process_backup(zcbor_state_t *state, uint32_t flags, size_t max_elem_count); | ||
@@ -304,4 +293,4 @@ /** Convenience function for starting encoding/decoding of a union. | ||
| */ | ||
| void zcbor_new_state(zcbor_state_t *state_array, uint_fast32_t n_states, | ||
| const uint8_t *payload, size_t payload_len, uint_fast32_t elem_count); | ||
| void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, | ||
| const uint8_t *payload, size_t payload_len, size_t elem_count); | ||
@@ -363,4 +352,4 @@ #ifdef ZCBOR_STOP_ON_ERROR | ||
| * This function also updates all backups to the new payload_end. | ||
| * This sets a flag so that if a backup is processed with the flag | ||
| * @ref ZCBOR_FLAG_RESTORE, but without the flag | ||
| * This sets a flag so that @ref zcbor_process_backup fails if a backup is | ||
| * processed with the flag @ref ZCBOR_FLAG_RESTORE, but without the flag | ||
| * @ref ZCBOR_FLAG_TRANSFER_PAYLOAD since this would cause an invalid state. | ||
@@ -391,3 +380,3 @@ * | ||
| bool zcbor_validate_string_fragments(struct zcbor_string_fragment *fragments, | ||
| uint_fast32_t num_fragments); | ||
| size_t num_fragments); | ||
@@ -411,4 +400,32 @@ /** Assemble the fragments into a single string. | ||
| bool zcbor_splice_string_fragments(struct zcbor_string_fragment *fragments, | ||
| uint_fast32_t num_fragments, uint8_t *result, size_t *result_len); | ||
| size_t num_fragments, uint8_t *result, size_t *result_len); | ||
| /** Compare two struct zcbor_string instances. | ||
| * | ||
| * @param[in] str1 A string | ||
| * @param[in] str2 A string to compare to @p str1 | ||
| * | ||
| * @retval true if the strings are identical | ||
| * @retval false if length or contents don't match, or one one or both strings is NULL. | ||
| */ | ||
| bool zcbor_compare_strings(const struct zcbor_string *str1, | ||
| const struct zcbor_string *str2); | ||
| /** Calculate the length of a CBOR string, list, or map header. | ||
| * | ||
| * This can be used to find the start of the CBOR object when you have a | ||
| * pointer to the start of the contents. The function assumes that the header | ||
| * will be the shortest it can be. | ||
| * | ||
| * @param[in] num_elems The number of elements in the string, list, or map. | ||
| * | ||
| * @return The length of the header in bytes (1-9). | ||
| */ | ||
| size_t zcbor_header_len(size_t num_elems); | ||
| /** Find whether the state is at the end of a list or map. | ||
| */ | ||
| bool zcbor_array_at_end(zcbor_state_t *state); | ||
| #ifdef __cplusplus | ||
@@ -415,0 +432,0 @@ } |
@@ -44,2 +44,3 @@ /* | ||
| bool zcbor_int_decode(zcbor_state_t *state, void *result_int, size_t int_size); | ||
| bool zcbor_uint_decode(zcbor_state_t *state, void *result_uint, size_t uint_size); | ||
@@ -134,9 +135,21 @@ /** The following applies to all _expect() functions that don't have docs. | ||
| /** Decode and consume a boolean primitive value. */ | ||
| /** Decode and consume a simple value. */ | ||
| bool zcbor_simple_decode(zcbor_state_t *state, uint8_t *result); | ||
| bool zcbor_simple_expect(zcbor_state_t *state, uint8_t result); | ||
| /** Decode and consume a boolean simple value. */ | ||
| bool zcbor_bool_decode(zcbor_state_t *state, bool *result); | ||
| bool zcbor_bool_expect(zcbor_state_t *state, bool result); | ||
| /** Decode and consume a float */ | ||
| /** Decode and consume an IEEE754 float */ | ||
| bool zcbor_float16_decode(zcbor_state_t *state, float *result); | ||
| bool zcbor_float16_expect(zcbor_state_t *state, float result); | ||
| bool zcbor_float16_bytes_decode(zcbor_state_t *state, uint16_t *result); | ||
| bool zcbor_float16_bytes_expect(zcbor_state_t *state, uint16_t result); | ||
| bool zcbor_float16_32_decode(zcbor_state_t *state, float *result); | ||
| bool zcbor_float16_32_expect(zcbor_state_t *state, float result); | ||
| bool zcbor_float32_decode(zcbor_state_t *state, float *result); | ||
| bool zcbor_float32_expect(zcbor_state_t *state, float result); | ||
| bool zcbor_float32_64_decode(zcbor_state_t *state, double *result); | ||
| bool zcbor_float32_64_expect(zcbor_state_t *state, double result); | ||
| bool zcbor_float64_decode(zcbor_state_t *state, double *result); | ||
@@ -147,3 +160,3 @@ bool zcbor_float64_expect(zcbor_state_t *state, double result); | ||
| /** Consume and expect a "nil"/"undefined" primitive value. | ||
| /** Consume and expect a "nil"/"undefined" simple value. | ||
| * | ||
@@ -303,5 +316,5 @@ * @param[inout] state The current state of the encoding. | ||
| */ | ||
| bool zcbor_multi_decode(uint_fast32_t min_decode, uint_fast32_t max_decode, uint_fast32_t *num_decode, | ||
| bool zcbor_multi_decode(size_t min_decode, size_t max_decode, size_t *num_decode, | ||
| zcbor_decoder_t decoder, zcbor_state_t *state, void *result, | ||
| uint_fast32_t result_len); | ||
| size_t result_len); | ||
@@ -319,3 +332,3 @@ /** Attempt to decode a value that might not be present in the data. | ||
| */ | ||
| bool zcbor_present_decode(uint_fast32_t *present, | ||
| bool zcbor_present_decode(bool *present, | ||
| zcbor_decoder_t decoder, | ||
@@ -326,4 +339,4 @@ zcbor_state_t *state, | ||
| /** See @ref zcbor_new_state() */ | ||
| void zcbor_new_decode_state(zcbor_state_t *state_array, uint_fast32_t n_states, | ||
| const uint8_t *payload, size_t payload_len, uint_fast32_t elem_count); | ||
| void zcbor_new_decode_state(zcbor_state_t *state_array, size_t n_states, | ||
| const uint8_t *payload, size_t payload_len, size_t elem_count); | ||
@@ -330,0 +343,0 @@ /** Convenience macro for declaring and initializing a state with backups. |
+24
-15
@@ -49,2 +49,3 @@ /* | ||
| bool zcbor_int64_encode(zcbor_state_t *state, const int64_t *input); | ||
| bool zcbor_uint_encode(zcbor_state_t *state, const void *input_uint, size_t uint_size); | ||
| bool zcbor_uint32_encode(zcbor_state_t *state, const uint32_t *input); | ||
@@ -112,9 +113,17 @@ bool zcbor_uint64_encode(zcbor_state_t *state, const uint64_t *input); | ||
| /** Encode a tag. Must be called before encoding the value being tagged. */ | ||
| bool zcbor_tag_encode(zcbor_state_t *state, uint32_t input); | ||
| bool zcbor_tag_encode(zcbor_state_t *state, uint32_t tag); | ||
| /** Encode a boolean primitive value. */ | ||
| /** Encode a simple value. */ | ||
| bool zcbor_simple_encode(zcbor_state_t *state, uint8_t *input); | ||
| bool zcbor_simple_put(zcbor_state_t *state, uint8_t input); | ||
| /** Encode a boolean simple value. */ | ||
| bool zcbor_bool_put(zcbor_state_t *state, bool input); | ||
| bool zcbor_bool_encode(zcbor_state_t *state, const bool *input); | ||
| /** Encode a float */ | ||
| /** Encode an IEEE754 float */ | ||
| bool zcbor_float16_put(zcbor_state_t *state, float input); | ||
| bool zcbor_float16_encode(zcbor_state_t *state, const float *input); | ||
| bool zcbor_float16_bytes_put(zcbor_state_t *state, uint16_t input); | ||
| bool zcbor_float16_bytes_encode(zcbor_state_t *state, const uint16_t *input); | ||
| bool zcbor_float32_put(zcbor_state_t *state, float input); | ||
@@ -125,3 +134,3 @@ bool zcbor_float32_encode(zcbor_state_t *state, const float *input); | ||
| /** Encode a "nil"/"undefined" primitive value. @p unused should be NULL. | ||
| /** Encode a "nil"/"undefined" simple value. @p unused should be NULL. | ||
| * | ||
@@ -170,4 +179,4 @@ * @param[inout] state The current state of the encoding. | ||
| */ | ||
| bool zcbor_list_start_encode(zcbor_state_t *state, uint_fast32_t max_num); | ||
| bool zcbor_map_start_encode(zcbor_state_t *state, uint_fast32_t max_num); | ||
| bool zcbor_list_start_encode(zcbor_state_t *state, size_t max_num); | ||
| bool zcbor_map_start_encode(zcbor_state_t *state, size_t max_num); | ||
@@ -191,4 +200,4 @@ /** Encode the end of a list/map. Do some checks and deallocate backup. | ||
| */ | ||
| bool zcbor_list_end_encode(zcbor_state_t *state, uint_fast32_t max_num); | ||
| bool zcbor_map_end_encode(zcbor_state_t *state, uint_fast32_t max_num); | ||
| bool zcbor_list_end_encode(zcbor_state_t *state, size_t max_num); | ||
| bool zcbor_map_end_encode(zcbor_state_t *state, size_t max_num); | ||
| bool zcbor_list_map_end_force_encode(zcbor_state_t *state); | ||
@@ -244,7 +253,7 @@ | ||
| */ | ||
| bool zcbor_multi_encode(uint_fast32_t num_encode, | ||
| bool zcbor_multi_encode(size_t num_encode, | ||
| zcbor_encoder_t encoder, | ||
| zcbor_state_t *state, | ||
| const void *input, | ||
| uint_fast32_t result_len); | ||
| size_t result_len); | ||
@@ -255,5 +264,5 @@ /** Works like @ref zcbor_multi_encode | ||
| */ | ||
| bool zcbor_multi_encode_minmax(uint_fast32_t min_encode, uint_fast32_t max_encode, const uint_fast32_t *num_encode, | ||
| bool zcbor_multi_encode_minmax(size_t min_encode, size_t max_encode, const size_t *num_encode, | ||
| zcbor_encoder_t encoder, zcbor_state_t *state, const void *input, | ||
| uint_fast32_t input_len); | ||
| size_t input_len); | ||
@@ -264,3 +273,3 @@ /** Runs @p encoder on @p state and @p input if @p present is true. | ||
| */ | ||
| bool zcbor_present_encode(const uint_fast32_t *present, | ||
| bool zcbor_present_encode(const bool *present, | ||
| zcbor_encoder_t encoder, | ||
@@ -271,4 +280,4 @@ zcbor_state_t *state, | ||
| /** See @ref zcbor_new_state() */ | ||
| void zcbor_new_encode_state(zcbor_state_t *state_array, uint_fast32_t n_states, | ||
| uint8_t *payload, size_t payload_len, uint_fast32_t elem_count); | ||
| void zcbor_new_encode_state(zcbor_state_t *state_array, size_t n_states, | ||
| uint8_t *payload, size_t payload_len, size_t elem_count); | ||
@@ -275,0 +284,0 @@ /** Convenience macro for declaring and initializing a state with backups. |
+255
-228
| Metadata-Version: 2.1 | ||
| Name: zcbor | ||
| Version: 0.6.0 | ||
| Version: 0.7.0 | ||
| Summary: zcbor | ||
@@ -19,13 +19,15 @@ Home-page: https://github.com/NordicSemiconductor/zcbor | ||
| zcbor is a low footprint [CBOR](https://en.wikipedia.org/wiki/CBOR) library in the C language that comes with a schema-driven script tool that can validate your data, or even generate code for you. | ||
| Aside from the script, the CBOR library is a standalone library which is tailored for use in microcontrollers. | ||
| zcbor is a low footprint [CBOR](https://en.wikipedia.org/wiki/CBOR) library in the C language (C++ compatible), tailored for use in microcontrollers. | ||
| It comes with a schema-driven script tool that can validate your data, or even generate code. | ||
| The schema language (CDDL) allows creating very advanced and detailed schemas. | ||
| The validation/conversion part of the script works with YAML and JSON data, in addition to CBOR. | ||
| The validation and conversion part of the tool works with YAML and JSON data, in addition to CBOR. | ||
| It can for example validate a YAML file against a schema and convert it into CBOR. | ||
| The code generation part of the tool generates C code based on the given schema. | ||
| The generated code performs CBOR encoding and decoding using the C library, while also validating the data against all the rules in the schema. | ||
| The schema language used by zcbor is CDDL (Consise Data Definition Language) which is a powerful human-readable data description language defined in [IETF RFC 8610](https://datatracker.ietf.org/doc/rfc8610/). | ||
| zcbor was previously called "cddl-gen". | ||
| Features | ||
@@ -36,18 +38,49 @@ ======== | ||
| - Python script and module: | ||
| - C code: | ||
| - As a low-footprint CBOR decoding/encoding library similar to TinyCBOR/QCBOR/NanoCBOR. The library can be used independently of the Python script. ([More information](#cbor-decodingencoding-library)) | ||
| - To generate C code (using the Python script) for validating and decoding or encoding CBOR, for use in optimized or constrained environments, such as microcontrollers. ([More information](#code-generation)) | ||
| - Python script and module ([More information](#python-script-and-module)): | ||
| - Validate a YAML/JSON file and translate it into CBOR e.g. for transmission. | ||
| - Validate a YAML/JSON/CBOR file before processing it with some other tool | ||
| - Decode and validate incoming CBOR data into human-readable YAML/JSON. | ||
| - As part of a python script that processes YAML/JSON/CBOR files. zcbor is compatible with PyYAML and can additionally provide validation and/or easier inspection via named tuples. | ||
| - C code: | ||
| - Generate C code for validating and decoding or encoding CBOR, for use in optimized or constrained environments, such as microcontrollers. | ||
| - Provide a low-footprint CBOR decoding/encoding library similar to TinyCBOR/QCBOR/NanoCBOR. | ||
| - As part of a python script that processes YAML/JSON/CBOR files. | ||
| - Uses the same internal representation used by the PyYAML/json/cbor2 libraries. | ||
| - Do validation against a CDDL schema. | ||
| - Create a read-only representation via named tuples (with names taken from the CDDL schema). | ||
| Getting started | ||
| =============== | ||
| There are samples in the [samples](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/samples) directory that demonstrate different ways to use zcbor, both the script tool and the C code. | ||
| 1. The [hello_world sample](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/samples/hello_world/README.md) is a minimum examples of encoding and decoding using the C library. | ||
| 2. The [pet sample](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/samples/pet/README.md) shows a how to use the C library together with generated code, and how to use the script tool to do code generation and data conversion. | ||
| The [tests](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests) also demonstrate how to use zcbor in different ways. The [encoding](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/encode), [decoding](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/decode), and [unit](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/unit) tests run using [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (the samples do not use Zephyr). | ||
| Should I use code generation or the library directly? | ||
| ----------------------------------------------------- | ||
| The benefit of using code generation is greater for decoding than encoding. | ||
| This is because decoding is generally more complex than encoding, since when decoding you have to gracefully handle all possible payloads. | ||
| The code generation will provide a number of checks that are tedious to write manually. | ||
| These checks ensure that the payload is well-formed. | ||
| CBOR decoding/encoding library | ||
| ============================== | ||
| The CBOR library found at [headers](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/include) and [source](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/src) is used by the generated code, but can also be used directly. | ||
| To use it, instantiate a `zcbor_state_t` object, which is most easily done using the `zcbor_new_*_state()` functions or the `ZCBOR_STATE_*()` macros. | ||
| The CBOR library can be found in [include/](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/include) and [src/](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/src) and can be used directly, by including the files in your project. | ||
| If using zcbor with Zephyr, the library will be available when the [CONFIG_ZCBOR](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_ZCBOR) config is enabled. | ||
| The library is also used by generated code. See the the [Code generation](#code-generation) section for more info about code generation. | ||
| The C library is C++ compatible. | ||
| The zcbor state object | ||
| ---------------------- | ||
| To do encoding or decoding with the library, instantiate a `zcbor_state_t` object, which is most easily done using the `ZCBOR_STATE_*()` macros, look below or in the [hello_world](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/samples/hello_world/src/main.c) sample for example code. | ||
| The `elem_count` member refers to the number of encoded objects in the current list or map. | ||
@@ -57,31 +90,15 @@ `elem_count` starts again when entering a nested list or map, and is restored when exiting. | ||
| `elem_count` is one reason for needing "backup" states (the other is to allow rollback of the payload). | ||
| You need a number of backups corresponding to the maximum number of nested levels in your data. | ||
| Backups are needed for _decoding_ if there are any lists, maps, or CBOR-encoded strings (`zcbor_bstr_*_decode`) in the data. | ||
| Backups are needed for _encoding_ if there are any lists or maps *and* you are using canonical encoding (`ZCBOR_CANONICAL`), or when using the `zcbor_bstr_*_encode` functions. | ||
| Backups are needed for encoding if you are using canonical encoding (`ZCBOR_CANONICAL`), or using the `bstrx_cbor_*` functions. | ||
| Backups are needed for decoding if there are any lists, maps, or CBOR-encoded strings in the data. | ||
| Note that the benefits of using the library directly is greater for encoding than for decoding. | ||
| For decoding, the code generation will provide a number of checks that are tedious to write manually, and easy to forget. | ||
| ```c | ||
| /** The number of states must be at least equal to one more than the maximum | ||
| * nested depth of the data. | ||
| */ | ||
| zcbor_state_t states[n]; | ||
| /** Initialize a decoding state (could include an array of backup states). | ||
| * After calling this, decode_state[0] is ready to be used with the decoding APIs. */ | ||
| ZCBOR_STATE_D(decode_state, n, payload, payload_len, elem_count); | ||
| /** Initialize the states. After calling this, states[0] is ready to be used | ||
| * with the encoding/decoding APIs. | ||
| * elem_count must be the maximum expected number of top-level elements when | ||
| * decoding (1 if the data is wrapped in a list). | ||
| * When encoding, elem_count must be 0. | ||
| */ | ||
| zcbor_new_state(states, n, payload, payload_len, elem_count); | ||
| /** Alternatively, use one of the following convenience macros. */ | ||
| ZCBOR_STATE_D(decode_state, n, payload, payload_len, elem_count); | ||
| /** Initialize an encoding state (could include an array of backup states). | ||
| * After calling this, encode_state[0] is ready to be used with the encoding APIs. */ | ||
| ZCBOR_STATE_E(encode_state, n, payload, payload_len, 0); | ||
| ``` | ||
| The CBOR libraries assume little-endianness by default, but you can define ZCBOR_BIG_ENDIAN to change this. | ||
| Configuration | ||
@@ -92,2 +109,3 @@ ------------- | ||
| These configuration options can be enabled by adding them as compile definitions to the build. | ||
| If using zcbor with Zephyr, use the [Kconfig options](https://github.com/zephyrproject-rtos/zephyr/blob/main/modules/zcbor/Kconfig) instead. | ||
@@ -100,3 +118,3 @@ Name | Description | ||
| `ZCBOR_STOP_ON_ERROR` | Enable the `stop_on_error` functionality. This makes all functions abort their execution if called when an error has already happened. | ||
| `ZCBOR_BIG_ENDIAN` | All decoded values are returned as big-endian. | ||
| `ZCBOR_BIG_ENDIAN` | All decoded values are returned as big-endian. The default is little-endian. | ||
@@ -107,5 +125,2 @@ | ||
| Invoking zcbor.py from the command line | ||
| --------------------------------------- | ||
| The zcbor.py script can directly read CBOR, YAML, or JSON data and validate it against a CDDL description. | ||
@@ -115,2 +130,7 @@ It can also freely convert the data between CBOR/YAML/JSON. | ||
| Invoking zcbor.py from the command line | ||
| --------------------------------------- | ||
| zcbor.py can be installed via [`pip`](https://pypi.org/project/zcbor/), or alternatively invoked directly from its location in this repo. | ||
| Following are some generalized examples for validating, and for converting (which also validates) data from the command line. | ||
@@ -121,21 +141,15 @@ The script infers the data format from the file extension, but the format can also be specified explicitly. | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| zcbor validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| zcbor convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| ``` | ||
| Or invoke its command line executable (if installed via `pip`): | ||
| Or directly from within the repo. | ||
| ```sh | ||
| zcbor validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| zcbor convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| ``` | ||
| Note that since CBOR supports more data types than YAML and JSON, zcbor uses an idiomatic format when converting to/from YAML/JSON. | ||
| This is relevant when handling YAML/JSON conversions of data that uses the unsupported features. | ||
| The following data types are supported by CBOR, but not by YAML (and JSON which is a subset of YAML): | ||
| You can see an example of the conversions in [tests/cases/yaml_compatibility.yaml](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.yaml) and its CDDL file [tests/cases/yaml_compatibility.cddl](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.cddl). | ||
| 1. bytestrings: YAML supports only text strings. In YAML, bytestrings ('<bytestring>') are represented as {"bstr": "<hex-formatted bytestring>"}, or as {"bstr": <any type>} if the CBOR bytestring contains CBOR-formatted data, in which the data is decoded into <any type>. | ||
| 2. map keys other than text string: In YAML, such key value pairs are represented as {"keyval<unique int>": {"key": <key, not text>, "val": <value>}} | ||
| 3. tags: In cbor2, tags are represented by a special type, cbor2.CBORTag. In YAML, these are represented as {"tag": <tag number>, "val": <tagged data>}. | ||
| Importing zcbor in a Python script | ||
@@ -148,3 +162,3 @@ ---------------------------------- | ||
| 1. The format provided by the cbor2, yaml (pyyaml), and json packages. | ||
| 1. The format provided by the [cbor2](https://pypi.org/project/cbor2/), [yaml (PyYAML)](https://pypi.org/project/PyYAML/), and [json](https://docs.python.org/3/library/json.html) packages. | ||
| This is a format where the serialization types (map, list, string, number etc.) are mapped directly to the corresponding Python types. | ||
@@ -156,30 +170,128 @@ This format is common between these packages, which makes translation very simple. | ||
| Making CBOR YAML-/JSON-compatible | ||
| --------------------------------- | ||
| Since CBOR supports more data types than YAML and JSON, zcbor can optionally use a bespoke format when converting to/from YAML/JSON. | ||
| This is controlled with the `--yaml-compatibility` option to `convert` and `validate`. | ||
| This is relevant when handling YAML/JSON conversions of data that uses the unsupported features. | ||
| The following data types are supported by CBOR, but not by YAML (or JSON which is a subset of YAML): | ||
| 1. bytestrings: YAML supports only text strings. In YAML, bytestrings are represented as `{"zcbor_bstr": "<hex-formatted bytestring>"}`, or as `{"zcbor_bstr": <any type>}` if the CBOR bytestring contains CBOR-formatted data, in which the data is decoded into `<any type>`. | ||
| 2. map keys other than text string: In YAML, such key value pairs are represented as `{"zcbor_keyval<unique int>": {"key": <key, not text>, "val": <value>}}`. | ||
| 3. tags: In cbor2, tags are represented by a special type, `cbor2.CBORTag`. In YAML, these are represented as `{"zcbor_tag": <tag number>, "zcbor_tag_val": <tagged data>}`. | ||
| 4. undefined: In cbor2, undefined has its own value `cbor2.types.undefined`. In YAML, undefined is represented as: `["zcbor_undefined"]`. | ||
| You can see an example of the conversions in [tests/cases/yaml_compatibility.yaml](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.yaml) and its CDDL file [tests/cases/yaml_compatibility.cddl](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.cddl). | ||
| Code generation | ||
| =============== | ||
| The generated code consists of: | ||
| - A header file containing typedefs for the types defined in the CDDL, as well as declarations for decoding functions for some types (those specified as entry types). The typedefs are the same for both encoding and decoding. | ||
| - A C file containing all the encoding/decoding code. | ||
| The code is split across multiple functions, and each function contains a single `if` statement which "and"s and "or"s together calls into the cbor libraries or to other generated decoding functions. | ||
| Code generation is invoked with the `zcbor code` command: | ||
| CDDL allows placing restrictions on the members of your data structure. | ||
| Restrictions can be on type, on content (e.g. values/sizes of ints or strings), and repetition (e.g. the number of members in a list). | ||
| The generated code will validate the input (i.e. the structure if encoding, or the payload for decoding), which means that it will check all the restriction set in the CDDL description, and fail if a restriction is broken. | ||
| ```sh | ||
| zcbor code <--decode or --encode or both> -c <CDDL description file(s)> -t <which CDDL type(s) to expose in the API> --output-cmake <path to place the generated CMake file at> | ||
| zcbor code <--decode or --encode or both> -c <CDDL description file(s)> -t <which CDDL type(s) to expose in the API> --oc <path to the generated C file> --oh <path to the generated header file> --oht <path to the generated types header> | ||
| ``` | ||
| The cbor libraries do most of the actual translation and moving of bytes, and the validation of values. | ||
| When you call this, zcbor reads the CDDL files and creates C struct types to match the types described in the CDDL. | ||
| It then creates code that uses the C library to decode CBOR data into the structs, and/or encode CBOR from the data in the structs. | ||
| Finally, it takes the "entry types" (`-t`) and creates a public API function for each of them. | ||
| While doing these things, it will make a number of optimizations, e.g. inlining code for small types and removing ununsed functions. | ||
| It outputs the generated code into header and source files and optionally creates a CMake file to build them. | ||
| There are tests for the code generation in [tests/](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/). | ||
| The tests require [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (if your shell is set up to build Zephyr samples, the tests should also build). | ||
| The `zcbor code` command reads one or more CDDL file(s) and generates some or all of these files: | ||
| - A header file with types (always) | ||
| - A header file with declarations for decoding functions (if `--decode`/`-d` is specified) | ||
| - A C file with decoding functions (if `--decode`/`-d` is specified) | ||
| - A header file with declarations for encoding functions (if `--encode`/`-e` is specified) | ||
| - A C file with encoding functions (if `--encode`/`-e` is specified) | ||
| - A CMake file that creates a library with the generated code and the C library (if `--output-cmake` is specified). | ||
| CDDL allows placing restrictions on the members of your data. | ||
| Restrictions can be on type (int/string/list/bool etc.), on content (e.g. values/sizes of ints or strings), and repetition (e.g. the number of members in a list). | ||
| The generated code will validate the input, which means that it will check all the restriction set in the CDDL description, and fail if a restriction is broken. | ||
| There are tests for the code generation in [tests/decode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/decode) and [tests/encode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/encode). | ||
| The tests require [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (if your system is set up to build Zephyr samples, the tests should also build). | ||
| The generated C code is C++ compatible. | ||
| Build system | ||
| ------------ | ||
| When calling zcbor with the argument `--output-cmake <file path>`, a cmake file will be created at that location. | ||
| The cmake file creates a cmake target and adds the generated and non-generated source files, and the include directories to the header files. | ||
| This cmake file can then be included in your project's `CMakeLists.txt` file, and the target can be linked into your project. | ||
| This is demonstrated in the tests, e.g. at tests/decode/test3_simple/CMakeLists.txt. | ||
| When calling zcbor with the argument `--output-cmake <file path>`, a CMake file will be created at that location. | ||
| The generated CMake file creates a target library and adds the generated and non-generated source files as well as required include directories to it. | ||
| This CMake file can then be included in your project's `CMakeLists.txt` file, and the target can be linked into your project. | ||
| This is demonstrated in the tests, e.g. at [tests/decode/test3_simple/CMakeLists.txt](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/decode/test3_simple/CMakeLists.txt). | ||
| zcbor can be instructed to copy the non-generated sources to the same location as the generated sources with `--copy-sources`. | ||
| Usage Example | ||
| ============= | ||
| There are buildable examples in the [samples](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/samples) directory. | ||
| To see how to use the C library directly, see the (hello_world)[samples/hello_world/src/main.c] sample, or the (pet)[samples/pet/src/main.c] sample (look for calls to functions prefixed with `zcbor_`). | ||
| To see how to use code generation, see the (pet)[samples/pet/src/main.c] sample. | ||
| Look at the (CMakeLists.txt)[samples/pet/CMakeLists.txt] file to see how zcbor is invoked for code generation (and for conversion). | ||
| To see how to do conversion, see the (pet)[samples/pet/CMakeLists.txt] sample. | ||
| Below are some additional examples of how to invoke zcbor for code generation and for converting/validating | ||
| Code generation | ||
| --------------- | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| # or | ||
| zcbor code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| ``` | ||
| Converting | ||
| ---------- | ||
| Here is an example call for converting from YAML to CBOR: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| # or | ||
| zcbor convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| ``` | ||
| Which takes a yaml structure from mypet.yaml, validates it against the Pet type in the CDDL description in pet.cddl, and writes binary CBOR data to mypet.cbor. | ||
| Validating | ||
| ---------- | ||
| Here is an example call for validating a JSON file: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c pet.cddl -t Pet --yaml-compatibility -i mypet.json | ||
| # or | ||
| zcbor validate -c pet.cddl -t Pet --yaml-compatibility -i mypet.json | ||
| ``` | ||
| Which takes the json structure in mypet.json, converts any [yaml-compatible](#making-cbor-yaml-json-compatible) values to their original form, and validates that against the Pet type in the CDDL description in pet.cddl. | ||
| Running tests | ||
| ============= | ||
| The tests for the generated code are based on the Zephyr ztest library. | ||
| These tests can be found in [tests/decode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/decode) and [tests/encode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/encode). | ||
| To set up the environment to run the ztest tests, follow [Zephyr's Getting Started Guide](https://docs.zephyrproject.org/latest/getting_started/index.html), or see the workflow in the [`.github`](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/.github) directory. | ||
| Tests for `convert` and `verify` are implemented with the unittest module. | ||
| These tests can be found in [tests/scripts/test_zcbor.py](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/scripts/test_zcbor.py). | ||
| In this file there are also tests for code style of all python scripts, using the `pycodestyle` library. | ||
| Tests for the docs, samples, etc. can be found in [tests/scripts/test_repo_files.py](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/scripts/test_repo_files.py). | ||
| For running the tests locally, there is [`tests/test.sh`](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/test.sh) which runs all above tests. | ||
| Introduction to CDDL | ||
@@ -234,3 +346,3 @@ ==================== | ||
| See [test3_simple](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/decode/test3_simple/) for CDDL example code. | ||
| See [pet.cddl](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/pet.cddl) for CDDL example code. | ||
@@ -257,3 +369,3 @@ | ||
| Major types `pint`, `nint`, `tag`, and `prim` elements have no payload, only _Value_. | ||
| Major types `pint` (0), `nint` (1), `tag` (6), and `simple` (7) elements have no payload, only _Value_. | ||
@@ -263,8 +375,10 @@ * `pint`: Interpret the _Value_ as a positive integer. | ||
| * `tag`: The _Value_ says something about the next non-tag element. | ||
| See the [CBOR tag documentation](See https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) for details. | ||
| * `prim`: Different _Additional info_ mean different things: | ||
| * 20: `false` | ||
| * 21: `true` | ||
| * 22: `null` | ||
| * 23: `undefined` | ||
| See the [CBOR tag documentation](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) for details. | ||
| * `simple`: Different _Additional info_ mean different things: | ||
| * 0-19: Unassigned simple values. | ||
| * 20: `false` simple value | ||
| * 21: `true` simple value | ||
| * 22: `null` simple value | ||
| * 23: `undefined` simple value | ||
| * 24: Interpret the _Value_ as a 1 byte simple value. These simple values are currently unassigned. | ||
| * 25: Interpret the _Value_ as an IEEE 754 float16. | ||
@@ -275,3 +389,3 @@ * 26: Interpret the _Value_ as an IEEE 754 float32. | ||
| For `bstr`, `tstr`, `list`, and `map`, the _Value_ describes the length of the _Payload_. | ||
| For `bstr` (2), `tstr` (3), `list` (4), and `map` (5), the _Value_ describes the length of the _Payload_. | ||
| For `bstr` and `tstr`, the length is in bytes, for `list`, the length is in number of elements, and for `map`, the length is in number of key/value element pairs. | ||
@@ -281,124 +395,25 @@ | ||
| If a `list` or `map` has _Additional info_ 31, it is "indefinite-length", which means it has an "unknown" number of elements. | ||
| Instead, its end is marked by a `prim` with _Additional info_ 31 (byte value 0xFF). | ||
| Instead, its end is marked by a `simple` with _Additional info_ 31 (byte value 0xFF). | ||
| Usage Example | ||
| ============= | ||
| Code generation | ||
| --------------- | ||
| History | ||
| ======= | ||
| This example is is taken from [test3_simple](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/decode/test3_simple/). | ||
| zcbor (then "cddl-gen") was initially conceived as a code generation project. | ||
| It was inspired by the need to securely decode the complex manifest data structures in the [IETF SUIT specification](https://datatracker.ietf.org/doc/draft-ietf-suit-manifest/). | ||
| This is reflected in the fact that there are multiple zcbor tests that use the CDDL and examples from various revisions of that specification. | ||
| Decoding/deserializing data securely requires doing some quite repetitive checks on each data element, to be sure that you are not decoding gibberish. | ||
| This is where code generation could pull a lot of weight. | ||
| Later it was discovered that the CBOR library that was designed to used by generated code could be useful by itself. | ||
| The script was also expanded so it could directly manipulate CBOR data. | ||
| Since CBOR, YAML, and JSON are all represented in roughly the same way internally in Python, it was easy to expand that data manipulation to support YAML and JSON. | ||
| If your CDDL file contains the following code: | ||
| Some places where zcbor is currently used: | ||
| - [MCUboot's serial recovery mechanism](https://github.com/mcu-tools/mcuboot/blob/main/boot/boot_serial/src/boot_serial.c) | ||
| - [Zephyr's mcumgr](https://github.com/zephyrproject-rtos/zephyr/blob/main/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c) | ||
| - [Zephyr's LwM2M SenML](https://github.com/zephyrproject-rtos/zephyr/blob/main/subsys/net/lib/lwm2m/lwm2m_rw_senml_cbor.c) | ||
| - [nRF Connect SDK's full modem update mechanism](https://github.com/nrfconnect/sdk-nrf/blob/main/subsys/mgmt/fmfu/src/fmfu_mgmt.c) | ||
| - [nRF Connect SDK's nrf_rpc](https://github.com/nrfconnect/sdk-nrfxlib/blob/main/nrf_rpc/nrf_rpc_cbor.c) | ||
| ```cddl | ||
| Timestamp = bstr .size 8 | ||
| ; Comments are denoted with a semicolon | ||
| Pet = [ | ||
| name: [ +tstr ], | ||
| birthday: Timestamp, | ||
| species: (cat: 1) / (dog: 2) / (other: 3), | ||
| ] | ||
| ``` | ||
| Call the Python script: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| # or | ||
| zcbor code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| ``` | ||
| And use the generated code with | ||
| ```c | ||
| #include <pet_decode.h> /* The name of the header file is taken from the name of | ||
| the cddl file, but can also be specifiec when calling | ||
| the script. */ | ||
| /* ... */ | ||
| /* The following type and function refer to the Pet type in the CDDL, which | ||
| * has been specified as an --entry-types (-t) when invoking zcbor. */ | ||
| Pet_t pet; | ||
| size_t decode_len; | ||
| bool success = cbor_decode_Pet(input, sizeof(input), &pet, &decode_len); | ||
| ``` | ||
| The process is the same for encoding, except: | ||
| - Change `-d` to `-e` when invoking zcbor | ||
| - Input parameters become output parameters and vice versa in the code: | ||
| ```c | ||
| #include <pet_encode.h> /* The name of the header file is taken from the name of | ||
| the cddl file, but can also be specifiec when calling | ||
| the script. */ | ||
| /* ... */ | ||
| /* The following type and function refer to the Pet type in the CDDL, which | ||
| * has been specified as an --entry-types (-t) when invoking zcbor. */ | ||
| Pet_t pet = { /* Initialize with desired data. */ }; | ||
| uint8_t output[100]; /* 100 is an example. Must be large enough for data to fit. */ | ||
| size_t out_len; | ||
| bool success = cbor_encode_Pet(output, sizeof(output), &pet, &out_len); | ||
| ``` | ||
| CBOR decoding/encoding library | ||
| ------------------------------ | ||
| For encoding: | ||
| ```c | ||
| #include <zcbor_encode.h> | ||
| uint8_t payload[100]; | ||
| zcbor_state_t state; | ||
| zcbor_new_state(&state, 1, payload, sizeof(payload), 0); | ||
| res = res && zcbor_list_start_encode(&state, 0); | ||
| res = res && zcbor_tstr_put(&state, "first"); | ||
| res = res && zcbor_tstr_put(&state, "second"); | ||
| res = res && zcbor_list_end_encode(&state, 0); | ||
| uint8_t timestamp[8] = {1, 2, 3, 4, 5, 6, 7, 8}; | ||
| struct zcbor_string timestamp_str = { | ||
| .value = timestamp, | ||
| .len = sizeof(timestamp), | ||
| }; | ||
| res = res && zcbor_bstr_encode(&state, ×tamp_str); | ||
| res = res && zcbor_uint32_put(&state, 2 /* dog */); | ||
| res = res && zcbor_list_end_encode(&state, 0); | ||
| ``` | ||
| Converting | ||
| ---------- | ||
| Here is an example call for converting from YAML to CBOR: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| # or | ||
| zcbor convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| ``` | ||
| Which takes a yaml structure from mypet.yaml, validates it against the Pet type in the CDDL description in pet.cddl, and writes binary CBOR data to mypet.cbor. | ||
| See the tests in <zcbor base>/tests/ for examples of using the python module | ||
| Running tests | ||
| ============= | ||
| The tests for the generated code are based on Zephyr ztests. | ||
| Tests for the conversion functions in the script are implemented with the unittest module. | ||
| There are also test.sh scripts to quickly run all tests. | ||
| [`tests/test.sh`](https://github.com/NordicSemiconductor/zcbor/blob/0.6.0/tests/test.sh) runs all tests, including python tests in [`tests/scripts`](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/scripts). | ||
| These tests are dependent upon the `pycodestyle` package from `pip`. | ||
| Run these scripts with no arguments. | ||
| To set up the environment to run the ztest tests, follow [Zephyr's Getting Started Guide](https://docs.zephyrproject.org/latest/getting_started/index.html), or see the workflow in the [`.github`](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/.github) directory. | ||
| Command line documentation | ||
@@ -413,3 +428,3 @@ ========================== | ||
| ``` | ||
| usage: zcbor [-h] {code,validate,convert} ... | ||
| usage: zcbor [-h] [--version] {code,validate,convert} ... | ||
@@ -424,2 +439,3 @@ Parse a CDDL file and validate/convert between YAML, JSON, and CBOR. Can also | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
@@ -432,3 +448,3 @@ ``` | ||
| ``` | ||
| usage: zcbor code [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| usage: zcbor code [-h] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] [--output-c OUTPUT_C] | ||
@@ -440,2 +456,3 @@ [--output-h OUTPUT_H] [--output-h-types OUTPUT_H_TYPES] | ||
| [--include-prefix INCLUDE_PREFIX] [-s] | ||
| [--file-header FILE_HEADER] | ||
@@ -459,3 +476,2 @@ Parse a CDDL file and produce C code that validates and xcodes CBOR. | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -538,2 +554,5 @@ multiple files is equivalent to concatenating them. | ||
| union members. | ||
| --file-header FILE_HEADER | ||
| Header to be included in the comment at the top of | ||
| generated C files, e.g. copyright. | ||
@@ -546,5 +565,6 @@ ``` | ||
| ``` | ||
| usage: zcbor validate [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] -i INPUT | ||
| usage: zcbor validate [-h] -c CDDL [--no-prelude] [-v] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE | ||
| [--default-max-qty DEFAULT_MAX_QTY] | ||
| [--yaml-compatibility] | ||
@@ -556,3 +576,2 @@ Read CBOR, YAML, or JSON data from file or stdin and validate it against a | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -565,7 +584,2 @@ multiple files is equivalent to concatenating them. | ||
| generating code. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| -i INPUT, --input INPUT | ||
@@ -582,2 +596,15 @@ Input data file. The option --input-as specifies how | ||
| as. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| --yaml-compatibility Whether to convert CBOR-only values to YAML-compatible | ||
| ones (when converting from CBOR), or vice versa (when | ||
| converting to CBOR). When this is enabled, all CBOR | ||
| data is guaranteed to convert into YAML/JSON. JSON and | ||
| YAML do not support all data types that CBOR/CDDL | ||
| supports. bytestrings (BSTR), tags, undefined, and | ||
| maps with non-text keys need special handling. See the | ||
| zcbor README for more information. | ||
@@ -590,7 +617,9 @@ ``` | ||
| ``` | ||
| usage: zcbor convert [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE -o | ||
| OUTPUT [--output-as {yaml,json,cbor,cborhex,c_code}] | ||
| usage: zcbor convert [-h] -c CDDL [--no-prelude] [-v] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE | ||
| [--default-max-qty DEFAULT_MAX_QTY] | ||
| [--yaml-compatibility] -o OUTPUT | ||
| [--output-as {yaml,json,cbor,cborhex,c_code}] | ||
| [--c-code-var-name C_CODE_VAR_NAME] | ||
| [--c-code-columns C_CODE_COLUMNS] | ||
@@ -600,21 +629,6 @@ Parse a CDDL file and validate/convert between CBOR and YAML/JSON. The script | ||
| conforms to the CDDL description. The script fails if the data does not | ||
| conform. 'zcbor validate' can be used if only validate is needed. JSON and | ||
| YAML do not support all data types that CBOR/CDDL supports. bytestrings | ||
| (BSTR), tags, and maps with non-text keys need special handling: All strings | ||
| in JSON/YAML are text strings. If a BSTR is needed, use a dict with a single | ||
| entry, with "bstr" as the key, and the byte string (as a hex string) as the | ||
| value, e.g. {"bstr": "0123456789abcdef"}. The value can also be another type, | ||
| e.g. which will be interpreted as a BSTR with the given value as contents (in | ||
| cddl: 'bstr .cbor SomeType'). E.g. {"bstr": ["first element", 2, [3]]} Dicts | ||
| in JSON/YAML only support text strings for keys, so if a dict needs other | ||
| types of keys, encapsulate the key and value into a dict (n is an arbitrary | ||
| integer): e.g. {"name": "foo", "keyvaln": {"key": 123, "val": "bar"}} which | ||
| will conform to the CDDL {tstr => tstr, int => tstr}. Tags are specified by a | ||
| dict with two elements, e.g. {"tag": 1234, "value": ["tagged string within | ||
| list"]} 'undefined' is specified as a list with a single text entry: | ||
| "zcbor_undefined". | ||
| conform. 'zcbor validate' can be used if only validate is needed. | ||
| options: | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -627,7 +641,2 @@ multiple files is equivalent to concatenating them. | ||
| generating code. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| -i INPUT, --input INPUT | ||
@@ -644,2 +653,15 @@ Input data file. The option --input-as specifies how | ||
| as. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| --yaml-compatibility Whether to convert CBOR-only values to YAML-compatible | ||
| ones (when converting from CBOR), or vice versa (when | ||
| converting to CBOR). When this is enabled, all CBOR | ||
| data is guaranteed to convert into YAML/JSON. JSON and | ||
| YAML do not support all data types that CBOR/CDDL | ||
| supports. bytestrings (BSTR), tags, undefined, and | ||
| maps with non-text keys need special handling. See the | ||
| zcbor README for more information. | ||
| -o OUTPUT, --output OUTPUT | ||
@@ -657,3 +679,8 @@ Output data file. The option --output-as specifies how | ||
| files. | ||
| --c-code-columns C_CODE_COLUMNS | ||
| Only relevant together with '--output-as c_code' or .c | ||
| files. The number of bytes per line in the variable | ||
| instantiation. If omitted, the entire declaration is a | ||
| single line. | ||
| ``` |
+254
-227
| zcbor | ||
| ===== | ||
| zcbor is a low footprint [CBOR](https://en.wikipedia.org/wiki/CBOR) library in the C language that comes with a schema-driven script tool that can validate your data, or even generate code for you. | ||
| Aside from the script, the CBOR library is a standalone library which is tailored for use in microcontrollers. | ||
| zcbor is a low footprint [CBOR](https://en.wikipedia.org/wiki/CBOR) library in the C language (C++ compatible), tailored for use in microcontrollers. | ||
| It comes with a schema-driven script tool that can validate your data, or even generate code. | ||
| The schema language (CDDL) allows creating very advanced and detailed schemas. | ||
| The validation/conversion part of the script works with YAML and JSON data, in addition to CBOR. | ||
| The validation and conversion part of the tool works with YAML and JSON data, in addition to CBOR. | ||
| It can for example validate a YAML file against a schema and convert it into CBOR. | ||
| The code generation part of the tool generates C code based on the given schema. | ||
| The generated code performs CBOR encoding and decoding using the C library, while also validating the data against all the rules in the schema. | ||
| The schema language used by zcbor is CDDL (Consise Data Definition Language) which is a powerful human-readable data description language defined in [IETF RFC 8610](https://datatracker.ietf.org/doc/rfc8610/). | ||
| zcbor was previously called "cddl-gen". | ||
| Features | ||
@@ -20,18 +22,49 @@ ======== | ||
| - Python script and module: | ||
| - C code: | ||
| - As a low-footprint CBOR decoding/encoding library similar to TinyCBOR/QCBOR/NanoCBOR. The library can be used independently of the Python script. ([More information](#cbor-decodingencoding-library)) | ||
| - To generate C code (using the Python script) for validating and decoding or encoding CBOR, for use in optimized or constrained environments, such as microcontrollers. ([More information](#code-generation)) | ||
| - Python script and module ([More information](#python-script-and-module)): | ||
| - Validate a YAML/JSON file and translate it into CBOR e.g. for transmission. | ||
| - Validate a YAML/JSON/CBOR file before processing it with some other tool | ||
| - Decode and validate incoming CBOR data into human-readable YAML/JSON. | ||
| - As part of a python script that processes YAML/JSON/CBOR files. zcbor is compatible with PyYAML and can additionally provide validation and/or easier inspection via named tuples. | ||
| - C code: | ||
| - Generate C code for validating and decoding or encoding CBOR, for use in optimized or constrained environments, such as microcontrollers. | ||
| - Provide a low-footprint CBOR decoding/encoding library similar to TinyCBOR/QCBOR/NanoCBOR. | ||
| - As part of a python script that processes YAML/JSON/CBOR files. | ||
| - Uses the same internal representation used by the PyYAML/json/cbor2 libraries. | ||
| - Do validation against a CDDL schema. | ||
| - Create a read-only representation via named tuples (with names taken from the CDDL schema). | ||
| Getting started | ||
| =============== | ||
| There are samples in the [samples](samples) directory that demonstrate different ways to use zcbor, both the script tool and the C code. | ||
| 1. The [hello_world sample](samples/hello_world/README.md) is a minimum examples of encoding and decoding using the C library. | ||
| 2. The [pet sample](samples/pet/README.md) shows a how to use the C library together with generated code, and how to use the script tool to do code generation and data conversion. | ||
| The [tests](tests) also demonstrate how to use zcbor in different ways. The [encoding](tests/encode), [decoding](tests/decode), and [unit](tests/unit) tests run using [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (the samples do not use Zephyr). | ||
| Should I use code generation or the library directly? | ||
| ----------------------------------------------------- | ||
| The benefit of using code generation is greater for decoding than encoding. | ||
| This is because decoding is generally more complex than encoding, since when decoding you have to gracefully handle all possible payloads. | ||
| The code generation will provide a number of checks that are tedious to write manually. | ||
| These checks ensure that the payload is well-formed. | ||
| CBOR decoding/encoding library | ||
| ============================== | ||
| The CBOR library found at [headers](include) and [source](src) is used by the generated code, but can also be used directly. | ||
| To use it, instantiate a `zcbor_state_t` object, which is most easily done using the `zcbor_new_*_state()` functions or the `ZCBOR_STATE_*()` macros. | ||
| The CBOR library can be found in [include/](include) and [src/](src) and can be used directly, by including the files in your project. | ||
| If using zcbor with Zephyr, the library will be available when the [CONFIG_ZCBOR](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_ZCBOR) config is enabled. | ||
| The library is also used by generated code. See the the [Code generation](#code-generation) section for more info about code generation. | ||
| The C library is C++ compatible. | ||
| The zcbor state object | ||
| ---------------------- | ||
| To do encoding or decoding with the library, instantiate a `zcbor_state_t` object, which is most easily done using the `ZCBOR_STATE_*()` macros, look below or in the [hello_world](samples/hello_world/src/main.c) sample for example code. | ||
| The `elem_count` member refers to the number of encoded objects in the current list or map. | ||
@@ -41,31 +74,15 @@ `elem_count` starts again when entering a nested list or map, and is restored when exiting. | ||
| `elem_count` is one reason for needing "backup" states (the other is to allow rollback of the payload). | ||
| You need a number of backups corresponding to the maximum number of nested levels in your data. | ||
| Backups are needed for _decoding_ if there are any lists, maps, or CBOR-encoded strings (`zcbor_bstr_*_decode`) in the data. | ||
| Backups are needed for _encoding_ if there are any lists or maps *and* you are using canonical encoding (`ZCBOR_CANONICAL`), or when using the `zcbor_bstr_*_encode` functions. | ||
| Backups are needed for encoding if you are using canonical encoding (`ZCBOR_CANONICAL`), or using the `bstrx_cbor_*` functions. | ||
| Backups are needed for decoding if there are any lists, maps, or CBOR-encoded strings in the data. | ||
| Note that the benefits of using the library directly is greater for encoding than for decoding. | ||
| For decoding, the code generation will provide a number of checks that are tedious to write manually, and easy to forget. | ||
| ```c | ||
| /** The number of states must be at least equal to one more than the maximum | ||
| * nested depth of the data. | ||
| */ | ||
| zcbor_state_t states[n]; | ||
| /** Initialize a decoding state (could include an array of backup states). | ||
| * After calling this, decode_state[0] is ready to be used with the decoding APIs. */ | ||
| ZCBOR_STATE_D(decode_state, n, payload, payload_len, elem_count); | ||
| /** Initialize the states. After calling this, states[0] is ready to be used | ||
| * with the encoding/decoding APIs. | ||
| * elem_count must be the maximum expected number of top-level elements when | ||
| * decoding (1 if the data is wrapped in a list). | ||
| * When encoding, elem_count must be 0. | ||
| */ | ||
| zcbor_new_state(states, n, payload, payload_len, elem_count); | ||
| /** Alternatively, use one of the following convenience macros. */ | ||
| ZCBOR_STATE_D(decode_state, n, payload, payload_len, elem_count); | ||
| /** Initialize an encoding state (could include an array of backup states). | ||
| * After calling this, encode_state[0] is ready to be used with the encoding APIs. */ | ||
| ZCBOR_STATE_E(encode_state, n, payload, payload_len, 0); | ||
| ``` | ||
| The CBOR libraries assume little-endianness by default, but you can define ZCBOR_BIG_ENDIAN to change this. | ||
| Configuration | ||
@@ -76,2 +93,3 @@ ------------- | ||
| These configuration options can be enabled by adding them as compile definitions to the build. | ||
| If using zcbor with Zephyr, use the [Kconfig options](https://github.com/zephyrproject-rtos/zephyr/blob/main/modules/zcbor/Kconfig) instead. | ||
@@ -84,3 +102,3 @@ Name | Description | ||
| `ZCBOR_STOP_ON_ERROR` | Enable the `stop_on_error` functionality. This makes all functions abort their execution if called when an error has already happened. | ||
| `ZCBOR_BIG_ENDIAN` | All decoded values are returned as big-endian. | ||
| `ZCBOR_BIG_ENDIAN` | All decoded values are returned as big-endian. The default is little-endian. | ||
@@ -91,5 +109,2 @@ | ||
| Invoking zcbor.py from the command line | ||
| --------------------------------------- | ||
| The zcbor.py script can directly read CBOR, YAML, or JSON data and validate it against a CDDL description. | ||
@@ -99,2 +114,7 @@ It can also freely convert the data between CBOR/YAML/JSON. | ||
| Invoking zcbor.py from the command line | ||
| --------------------------------------- | ||
| zcbor.py can be installed via [`pip`](https://pypi.org/project/zcbor/), or alternatively invoked directly from its location in this repo. | ||
| Following are some generalized examples for validating, and for converting (which also validates) data from the command line. | ||
@@ -105,21 +125,15 @@ The script infers the data format from the file extension, but the format can also be specified explicitly. | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| zcbor validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| zcbor convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| ``` | ||
| Or invoke its command line executable (if installed via `pip`): | ||
| Or directly from within the repo. | ||
| ```sh | ||
| zcbor validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| zcbor convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| ``` | ||
| Note that since CBOR supports more data types than YAML and JSON, zcbor uses an idiomatic format when converting to/from YAML/JSON. | ||
| This is relevant when handling YAML/JSON conversions of data that uses the unsupported features. | ||
| The following data types are supported by CBOR, but not by YAML (and JSON which is a subset of YAML): | ||
| You can see an example of the conversions in [tests/cases/yaml_compatibility.yaml](tests/cases/yaml_compatibility.yaml) and its CDDL file [tests/cases/yaml_compatibility.cddl](tests/cases/yaml_compatibility.cddl). | ||
| 1. bytestrings: YAML supports only text strings. In YAML, bytestrings ('<bytestring>') are represented as {"bstr": "<hex-formatted bytestring>"}, or as {"bstr": <any type>} if the CBOR bytestring contains CBOR-formatted data, in which the data is decoded into <any type>. | ||
| 2. map keys other than text string: In YAML, such key value pairs are represented as {"keyval<unique int>": {"key": <key, not text>, "val": <value>}} | ||
| 3. tags: In cbor2, tags are represented by a special type, cbor2.CBORTag. In YAML, these are represented as {"tag": <tag number>, "val": <tagged data>}. | ||
| Importing zcbor in a Python script | ||
@@ -132,3 +146,3 @@ ---------------------------------- | ||
| 1. The format provided by the cbor2, yaml (pyyaml), and json packages. | ||
| 1. The format provided by the [cbor2](https://pypi.org/project/cbor2/), [yaml (PyYAML)](https://pypi.org/project/PyYAML/), and [json](https://docs.python.org/3/library/json.html) packages. | ||
| This is a format where the serialization types (map, list, string, number etc.) are mapped directly to the corresponding Python types. | ||
@@ -140,30 +154,128 @@ This format is common between these packages, which makes translation very simple. | ||
| Making CBOR YAML-/JSON-compatible | ||
| --------------------------------- | ||
| Since CBOR supports more data types than YAML and JSON, zcbor can optionally use a bespoke format when converting to/from YAML/JSON. | ||
| This is controlled with the `--yaml-compatibility` option to `convert` and `validate`. | ||
| This is relevant when handling YAML/JSON conversions of data that uses the unsupported features. | ||
| The following data types are supported by CBOR, but not by YAML (or JSON which is a subset of YAML): | ||
| 1. bytestrings: YAML supports only text strings. In YAML, bytestrings are represented as `{"zcbor_bstr": "<hex-formatted bytestring>"}`, or as `{"zcbor_bstr": <any type>}` if the CBOR bytestring contains CBOR-formatted data, in which the data is decoded into `<any type>`. | ||
| 2. map keys other than text string: In YAML, such key value pairs are represented as `{"zcbor_keyval<unique int>": {"key": <key, not text>, "val": <value>}}`. | ||
| 3. tags: In cbor2, tags are represented by a special type, `cbor2.CBORTag`. In YAML, these are represented as `{"zcbor_tag": <tag number>, "zcbor_tag_val": <tagged data>}`. | ||
| 4. undefined: In cbor2, undefined has its own value `cbor2.types.undefined`. In YAML, undefined is represented as: `["zcbor_undefined"]`. | ||
| You can see an example of the conversions in [tests/cases/yaml_compatibility.yaml](tests/cases/yaml_compatibility.yaml) and its CDDL file [tests/cases/yaml_compatibility.cddl](tests/cases/yaml_compatibility.cddl). | ||
| Code generation | ||
| =============== | ||
| The generated code consists of: | ||
| - A header file containing typedefs for the types defined in the CDDL, as well as declarations for decoding functions for some types (those specified as entry types). The typedefs are the same for both encoding and decoding. | ||
| - A C file containing all the encoding/decoding code. | ||
| The code is split across multiple functions, and each function contains a single `if` statement which "and"s and "or"s together calls into the cbor libraries or to other generated decoding functions. | ||
| Code generation is invoked with the `zcbor code` command: | ||
| CDDL allows placing restrictions on the members of your data structure. | ||
| Restrictions can be on type, on content (e.g. values/sizes of ints or strings), and repetition (e.g. the number of members in a list). | ||
| The generated code will validate the input (i.e. the structure if encoding, or the payload for decoding), which means that it will check all the restriction set in the CDDL description, and fail if a restriction is broken. | ||
| ```sh | ||
| zcbor code <--decode or --encode or both> -c <CDDL description file(s)> -t <which CDDL type(s) to expose in the API> --output-cmake <path to place the generated CMake file at> | ||
| zcbor code <--decode or --encode or both> -c <CDDL description file(s)> -t <which CDDL type(s) to expose in the API> --oc <path to the generated C file> --oh <path to the generated header file> --oht <path to the generated types header> | ||
| ``` | ||
| The cbor libraries do most of the actual translation and moving of bytes, and the validation of values. | ||
| When you call this, zcbor reads the CDDL files and creates C struct types to match the types described in the CDDL. | ||
| It then creates code that uses the C library to decode CBOR data into the structs, and/or encode CBOR from the data in the structs. | ||
| Finally, it takes the "entry types" (`-t`) and creates a public API function for each of them. | ||
| While doing these things, it will make a number of optimizations, e.g. inlining code for small types and removing ununsed functions. | ||
| It outputs the generated code into header and source files and optionally creates a CMake file to build them. | ||
| There are tests for the code generation in [tests/](tests/). | ||
| The tests require [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (if your shell is set up to build Zephyr samples, the tests should also build). | ||
| The `zcbor code` command reads one or more CDDL file(s) and generates some or all of these files: | ||
| - A header file with types (always) | ||
| - A header file with declarations for decoding functions (if `--decode`/`-d` is specified) | ||
| - A C file with decoding functions (if `--decode`/`-d` is specified) | ||
| - A header file with declarations for encoding functions (if `--encode`/`-e` is specified) | ||
| - A C file with encoding functions (if `--encode`/`-e` is specified) | ||
| - A CMake file that creates a library with the generated code and the C library (if `--output-cmake` is specified). | ||
| CDDL allows placing restrictions on the members of your data. | ||
| Restrictions can be on type (int/string/list/bool etc.), on content (e.g. values/sizes of ints or strings), and repetition (e.g. the number of members in a list). | ||
| The generated code will validate the input, which means that it will check all the restriction set in the CDDL description, and fail if a restriction is broken. | ||
| There are tests for the code generation in [tests/decode](tests/decode) and [tests/encode](tests/encode). | ||
| The tests require [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (if your system is set up to build Zephyr samples, the tests should also build). | ||
| The generated C code is C++ compatible. | ||
| Build system | ||
| ------------ | ||
| When calling zcbor with the argument `--output-cmake <file path>`, a cmake file will be created at that location. | ||
| The cmake file creates a cmake target and adds the generated and non-generated source files, and the include directories to the header files. | ||
| This cmake file can then be included in your project's `CMakeLists.txt` file, and the target can be linked into your project. | ||
| This is demonstrated in the tests, e.g. at tests/decode/test3_simple/CMakeLists.txt. | ||
| When calling zcbor with the argument `--output-cmake <file path>`, a CMake file will be created at that location. | ||
| The generated CMake file creates a target library and adds the generated and non-generated source files as well as required include directories to it. | ||
| This CMake file can then be included in your project's `CMakeLists.txt` file, and the target can be linked into your project. | ||
| This is demonstrated in the tests, e.g. at [tests/decode/test3_simple/CMakeLists.txt](tests/decode/test3_simple/CMakeLists.txt). | ||
| zcbor can be instructed to copy the non-generated sources to the same location as the generated sources with `--copy-sources`. | ||
| Usage Example | ||
| ============= | ||
| There are buildable examples in the [samples](samples) directory. | ||
| To see how to use the C library directly, see the (hello_world)[samples/hello_world/src/main.c] sample, or the (pet)[samples/pet/src/main.c] sample (look for calls to functions prefixed with `zcbor_`). | ||
| To see how to use code generation, see the (pet)[samples/pet/src/main.c] sample. | ||
| Look at the (CMakeLists.txt)[samples/pet/CMakeLists.txt] file to see how zcbor is invoked for code generation (and for conversion). | ||
| To see how to do conversion, see the (pet)[samples/pet/CMakeLists.txt] sample. | ||
| Below are some additional examples of how to invoke zcbor for code generation and for converting/validating | ||
| Code generation | ||
| --------------- | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| # or | ||
| zcbor code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| ``` | ||
| Converting | ||
| ---------- | ||
| Here is an example call for converting from YAML to CBOR: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| # or | ||
| zcbor convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| ``` | ||
| Which takes a yaml structure from mypet.yaml, validates it against the Pet type in the CDDL description in pet.cddl, and writes binary CBOR data to mypet.cbor. | ||
| Validating | ||
| ---------- | ||
| Here is an example call for validating a JSON file: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c pet.cddl -t Pet --yaml-compatibility -i mypet.json | ||
| # or | ||
| zcbor validate -c pet.cddl -t Pet --yaml-compatibility -i mypet.json | ||
| ``` | ||
| Which takes the json structure in mypet.json, converts any [yaml-compatible](#making-cbor-yaml-json-compatible) values to their original form, and validates that against the Pet type in the CDDL description in pet.cddl. | ||
| Running tests | ||
| ============= | ||
| The tests for the generated code are based on the Zephyr ztest library. | ||
| These tests can be found in [tests/decode](tests/decode) and [tests/encode](tests/encode). | ||
| To set up the environment to run the ztest tests, follow [Zephyr's Getting Started Guide](https://docs.zephyrproject.org/latest/getting_started/index.html), or see the workflow in the [`.github`](.github) directory. | ||
| Tests for `convert` and `verify` are implemented with the unittest module. | ||
| These tests can be found in [tests/scripts/test_zcbor.py](tests/scripts/test_zcbor.py). | ||
| In this file there are also tests for code style of all python scripts, using the `pycodestyle` library. | ||
| Tests for the docs, samples, etc. can be found in [tests/scripts/test_repo_files.py](tests/scripts/test_repo_files.py). | ||
| For running the tests locally, there is [`tests/test.sh`](tests/test.sh) which runs all above tests. | ||
| Introduction to CDDL | ||
@@ -218,3 +330,3 @@ ==================== | ||
| See [test3_simple](tests/decode/test3_simple/) for CDDL example code. | ||
| See [pet.cddl](tests/cases/pet.cddl) for CDDL example code. | ||
@@ -241,3 +353,3 @@ | ||
| Major types `pint`, `nint`, `tag`, and `prim` elements have no payload, only _Value_. | ||
| Major types `pint` (0), `nint` (1), `tag` (6), and `simple` (7) elements have no payload, only _Value_. | ||
@@ -247,8 +359,10 @@ * `pint`: Interpret the _Value_ as a positive integer. | ||
| * `tag`: The _Value_ says something about the next non-tag element. | ||
| See the [CBOR tag documentation](See https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) for details. | ||
| * `prim`: Different _Additional info_ mean different things: | ||
| * 20: `false` | ||
| * 21: `true` | ||
| * 22: `null` | ||
| * 23: `undefined` | ||
| See the [CBOR tag documentation](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) for details. | ||
| * `simple`: Different _Additional info_ mean different things: | ||
| * 0-19: Unassigned simple values. | ||
| * 20: `false` simple value | ||
| * 21: `true` simple value | ||
| * 22: `null` simple value | ||
| * 23: `undefined` simple value | ||
| * 24: Interpret the _Value_ as a 1 byte simple value. These simple values are currently unassigned. | ||
| * 25: Interpret the _Value_ as an IEEE 754 float16. | ||
@@ -259,3 +373,3 @@ * 26: Interpret the _Value_ as an IEEE 754 float32. | ||
| For `bstr`, `tstr`, `list`, and `map`, the _Value_ describes the length of the _Payload_. | ||
| For `bstr` (2), `tstr` (3), `list` (4), and `map` (5), the _Value_ describes the length of the _Payload_. | ||
| For `bstr` and `tstr`, the length is in bytes, for `list`, the length is in number of elements, and for `map`, the length is in number of key/value element pairs. | ||
@@ -265,124 +379,25 @@ | ||
| If a `list` or `map` has _Additional info_ 31, it is "indefinite-length", which means it has an "unknown" number of elements. | ||
| Instead, its end is marked by a `prim` with _Additional info_ 31 (byte value 0xFF). | ||
| Instead, its end is marked by a `simple` with _Additional info_ 31 (byte value 0xFF). | ||
| Usage Example | ||
| ============= | ||
| Code generation | ||
| --------------- | ||
| History | ||
| ======= | ||
| This example is is taken from [test3_simple](tests/decode/test3_simple/). | ||
| zcbor (then "cddl-gen") was initially conceived as a code generation project. | ||
| It was inspired by the need to securely decode the complex manifest data structures in the [IETF SUIT specification](https://datatracker.ietf.org/doc/draft-ietf-suit-manifest/). | ||
| This is reflected in the fact that there are multiple zcbor tests that use the CDDL and examples from various revisions of that specification. | ||
| Decoding/deserializing data securely requires doing some quite repetitive checks on each data element, to be sure that you are not decoding gibberish. | ||
| This is where code generation could pull a lot of weight. | ||
| Later it was discovered that the CBOR library that was designed to used by generated code could be useful by itself. | ||
| The script was also expanded so it could directly manipulate CBOR data. | ||
| Since CBOR, YAML, and JSON are all represented in roughly the same way internally in Python, it was easy to expand that data manipulation to support YAML and JSON. | ||
| If your CDDL file contains the following code: | ||
| Some places where zcbor is currently used: | ||
| - [MCUboot's serial recovery mechanism](https://github.com/mcu-tools/mcuboot/blob/main/boot/boot_serial/src/boot_serial.c) | ||
| - [Zephyr's mcumgr](https://github.com/zephyrproject-rtos/zephyr/blob/main/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c) | ||
| - [Zephyr's LwM2M SenML](https://github.com/zephyrproject-rtos/zephyr/blob/main/subsys/net/lib/lwm2m/lwm2m_rw_senml_cbor.c) | ||
| - [nRF Connect SDK's full modem update mechanism](https://github.com/nrfconnect/sdk-nrf/blob/main/subsys/mgmt/fmfu/src/fmfu_mgmt.c) | ||
| - [nRF Connect SDK's nrf_rpc](https://github.com/nrfconnect/sdk-nrfxlib/blob/main/nrf_rpc/nrf_rpc_cbor.c) | ||
| ```cddl | ||
| Timestamp = bstr .size 8 | ||
| ; Comments are denoted with a semicolon | ||
| Pet = [ | ||
| name: [ +tstr ], | ||
| birthday: Timestamp, | ||
| species: (cat: 1) / (dog: 2) / (other: 3), | ||
| ] | ||
| ``` | ||
| Call the Python script: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| # or | ||
| zcbor code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| ``` | ||
| And use the generated code with | ||
| ```c | ||
| #include <pet_decode.h> /* The name of the header file is taken from the name of | ||
| the cddl file, but can also be specifiec when calling | ||
| the script. */ | ||
| /* ... */ | ||
| /* The following type and function refer to the Pet type in the CDDL, which | ||
| * has been specified as an --entry-types (-t) when invoking zcbor. */ | ||
| Pet_t pet; | ||
| size_t decode_len; | ||
| bool success = cbor_decode_Pet(input, sizeof(input), &pet, &decode_len); | ||
| ``` | ||
| The process is the same for encoding, except: | ||
| - Change `-d` to `-e` when invoking zcbor | ||
| - Input parameters become output parameters and vice versa in the code: | ||
| ```c | ||
| #include <pet_encode.h> /* The name of the header file is taken from the name of | ||
| the cddl file, but can also be specifiec when calling | ||
| the script. */ | ||
| /* ... */ | ||
| /* The following type and function refer to the Pet type in the CDDL, which | ||
| * has been specified as an --entry-types (-t) when invoking zcbor. */ | ||
| Pet_t pet = { /* Initialize with desired data. */ }; | ||
| uint8_t output[100]; /* 100 is an example. Must be large enough for data to fit. */ | ||
| size_t out_len; | ||
| bool success = cbor_encode_Pet(output, sizeof(output), &pet, &out_len); | ||
| ``` | ||
| CBOR decoding/encoding library | ||
| ------------------------------ | ||
| For encoding: | ||
| ```c | ||
| #include <zcbor_encode.h> | ||
| uint8_t payload[100]; | ||
| zcbor_state_t state; | ||
| zcbor_new_state(&state, 1, payload, sizeof(payload), 0); | ||
| res = res && zcbor_list_start_encode(&state, 0); | ||
| res = res && zcbor_tstr_put(&state, "first"); | ||
| res = res && zcbor_tstr_put(&state, "second"); | ||
| res = res && zcbor_list_end_encode(&state, 0); | ||
| uint8_t timestamp[8] = {1, 2, 3, 4, 5, 6, 7, 8}; | ||
| struct zcbor_string timestamp_str = { | ||
| .value = timestamp, | ||
| .len = sizeof(timestamp), | ||
| }; | ||
| res = res && zcbor_bstr_encode(&state, ×tamp_str); | ||
| res = res && zcbor_uint32_put(&state, 2 /* dog */); | ||
| res = res && zcbor_list_end_encode(&state, 0); | ||
| ``` | ||
| Converting | ||
| ---------- | ||
| Here is an example call for converting from YAML to CBOR: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| # or | ||
| zcbor convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| ``` | ||
| Which takes a yaml structure from mypet.yaml, validates it against the Pet type in the CDDL description in pet.cddl, and writes binary CBOR data to mypet.cbor. | ||
| See the tests in <zcbor base>/tests/ for examples of using the python module | ||
| Running tests | ||
| ============= | ||
| The tests for the generated code are based on Zephyr ztests. | ||
| Tests for the conversion functions in the script are implemented with the unittest module. | ||
| There are also test.sh scripts to quickly run all tests. | ||
| [`tests/test.sh`](tests/test.sh) runs all tests, including python tests in [`tests/scripts`](tests/scripts). | ||
| These tests are dependent upon the `pycodestyle` package from `pip`. | ||
| Run these scripts with no arguments. | ||
| To set up the environment to run the ztest tests, follow [Zephyr's Getting Started Guide](https://docs.zephyrproject.org/latest/getting_started/index.html), or see the workflow in the [`.github`](.github) directory. | ||
| Command line documentation | ||
@@ -397,3 +412,3 @@ ========================== | ||
| ``` | ||
| usage: zcbor [-h] {code,validate,convert} ... | ||
| usage: zcbor [-h] [--version] {code,validate,convert} ... | ||
@@ -408,2 +423,3 @@ Parse a CDDL file and validate/convert between YAML, JSON, and CBOR. Can also | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
@@ -416,3 +432,3 @@ ``` | ||
| ``` | ||
| usage: zcbor code [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| usage: zcbor code [-h] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] [--output-c OUTPUT_C] | ||
@@ -424,2 +440,3 @@ [--output-h OUTPUT_H] [--output-h-types OUTPUT_H_TYPES] | ||
| [--include-prefix INCLUDE_PREFIX] [-s] | ||
| [--file-header FILE_HEADER] | ||
@@ -443,3 +460,2 @@ Parse a CDDL file and produce C code that validates and xcodes CBOR. | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -522,2 +538,5 @@ multiple files is equivalent to concatenating them. | ||
| union members. | ||
| --file-header FILE_HEADER | ||
| Header to be included in the comment at the top of | ||
| generated C files, e.g. copyright. | ||
@@ -530,5 +549,6 @@ ``` | ||
| ``` | ||
| usage: zcbor validate [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] -i INPUT | ||
| usage: zcbor validate [-h] -c CDDL [--no-prelude] [-v] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE | ||
| [--default-max-qty DEFAULT_MAX_QTY] | ||
| [--yaml-compatibility] | ||
@@ -540,3 +560,2 @@ Read CBOR, YAML, or JSON data from file or stdin and validate it against a | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -549,7 +568,2 @@ multiple files is equivalent to concatenating them. | ||
| generating code. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| -i INPUT, --input INPUT | ||
@@ -566,2 +580,15 @@ Input data file. The option --input-as specifies how | ||
| as. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| --yaml-compatibility Whether to convert CBOR-only values to YAML-compatible | ||
| ones (when converting from CBOR), or vice versa (when | ||
| converting to CBOR). When this is enabled, all CBOR | ||
| data is guaranteed to convert into YAML/JSON. JSON and | ||
| YAML do not support all data types that CBOR/CDDL | ||
| supports. bytestrings (BSTR), tags, undefined, and | ||
| maps with non-text keys need special handling. See the | ||
| zcbor README for more information. | ||
@@ -574,7 +601,9 @@ ``` | ||
| ``` | ||
| usage: zcbor convert [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE -o | ||
| OUTPUT [--output-as {yaml,json,cbor,cborhex,c_code}] | ||
| usage: zcbor convert [-h] -c CDDL [--no-prelude] [-v] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE | ||
| [--default-max-qty DEFAULT_MAX_QTY] | ||
| [--yaml-compatibility] -o OUTPUT | ||
| [--output-as {yaml,json,cbor,cborhex,c_code}] | ||
| [--c-code-var-name C_CODE_VAR_NAME] | ||
| [--c-code-columns C_CODE_COLUMNS] | ||
@@ -584,21 +613,6 @@ Parse a CDDL file and validate/convert between CBOR and YAML/JSON. The script | ||
| conforms to the CDDL description. The script fails if the data does not | ||
| conform. 'zcbor validate' can be used if only validate is needed. JSON and | ||
| YAML do not support all data types that CBOR/CDDL supports. bytestrings | ||
| (BSTR), tags, and maps with non-text keys need special handling: All strings | ||
| in JSON/YAML are text strings. If a BSTR is needed, use a dict with a single | ||
| entry, with "bstr" as the key, and the byte string (as a hex string) as the | ||
| value, e.g. {"bstr": "0123456789abcdef"}. The value can also be another type, | ||
| e.g. which will be interpreted as a BSTR with the given value as contents (in | ||
| cddl: 'bstr .cbor SomeType'). E.g. {"bstr": ["first element", 2, [3]]} Dicts | ||
| in JSON/YAML only support text strings for keys, so if a dict needs other | ||
| types of keys, encapsulate the key and value into a dict (n is an arbitrary | ||
| integer): e.g. {"name": "foo", "keyvaln": {"key": 123, "val": "bar"}} which | ||
| will conform to the CDDL {tstr => tstr, int => tstr}. Tags are specified by a | ||
| dict with two elements, e.g. {"tag": 1234, "value": ["tagged string within | ||
| list"]} 'undefined' is specified as a list with a single text entry: | ||
| "zcbor_undefined". | ||
| conform. 'zcbor validate' can be used if only validate is needed. | ||
| options: | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -611,7 +625,2 @@ multiple files is equivalent to concatenating them. | ||
| generating code. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| -i INPUT, --input INPUT | ||
@@ -628,2 +637,15 @@ Input data file. The option --input-as specifies how | ||
| as. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| --yaml-compatibility Whether to convert CBOR-only values to YAML-compatible | ||
| ones (when converting from CBOR), or vice versa (when | ||
| converting to CBOR). When this is enabled, all CBOR | ||
| data is guaranteed to convert into YAML/JSON. JSON and | ||
| YAML do not support all data types that CBOR/CDDL | ||
| supports. bytestrings (BSTR), tags, undefined, and | ||
| maps with non-text keys need special handling. See the | ||
| zcbor README for more information. | ||
| -o OUTPUT, --output OUTPUT | ||
@@ -641,3 +663,8 @@ Output data file. The option --output-as specifies how | ||
| files. | ||
| --c-code-columns C_CODE_COLUMNS | ||
| Only relevant together with '--output-as c_code' or .c | ||
| files. The number of bytes per line in the variable | ||
| instantiation. If omitted, the entire declaration is a | ||
| single line. | ||
| ``` |
+1
-1
@@ -89,4 +89,4 @@ #!/usr/bin/env python3 | ||
| ("include/zcbor_decode.h", "include/zcbor_encode.h", "include/zcbor_common.h", | ||
| "include/zcbor_debug.h")), | ||
| "include/zcbor_tags.h", "include/zcbor_debug.h")), | ||
| ("lib/zcbor/src", ("src/zcbor_decode.c", "src/zcbor_encode.c", "src/zcbor_common.c"))], | ||
| ) |
+47
-13
@@ -19,3 +19,3 @@ /* | ||
| bool zcbor_new_backup(zcbor_state_t *state, uint_fast32_t new_elem_count) | ||
| bool zcbor_new_backup(zcbor_state_t *state, size_t new_elem_count) | ||
| { | ||
@@ -35,3 +35,3 @@ ZCBOR_CHECK_ERROR(); | ||
| * backup would be unused. */ | ||
| uint_fast32_t i = (state->constant_state->current_backup) - 1; | ||
| size_t i = (state->constant_state->current_backup) - 1; | ||
@@ -48,6 +48,6 @@ memcpy(&state->constant_state->backup_list[i], state, | ||
| bool zcbor_process_backup(zcbor_state_t *state, uint32_t flags, | ||
| uint_fast32_t max_elem_count) | ||
| size_t max_elem_count) | ||
| { | ||
| const uint8_t *payload = state->payload; | ||
| const uint_fast32_t elem_count = state->elem_count; | ||
| const size_t elem_count = state->elem_count; | ||
@@ -64,3 +64,3 @@ ZCBOR_CHECK_ERROR(); | ||
| * 0th backup would be unused. */ | ||
| uint_fast32_t i = state->constant_state->current_backup - 1; | ||
| size_t i = state->constant_state->current_backup - 1; | ||
@@ -97,3 +97,3 @@ if (!(flags & ZCBOR_FLAG_TRANSFER_PAYLOAD)) { | ||
| if (state->constant_state) { | ||
| for (int i = 0; i < state->constant_state->current_backup; i++) { | ||
| for (unsigned int i = 0; i < state->constant_state->current_backup; i++) { | ||
| state->constant_state->backup_list[i].payload_end = new_payload_end; | ||
@@ -131,4 +131,4 @@ state->constant_state->backup_list[i].payload_moved = true; | ||
| void zcbor_new_state(zcbor_state_t *state_array, uint_fast32_t n_states, | ||
| const uint8_t *payload, size_t payload_len, uint_fast32_t elem_count) | ||
| void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, | ||
| const uint8_t *payload, size_t payload_len, size_t elem_count) | ||
| { | ||
@@ -171,3 +171,3 @@ state_array[0].payload = payload; | ||
| bool zcbor_validate_string_fragments(struct zcbor_string_fragment *fragments, | ||
| uint_fast32_t num_fragments) | ||
| size_t num_fragments) | ||
| { | ||
@@ -180,3 +180,3 @@ size_t total_len = 0; | ||
| for (uint_fast32_t i = 0; i < num_fragments; i++) { | ||
| for (size_t i = 0; i < num_fragments; i++) { | ||
| if (fragments[i].offset != total_len) { | ||
@@ -202,3 +202,3 @@ return false; | ||
| if (num_fragments && (fragments[0].total_len == ZCBOR_STRING_FRAGMENT_UNKNOWN_LENGTH)) { | ||
| for (uint_fast32_t i = 0; i < num_fragments; i++) { | ||
| for (size_t i = 0; i < num_fragments; i++) { | ||
| fragments[i].total_len = total_len; | ||
@@ -212,3 +212,3 @@ } | ||
| bool zcbor_splice_string_fragments(struct zcbor_string_fragment *fragments, | ||
| uint_fast32_t num_fragments, uint8_t *result, size_t *result_len) | ||
| size_t num_fragments, uint8_t *result, size_t *result_len) | ||
| { | ||
@@ -221,3 +221,3 @@ size_t total_len = 0; | ||
| for (uint_fast32_t i = 0; i < num_fragments; i++) { | ||
| for (size_t i = 0; i < num_fragments; i++) { | ||
| if ((total_len > *result_len) | ||
@@ -235,1 +235,35 @@ || (fragments[i].fragment.len > (*result_len - total_len))) { | ||
| } | ||
| bool zcbor_compare_strings(const struct zcbor_string *str1, | ||
| const struct zcbor_string *str2) | ||
| { | ||
| return (str1 != NULL) && (str2 != NULL) | ||
| && (str1->value != NULL) && (str2->value != NULL) && (str1->len == str2->len) | ||
| && (memcmp(str1->value, str2->value, str1->len) == 0); | ||
| } | ||
| size_t zcbor_header_len(size_t num_elems) | ||
| { | ||
| if (num_elems <= ZCBOR_VALUE_IN_HEADER) { | ||
| return 1; | ||
| } else if (num_elems <= 0xFF) { | ||
| return 2; | ||
| } else if (num_elems <= 0xFFFF) { | ||
| return 3; | ||
| } else if (num_elems <= 0xFFFFFFFF) { | ||
| return 5; | ||
| } else { | ||
| return 9; | ||
| } | ||
| } | ||
| bool zcbor_array_at_end(zcbor_state_t *state) | ||
| { | ||
| return ((!state->indefinite_length_array && (state->elem_count == 0)) | ||
| || (state->indefinite_length_array | ||
| && (state->payload < state->payload_end) | ||
| && (*state->payload == 0xFF))); | ||
| } |
+278
-118
@@ -17,3 +17,3 @@ /* | ||
| */ | ||
| static uint_fast32_t additional_len(uint8_t additional) | ||
| static size_t additional_len(uint8_t additional) | ||
| { | ||
@@ -46,2 +46,3 @@ if (ZCBOR_VALUE_IS_1_BYTE <= additional && additional <= ZCBOR_VALUE_IS_8_BYTES) { | ||
| static bool initial_checks(zcbor_state_t *state) | ||
@@ -54,2 +55,3 @@ { | ||
| static bool type_check(zcbor_state_t *state, zcbor_major_type_t exp_major_type) | ||
@@ -68,2 +70,3 @@ { | ||
| #define INITIAL_CHECKS() \ | ||
@@ -83,7 +86,13 @@ do {\ | ||
| static void err_restore(zcbor_state_t *state, int err) | ||
| { | ||
| state->payload = state->payload_bak; | ||
| state->elem_count++; | ||
| zcbor_error(state, err); | ||
| } | ||
| #define ERR_RESTORE(err) \ | ||
| do { \ | ||
| state->payload = state->payload_bak; \ | ||
| state->elem_count++; \ | ||
| ZCBOR_ERR(err); \ | ||
| err_restore(state, err); \ | ||
| ZCBOR_FAIL(); \ | ||
| } while(0) | ||
@@ -98,2 +107,3 @@ | ||
| /** Get a single value. | ||
@@ -118,3 +128,3 @@ * | ||
| static bool value_extract(zcbor_state_t *state, | ||
| void *const result, uint_fast32_t result_len) | ||
| void *const result, size_t result_len) | ||
| { | ||
@@ -142,3 +152,3 @@ zcbor_trace(); | ||
| } else { | ||
| uint_fast32_t len = additional_len(additional); | ||
| size_t len = additional_len(additional); | ||
@@ -153,3 +163,3 @@ FAIL_AND_DECR_IF(len > result_len, ZCBOR_ERR_INT_SIZE); | ||
| #else | ||
| for (uint_fast32_t i = 0; i < len; i++) { | ||
| for (size_t i = 0; i < len; i++) { | ||
| u8_result[i] = (state->payload)[len - i - 1]; | ||
@@ -195,3 +205,3 @@ } | ||
| /* Convert from CBOR's representation by flipping all bits. */ | ||
| for (int i = 0; i < int_size; i++) { | ||
| for (unsigned int i = 0; i < int_size; i++) { | ||
| result_uint8[i] = (uint8_t)~result_uint8[i]; | ||
@@ -217,7 +227,8 @@ } | ||
| bool zcbor_uint32_decode(zcbor_state_t *state, uint32_t *result) | ||
| bool zcbor_uint_decode(zcbor_state_t *state, void *result_uint, size_t uint_size) | ||
| { | ||
| INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_PINT); | ||
| if (!value_extract(state, result, sizeof(*result))) { | ||
| if (!value_extract(state, result_uint, uint_size)) { | ||
| zcbor_print("uint with size %d failed.\r\n", uint_size); | ||
| ZCBOR_FAIL(); | ||
@@ -229,2 +240,8 @@ } | ||
| bool zcbor_uint32_decode(zcbor_state_t *state, uint32_t *result) | ||
| { | ||
| return zcbor_uint_decode(state, result, sizeof(*result)); | ||
| } | ||
| bool zcbor_int32_expect_union(zcbor_state_t *state, int32_t result) | ||
@@ -290,8 +307,3 @@ { | ||
| { | ||
| INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_PINT); | ||
| if (!value_extract(state, result, sizeof(*result))) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| return zcbor_uint_decode(state, result, sizeof(*result)); | ||
| } | ||
@@ -303,3 +315,3 @@ | ||
| { | ||
| return value_extract(state, result, sizeof(size_t)); | ||
| return zcbor_uint_decode(state, result, sizeof(*result)); | ||
| } | ||
@@ -351,11 +363,20 @@ #endif | ||
| static bool str_start_decode_with_overflow_check(zcbor_state_t *state, | ||
| struct zcbor_string *result, zcbor_major_type_t exp_major_type) | ||
| { | ||
| bool res = str_start_decode(state, result, exp_major_type); | ||
| static bool str_overflow_check(zcbor_state_t *state, struct zcbor_string *result) | ||
| { | ||
| if (result->len > (state->payload_end - state->payload)) { | ||
| if (!res) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| /* Casting to size_t is safe since str_start_decode() checks that | ||
| * payload_end is bigger that payload. */ | ||
| if (result->len > (size_t)(state->payload_end - state->payload)) { | ||
| zcbor_print("error: 0x%zu > 0x%zu\r\n", | ||
| result->len, | ||
| (state->payload_end - state->payload)); | ||
| result->len, | ||
| (state->payload_end - state->payload)); | ||
| ERR_RESTORE(ZCBOR_ERR_NO_PAYLOAD); | ||
| } | ||
| return true; | ||
@@ -372,10 +393,6 @@ } | ||
| if(!str_start_decode(state, result, ZCBOR_MAJOR_TYPE_BSTR)) { | ||
| if(!str_start_decode_with_overflow_check(state, result, ZCBOR_MAJOR_TYPE_BSTR)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| if (!str_overflow_check(state, result)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| if (!zcbor_new_backup(state, ZCBOR_MAX_ELEM_COUNT)) { | ||
@@ -481,10 +498,6 @@ FAIL_RESTORE(); | ||
| { | ||
| if (!str_start_decode(state, result, exp_major_type)) { | ||
| if (!str_start_decode_with_overflow_check(state, result, exp_major_type)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| if (!str_overflow_check(state, result)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| state->payload += result->len; | ||
@@ -515,4 +528,3 @@ return true; | ||
| } | ||
| if ((tmp_result.len != result->len) | ||
| || memcmp(result->value, tmp_result.value, tmp_result.len)) { | ||
| if (!zcbor_compare_strings(&tmp_result, result)) { | ||
| ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); | ||
@@ -563,3 +575,3 @@ } | ||
| { | ||
| uint_fast32_t new_elem_count; | ||
| size_t new_elem_count; | ||
| bool indefinite_length_array = false; | ||
@@ -625,3 +637,3 @@ | ||
| { | ||
| uint_fast32_t max_elem_count = 0; | ||
| size_t max_elem_count = 0; | ||
@@ -669,15 +681,30 @@ if (state->indefinite_length_array) { | ||
| static bool primx_expect(zcbor_state_t *state, uint8_t result) | ||
| bool zcbor_simple_decode(zcbor_state_t *state, uint8_t *result) | ||
| { | ||
| uint32_t value; | ||
| INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_SIMPLE); | ||
| INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_PRIM); | ||
| /* Simple values must be 0-23 (additional is 0-23) or 24-255 (additional is 24). | ||
| * Other additional values are not considered simple values. */ | ||
| ZCBOR_ERR_IF(ADDITIONAL(*state->payload) > 24, ZCBOR_ERR_WRONG_TYPE); | ||
| if (!value_extract(state, &value, sizeof(value))) { | ||
| if (!value_extract(state, result, sizeof(*result))) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_simple_expect(zcbor_state_t *state, uint8_t result) | ||
| { | ||
| uint8_t value; | ||
| if (!zcbor_simple_decode(state, &value)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| if (value != result) { | ||
| zcbor_print("simple value %u != %u\r\n", value, result); | ||
| ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); | ||
| } | ||
| return true; | ||
@@ -689,5 +716,28 @@ } | ||
| { | ||
| if (!primx_expect(state, 22)) { | ||
| (void)unused; | ||
| return zcbor_simple_expect(state, 22); | ||
| } | ||
| bool zcbor_undefined_expect(zcbor_state_t *state, void *unused) | ||
| { | ||
| (void)unused; | ||
| return zcbor_simple_expect(state, 23); | ||
| } | ||
| bool zcbor_bool_decode(zcbor_state_t *state, bool *result) | ||
| { | ||
| uint8_t value; | ||
| if (!zcbor_simple_decode(state, &value)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| value -= ZCBOR_BOOL_TO_SIMPLE; | ||
| if (value > 1) { | ||
| ERR_RESTORE(ZCBOR_ERR_WRONG_TYPE); | ||
| } | ||
| *result = value; | ||
| zcbor_print("boolval: %u\r\n", *result); | ||
| return true; | ||
@@ -697,7 +747,25 @@ } | ||
| bool zcbor_undefined_expect(zcbor_state_t *state, void *unused) | ||
| bool zcbor_bool_expect(zcbor_state_t *state, bool result) | ||
| { | ||
| if (!primx_expect(state, 23)) { | ||
| return zcbor_simple_expect(state, (uint8_t)(!!result) + ZCBOR_BOOL_TO_SIMPLE); | ||
| } | ||
| static bool float_check(zcbor_state_t *state, uint8_t additional_val) | ||
| { | ||
| INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_SIMPLE); | ||
| ZCBOR_ERR_IF(ADDITIONAL(*state->payload) != additional_val, ZCBOR_ERR_FLOAT_SIZE); | ||
| return true; | ||
| } | ||
| bool zcbor_float16_bytes_decode(zcbor_state_t *state, uint16_t *result) | ||
| { | ||
| ZCBOR_FAIL_IF(!float_check(state, ZCBOR_VALUE_IS_2_BYTES)); | ||
| if (!value_extract(state, result, sizeof(*result))) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
@@ -707,13 +775,57 @@ } | ||
| bool zcbor_bool_decode(zcbor_state_t *state, bool *result) | ||
| bool zcbor_float16_bytes_expect(zcbor_state_t *state, uint16_t result) | ||
| { | ||
| if (zcbor_bool_expect(state, false)) { | ||
| *result = false; | ||
| } else if (zcbor_bool_expect(state, true)) { | ||
| *result = true; | ||
| } else { | ||
| uint16_t value; | ||
| if (!zcbor_float16_bytes_decode(state, &value)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| if (value != result) { | ||
| ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); | ||
| } | ||
| return true; | ||
| } | ||
| zcbor_print("boolval: %u\r\n", *result); | ||
| /* Float16: */ | ||
| #define F16_SIGN_OFFS 15 /* Bit offset of the sign bit. */ | ||
| #define F16_EXPO_OFFS 10 /* Bit offset of the exponent. */ | ||
| #define F16_EXPO_MSK 0x1F /* Bitmask for the exponent (right shifted by F16_EXPO_OFFS). */ | ||
| #define F16_MANTISSA_MSK 0x3FF /* Bitmask for the mantissa. */ | ||
| #define F16_MIN_EXPO 24 /* Negative exponent of the non-zero float16 value closest to 0 (2^-24) */ | ||
| #define F16_MIN (1.0 / (1 << F16_MIN_EXPO)) /* The non-zero float16 value closest to 0 (2^-24) */ | ||
| #define F16_BIAS 15 /* The exponent bias of normalized float16 values. */ | ||
| /* Float32: */ | ||
| #define F32_SIGN_OFFS 31 /* Bit offset of the sign bit. */ | ||
| #define F32_EXPO_OFFS 23 /* Bit offset of the exponent. */ | ||
| #define F32_EXPO_MSK 0xFF /* Bitmask for the exponent (right shifted by F32_EXPO_OFFS). */ | ||
| #define F32_BIAS 127 /* The exponent bias of normalized float32 values. */ | ||
| bool zcbor_float16_decode(zcbor_state_t *state, float *result) | ||
| { | ||
| uint16_t value16; | ||
| if (!zcbor_float16_bytes_decode(state, &value16)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| uint32_t sign = value16 >> F16_SIGN_OFFS; | ||
| uint32_t expo = (value16 >> F16_EXPO_OFFS) & F16_EXPO_MSK; | ||
| uint32_t mantissa = value16 & F16_MANTISSA_MSK; | ||
| if ((expo == 0) && (mantissa != 0)) { | ||
| /* Subnormal float16 - convert to normalized float32 */ | ||
| *result = ((float)mantissa * (float)F16_MIN) * (sign ? -1 : 1); | ||
| } else { | ||
| /* Normalized / zero / Infinity / NaN */ | ||
| uint32_t new_expo = (expo == 0 /* zero */) ? 0 | ||
| : (expo == F16_EXPO_MSK /* inf/NaN */) ? F32_EXPO_MSK | ||
| : (expo + (F32_BIAS - F16_BIAS)); | ||
| uint32_t value32 = (sign << F32_SIGN_OFFS) | (new_expo << F32_EXPO_OFFS) | ||
| | (mantissa << (F32_EXPO_OFFS - F16_EXPO_OFFS)); | ||
| memcpy(result, &value32, sizeof(*result)); | ||
| } | ||
| return true; | ||
@@ -723,7 +835,12 @@ } | ||
| bool zcbor_bool_expect(zcbor_state_t *state, bool result) | ||
| bool zcbor_float16_expect(zcbor_state_t *state, float result) | ||
| { | ||
| if (!primx_expect(state, (uint8_t)(!!result) + ZCBOR_BOOL_TO_PRIM)) { | ||
| float value; | ||
| if (!zcbor_float16_decode(state, &value)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| if (value != result) { | ||
| ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); | ||
| } | ||
| return true; | ||
@@ -735,4 +852,3 @@ } | ||
| { | ||
| INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_PRIM); | ||
| ZCBOR_ERR_IF(ADDITIONAL(*state->payload) != ZCBOR_VALUE_IS_4_BYTES, ZCBOR_ERR_FLOAT_SIZE); | ||
| ZCBOR_FAIL_IF(!float_check(state, ZCBOR_VALUE_IS_4_BYTES)); | ||
@@ -761,6 +877,29 @@ if (!value_extract(state, result, sizeof(*result))) { | ||
| bool zcbor_float16_32_decode(zcbor_state_t *state, float *result) | ||
| { | ||
| if (zcbor_float16_decode(state, result)) { | ||
| /* Do nothing */ | ||
| } else if (!zcbor_float32_decode(state, result)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_float16_32_expect(zcbor_state_t *state, float result) | ||
| { | ||
| if (zcbor_float16_expect(state, (float)result)) { | ||
| /* Do nothing */ | ||
| } else if (!zcbor_float32_expect(state, result)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_float64_decode(zcbor_state_t *state, double *result) | ||
| { | ||
| INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_PRIM); | ||
| ZCBOR_ERR_IF(ADDITIONAL(*state->payload) != ZCBOR_VALUE_IS_8_BYTES, ZCBOR_ERR_FLOAT_SIZE); | ||
| ZCBOR_FAIL_IF(!float_check(state, ZCBOR_VALUE_IS_8_BYTES)); | ||
@@ -789,3 +928,3 @@ if (!value_extract(state, result, sizeof(*result))) { | ||
| bool zcbor_float_decode(zcbor_state_t *state, double *result) | ||
| bool zcbor_float32_64_decode(zcbor_state_t *state, double *result) | ||
| { | ||
@@ -804,6 +943,36 @@ float float_result; | ||
| bool zcbor_float32_64_expect(zcbor_state_t *state, double result) | ||
| { | ||
| if (zcbor_float64_expect(state, result)) { | ||
| /* Do nothing */ | ||
| } else if (!zcbor_float32_expect(state, (float)result)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_float_decode(zcbor_state_t *state, double *result) | ||
| { | ||
| float float_result; | ||
| if (zcbor_float16_decode(state, &float_result)) { | ||
| *result = (double)float_result; | ||
| } else if (zcbor_float32_decode(state, &float_result)) { | ||
| *result = (double)float_result; | ||
| } else if (!zcbor_float64_decode(state, result)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_float_expect(zcbor_state_t *state, double result) | ||
| { | ||
| if (zcbor_float32_expect(state, (float)result)) { | ||
| if (zcbor_float16_expect(state, (float)result)) { | ||
| /* Do nothing */ | ||
| } else if (zcbor_float32_expect(state, (float)result)) { | ||
| /* Do nothing */ | ||
| } else if (!zcbor_float64_expect(state, result)) { | ||
@@ -821,2 +990,3 @@ ZCBOR_FAIL(); | ||
| "'any' type cannot be returned, only skipped.\r\n"); | ||
| (void)result; | ||
@@ -826,42 +996,22 @@ INITIAL_CHECKS(); | ||
| uint8_t additional = ADDITIONAL(*state->payload); | ||
| uint_fast32_t value; | ||
| uint_fast32_t num_decode; | ||
| uint_fast32_t temp_elem_count; | ||
| uint_fast32_t elem_count_bak = state->elem_count; | ||
| uint8_t const *payload_bak = state->payload; | ||
| uint64_t tag_dummy; | ||
| uint64_t value = 0; /* In case of indefinite_length_array. */ | ||
| zcbor_state_t state_copy; | ||
| payload_bak = state->payload; | ||
| memcpy(&state_copy, state, sizeof(zcbor_state_t)); | ||
| if (!zcbor_multi_decode(0, ZCBOR_LARGE_ELEM_COUNT, &num_decode, | ||
| (zcbor_decoder_t *)zcbor_tag_decode, state, | ||
| (void *)&tag_dummy, 0)) { | ||
| state->elem_count = elem_count_bak; | ||
| state->payload = payload_bak; | ||
| ZCBOR_FAIL(); | ||
| } | ||
| while (major_type == ZCBOR_MAJOR_TYPE_TAG) { | ||
| uint32_t tag_dummy; | ||
| if ((major_type == ZCBOR_MAJOR_TYPE_MAP) || (major_type == ZCBOR_MAJOR_TYPE_LIST)) { | ||
| if (additional == ZCBOR_VALUE_IS_INDEFINITE_LENGTH) { | ||
| ZCBOR_ERR_IF(state->elem_count == 0, ZCBOR_ERR_LOW_ELEM_COUNT); | ||
| state->payload++; | ||
| state->elem_count--; | ||
| temp_elem_count = state->elem_count; | ||
| payload_bak = state->payload; | ||
| state->elem_count = ZCBOR_LARGE_ELEM_COUNT; | ||
| if (!zcbor_multi_decode(0, ZCBOR_LARGE_ELEM_COUNT, &num_decode, | ||
| (zcbor_decoder_t *)zcbor_any_skip, state, | ||
| NULL, 0) | ||
| || (state->payload >= state->payload_end) | ||
| || !(*(state->payload++) == 0xFF)) { | ||
| state->elem_count = elem_count_bak; | ||
| state->payload = payload_bak; | ||
| ZCBOR_FAIL(); | ||
| } | ||
| state->elem_count = temp_elem_count; | ||
| return true; | ||
| if (!zcbor_tag_decode(&state_copy, &tag_dummy)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| ZCBOR_ERR_IF(state_copy.payload >= state_copy.payload_end, ZCBOR_ERR_NO_PAYLOAD); | ||
| major_type = MAJOR_TYPE(*state_copy.payload); | ||
| additional = ADDITIONAL(*state_copy.payload); | ||
| } | ||
| if (!value_extract(state, &value, sizeof(value))) { | ||
| const bool indefinite_length_array = ((additional == ZCBOR_VALUE_IS_INDEFINITE_LENGTH) | ||
| && ((major_type == ZCBOR_MAJOR_TYPE_LIST) || (major_type == ZCBOR_MAJOR_TYPE_MAP))); | ||
| if (!indefinite_length_array && !value_extract(&state_copy, &value, sizeof(value))) { | ||
| /* Can happen because of elem_count (or payload_end) */ | ||
@@ -874,22 +1024,29 @@ ZCBOR_FAIL(); | ||
| case ZCBOR_MAJOR_TYPE_TSTR: | ||
| /* 'value' is the length of the BSTR or TSTR */ | ||
| if (value > (state->payload_end - state->payload)) { | ||
| ZCBOR_ERR(ZCBOR_ERR_NO_PAYLOAD); | ||
| } | ||
| (state->payload) += value; | ||
| /* 'value' is the length of the BSTR or TSTR. | ||
| * The cast to size_t is safe because value_extract() above | ||
| * checks that payload_end is greater than payload. */ | ||
| ZCBOR_ERR_IF( | ||
| value > (uint64_t)(state_copy.payload_end - state_copy.payload), | ||
| ZCBOR_ERR_NO_PAYLOAD); | ||
| (state_copy.payload) += value; | ||
| break; | ||
| case ZCBOR_MAJOR_TYPE_MAP: | ||
| value *= 2; /* Because all members have a key. */ | ||
| /* Fallthrough */ | ||
| ZCBOR_ERR_IF(value > (SIZE_MAX / 2), ZCBOR_ERR_INT_SIZE); | ||
| value *= 2; | ||
| /* fallthrough */ | ||
| case ZCBOR_MAJOR_TYPE_LIST: | ||
| temp_elem_count = state->elem_count; | ||
| state->elem_count = value; | ||
| if (!zcbor_multi_decode(value, value, &num_decode, | ||
| (zcbor_decoder_t *)zcbor_any_skip, state, | ||
| NULL, 0)) { | ||
| state->elem_count = elem_count_bak; | ||
| state->payload = payload_bak; | ||
| if (indefinite_length_array) { | ||
| state_copy.payload++; | ||
| value = ZCBOR_LARGE_ELEM_COUNT; | ||
| } | ||
| state_copy.elem_count = (size_t)value; | ||
| state_copy.indefinite_length_array = indefinite_length_array; | ||
| while (!zcbor_array_at_end(&state_copy)) { | ||
| if (!zcbor_any_skip(&state_copy, NULL)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| } | ||
| if (indefinite_length_array && !array_end_expect(&state_copy)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| state->elem_count = temp_elem_count; | ||
| break; | ||
@@ -901,2 +1058,5 @@ default: | ||
| state->payload = state_copy.payload; | ||
| state->elem_count--; | ||
| return true; | ||
@@ -932,14 +1092,14 @@ } | ||
| bool zcbor_multi_decode(uint_fast32_t min_decode, | ||
| uint_fast32_t max_decode, | ||
| uint_fast32_t *num_decode, | ||
| bool zcbor_multi_decode(size_t min_decode, | ||
| size_t max_decode, | ||
| size_t *num_decode, | ||
| zcbor_decoder_t decoder, | ||
| zcbor_state_t *state, | ||
| void *result, | ||
| uint_fast32_t result_len) | ||
| size_t result_len) | ||
| { | ||
| ZCBOR_CHECK_ERROR(); | ||
| for (uint_fast32_t i = 0; i < max_decode; i++) { | ||
| for (size_t i = 0; i < max_decode; i++) { | ||
| uint8_t const *payload_bak = state->payload; | ||
| uint_fast32_t elem_count_bak = state->elem_count; | ||
| size_t elem_count_bak = state->elem_count; | ||
@@ -962,3 +1122,3 @@ if (!decoder(state, | ||
| bool zcbor_present_decode(uint_fast32_t *present, | ||
| bool zcbor_present_decode(bool *present, | ||
| zcbor_decoder_t decoder, | ||
@@ -968,3 +1128,3 @@ zcbor_state_t *state, | ||
| { | ||
| uint_fast32_t num_decode; | ||
| size_t num_decode; | ||
| bool retval = zcbor_multi_decode(0, 1, &num_decode, decoder, state, result, 0); | ||
@@ -974,3 +1134,3 @@ | ||
| *present = num_decode; | ||
| *present = !!num_decode; | ||
| return retval; | ||
@@ -980,6 +1140,6 @@ } | ||
| void zcbor_new_decode_state(zcbor_state_t *state_array, uint_fast32_t n_states, | ||
| const uint8_t *payload, size_t payload_len, uint_fast32_t elem_count) | ||
| void zcbor_new_decode_state(zcbor_state_t *state_array, size_t n_states, | ||
| const uint8_t *payload, size_t payload_len, size_t elem_count) | ||
| { | ||
| zcbor_new_state(state_array, n_states, payload, payload_len, elem_count); | ||
| } |
+191
-93
@@ -18,3 +18,3 @@ /* | ||
| static uint8_t log2ceil(uint_fast32_t val) | ||
| static uint8_t log2ceil(size_t val) | ||
| { | ||
@@ -36,3 +36,4 @@ switch(val) { | ||
| static uint8_t get_additional(uint_fast32_t len, uint8_t value0) | ||
| static uint8_t get_additional(size_t len, uint8_t value0) | ||
| { | ||
@@ -42,2 +43,3 @@ return len == 0 ? value0 : (uint8_t)(24 + log2ceil(len)); | ||
| static bool encode_header_byte(zcbor_state_t *state, | ||
@@ -56,3 +58,3 @@ zcbor_major_type_t major_type, uint8_t additional) | ||
| static uint_fast32_t get_encoded_len(const void *const result, uint_fast32_t result_len); | ||
| static size_t get_encoded_len(const void *const result, size_t result_len); | ||
@@ -63,6 +65,6 @@ | ||
| static bool value_encode_len(zcbor_state_t *state, zcbor_major_type_t major_type, | ||
| const void *const result, uint_fast32_t result_len) | ||
| const void *const result, size_t result_len) | ||
| { | ||
| uint8_t *u8_result = (uint8_t *)result; | ||
| uint_fast32_t encoded_len = get_encoded_len(result, result_len); | ||
| size_t encoded_len = get_encoded_len(result, result_len); | ||
@@ -95,6 +97,6 @@ if ((state->payload + 1 + encoded_len) > state->payload_end) { | ||
| static uint_fast32_t get_result_len(const void *const input, uint_fast32_t max_result_len) | ||
| static size_t get_result_len(const void *const input, size_t max_result_len) | ||
| { | ||
| uint8_t *u8_result = (uint8_t *)input; | ||
| uint_fast32_t len = max_result_len; | ||
| size_t len = max_result_len; | ||
@@ -116,4 +118,4 @@ for (; len > 0; len--) { | ||
| static const void *get_result(const void *const input, uint_fast32_t max_result_len, | ||
| uint_fast32_t result_len) | ||
| static const void *get_result(const void *const input, size_t max_result_len, | ||
| size_t result_len) | ||
| { | ||
@@ -123,2 +125,4 @@ #ifdef CONFIG_BIG_ENDIAN | ||
| #else | ||
| (void)max_result_len; | ||
| (void)result_len; | ||
| return input; | ||
@@ -129,3 +133,3 @@ #endif | ||
| static uint_fast32_t get_encoded_len(const void *const result, uint_fast32_t result_len) | ||
| static size_t get_encoded_len(const void *const result, size_t result_len) | ||
| { | ||
@@ -142,7 +146,7 @@ const uint8_t *u8_result = (const uint8_t *)result; | ||
| static bool value_encode(zcbor_state_t *state, zcbor_major_type_t major_type, | ||
| const void *const input, uint_fast32_t max_result_len) | ||
| const void *const input, size_t max_result_len) | ||
| { | ||
| zcbor_assert_state(max_result_len != 0, "0-length result not supported.\r\n"); | ||
| uint_fast32_t result_len = get_result_len(input, max_result_len); | ||
| size_t result_len = get_result_len(input, max_result_len); | ||
| const void *const result = get_result(input, max_result_len, result_len); | ||
@@ -174,3 +178,3 @@ | ||
| /* Convert to CBOR's representation by flipping all bits. */ | ||
| for (int i = 0; i < int_size; i++) { | ||
| for (unsigned int i = 0; i < int_size; i++) { | ||
| input_buf[i] = (uint8_t)~input_uint8[i]; | ||
@@ -191,9 +195,13 @@ } | ||
| bool zcbor_int32_encode(zcbor_state_t *state, const int32_t *input) | ||
| bool zcbor_uint_encode(zcbor_state_t *state, const void *input_uint, size_t uint_size) | ||
| { | ||
| return zcbor_int_encode(state, input, sizeof(*input)); | ||
| if (!value_encode(state, ZCBOR_MAJOR_TYPE_PINT, input_uint, uint_size)) { | ||
| zcbor_print("uint with size %d failed.\r\n", uint_size); | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_int64_encode(zcbor_state_t *state, const int64_t *input) | ||
| bool zcbor_int32_encode(zcbor_state_t *state, const int32_t *input) | ||
| { | ||
@@ -204,9 +212,5 @@ return zcbor_int_encode(state, input, sizeof(*input)); | ||
| static bool uint32_encode(zcbor_state_t *state, const uint32_t *input, | ||
| zcbor_major_type_t major_type) | ||
| bool zcbor_int64_encode(zcbor_state_t *state, const int64_t *input) | ||
| { | ||
| if (!value_encode(state, major_type, input, 4)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| return zcbor_int_encode(state, input, sizeof(*input)); | ||
| } | ||
@@ -217,25 +221,9 @@ | ||
| { | ||
| if (!uint32_encode(state, input, ZCBOR_MAJOR_TYPE_PINT)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| return zcbor_uint_encode(state, input, sizeof(*input)); | ||
| } | ||
| static bool uint64_encode(zcbor_state_t *state, const uint64_t *input, | ||
| zcbor_major_type_t major_type) | ||
| { | ||
| if (!value_encode(state, major_type, input, 8)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_uint64_encode(zcbor_state_t *state, const uint64_t *input) | ||
| { | ||
| if (!uint64_encode(state, input, ZCBOR_MAJOR_TYPE_PINT)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| return zcbor_uint_encode(state, input, sizeof(*input)); | ||
| } | ||
@@ -258,3 +246,3 @@ | ||
| { | ||
| return zcbor_uint64_put(state, input); | ||
| return zcbor_uint_encode(state, &input, sizeof(input)); | ||
| } | ||
@@ -265,6 +253,3 @@ | ||
| { | ||
| if (!uint64_encode(state, &input, ZCBOR_MAJOR_TYPE_PINT)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| return zcbor_uint_encode(state, &input, sizeof(input)); | ||
| } | ||
@@ -276,3 +261,3 @@ | ||
| { | ||
| return zcbor_uint64_put(state, input); | ||
| return zcbor_uint_encode(state, &input, sizeof(input)); | ||
| } | ||
@@ -283,3 +268,3 @@ | ||
| { | ||
| return zcbor_size_put(state, *input); | ||
| return zcbor_uint_encode(state, input, sizeof(*input)); | ||
| } | ||
@@ -304,11 +289,2 @@ #endif | ||
| static bool primitive_put(zcbor_state_t *state, uint32_t input) | ||
| { | ||
| if (!uint32_encode(state, &input, ZCBOR_MAJOR_TYPE_PRIM)) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| static size_t remaining_str_len(zcbor_state_t *state) | ||
@@ -332,4 +308,3 @@ { | ||
| /* Encode a dummy header */ | ||
| if (!uint64_encode(state, &max_len, | ||
| ZCBOR_MAJOR_TYPE_BSTR)) { | ||
| if (!value_encode(state, ZCBOR_MAJOR_TYPE_BSTR, &max_len, sizeof(max_len))) { | ||
| ZCBOR_FAIL(); | ||
@@ -371,3 +346,4 @@ } | ||
| { | ||
| if (input->len > (state->payload_end - state->payload)) { | ||
| ZCBOR_CHECK_PAYLOAD(); /* To make the size_t cast below safe. */ | ||
| if (input->len > (size_t)(state->payload_end - state->payload)) { | ||
| ZCBOR_ERR(ZCBOR_ERR_NO_PAYLOAD); | ||
@@ -400,3 +376,3 @@ } | ||
| static bool list_map_start_encode(zcbor_state_t *state, uint_fast32_t max_num, | ||
| static bool list_map_start_encode(zcbor_state_t *state, size_t max_num, | ||
| zcbor_major_type_t major_type) | ||
@@ -415,2 +391,4 @@ { | ||
| #else | ||
| (void)max_num; | ||
| if (!encode_header_byte(state, major_type, ZCBOR_VALUE_IS_INDEFINITE_LENGTH)) { | ||
@@ -424,3 +402,3 @@ ZCBOR_FAIL(); | ||
| bool zcbor_list_start_encode(zcbor_state_t *state, uint_fast32_t max_num) | ||
| bool zcbor_list_start_encode(zcbor_state_t *state, size_t max_num) | ||
| { | ||
@@ -431,3 +409,3 @@ return list_map_start_encode(state, max_num, ZCBOR_MAJOR_TYPE_LIST); | ||
| bool zcbor_map_start_encode(zcbor_state_t *state, uint_fast32_t max_num) | ||
| bool zcbor_map_start_encode(zcbor_state_t *state, size_t max_num) | ||
| { | ||
@@ -439,5 +417,5 @@ return list_map_start_encode(state, max_num, ZCBOR_MAJOR_TYPE_MAP); | ||
| #ifdef ZCBOR_CANONICAL | ||
| static uint_fast32_t get_encoded_len2(const void *const input, uint_fast32_t max_result_len) | ||
| static size_t get_encoded_len2(const void *const input, size_t max_result_len) | ||
| { | ||
| uint_fast32_t result_len = get_result_len(input, max_result_len); | ||
| size_t result_len = get_result_len(input, max_result_len); | ||
| const void *const result = get_result(input, max_result_len, result_len); | ||
@@ -450,7 +428,7 @@ | ||
| static bool list_map_end_encode(zcbor_state_t *state, uint_fast32_t max_num, | ||
| static bool list_map_end_encode(zcbor_state_t *state, size_t max_num, | ||
| zcbor_major_type_t major_type) | ||
| { | ||
| #ifdef ZCBOR_CANONICAL | ||
| uint_fast32_t list_count = ((major_type == ZCBOR_MAJOR_TYPE_LIST) ? | ||
| size_t list_count = ((major_type == ZCBOR_MAJOR_TYPE_LIST) ? | ||
| state->elem_count | ||
@@ -461,4 +439,4 @@ : (state->elem_count / 2)); | ||
| uint_fast32_t max_header_len = get_encoded_len2(&max_num, 4); | ||
| uint_fast32_t header_len = get_encoded_len2(&list_count, 4); | ||
| size_t max_header_len = get_encoded_len2(&max_num, 4); | ||
| size_t header_len = get_encoded_len2(&list_count, 4); | ||
@@ -497,3 +475,5 @@ if (!zcbor_process_backup(state, ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME, 0xFFFFFFFF)) { | ||
| #else | ||
| if (!encode_header_byte(state, ZCBOR_MAJOR_TYPE_PRIM, ZCBOR_VALUE_IS_INDEFINITE_LENGTH)) { | ||
| (void)max_num; | ||
| (void)major_type; | ||
| if (!encode_header_byte(state, ZCBOR_MAJOR_TYPE_SIMPLE, ZCBOR_VALUE_IS_INDEFINITE_LENGTH)) { | ||
| ZCBOR_FAIL(); | ||
@@ -506,3 +486,3 @@ } | ||
| bool zcbor_list_end_encode(zcbor_state_t *state, uint_fast32_t max_num) | ||
| bool zcbor_list_end_encode(zcbor_state_t *state, size_t max_num) | ||
| { | ||
@@ -513,3 +493,3 @@ return list_map_end_encode(state, max_num, ZCBOR_MAJOR_TYPE_LIST); | ||
| bool zcbor_map_end_encode(zcbor_state_t *state, uint_fast32_t max_num) | ||
| bool zcbor_map_end_encode(zcbor_state_t *state, size_t max_num) | ||
| { | ||
@@ -528,2 +508,3 @@ return list_map_end_encode(state, max_num, ZCBOR_MAJOR_TYPE_MAP); | ||
| #endif | ||
| (void)state; | ||
| return true; | ||
@@ -533,6 +514,22 @@ } | ||
| bool zcbor_simple_encode(zcbor_state_t *state, uint8_t *input) | ||
| { | ||
| if (!value_encode(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, sizeof(*input))) { | ||
| zcbor_print("Error encoding %u (0x%p)\r\n", *input, input); | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_simple_put(zcbor_state_t *state, uint8_t input) | ||
| { | ||
| return value_encode(state, ZCBOR_MAJOR_TYPE_SIMPLE, &input, sizeof(input)); | ||
| } | ||
| bool zcbor_nil_put(zcbor_state_t *state, const void *unused) | ||
| { | ||
| (void)unused; | ||
| return primitive_put(state, 22); | ||
| return zcbor_simple_put(state, 22); | ||
| } | ||
@@ -544,3 +541,3 @@ | ||
| (void)unused; | ||
| return primitive_put(state, 23); | ||
| return zcbor_simple_put(state, 23); | ||
| } | ||
@@ -551,6 +548,3 @@ | ||
| { | ||
| if (!primitive_put(state, (uint32_t)(*input + ZCBOR_BOOL_TO_PRIM))) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| return zcbor_bool_put(state, *input); | ||
| } | ||
@@ -561,6 +555,3 @@ | ||
| { | ||
| if (!primitive_put(state, (uint32_t)(input + ZCBOR_BOOL_TO_PRIM))) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| return zcbor_simple_put(state, (!!input + ZCBOR_BOOL_TO_SIMPLE)); | ||
| } | ||
@@ -571,3 +562,3 @@ | ||
| { | ||
| if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_PRIM, input, | ||
| if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, | ||
| sizeof(*input))) { | ||
@@ -589,3 +580,3 @@ ZCBOR_FAIL(); | ||
| { | ||
| if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_PRIM, input, | ||
| if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, | ||
| sizeof(*input))) { | ||
@@ -605,2 +596,108 @@ ZCBOR_FAIL(); | ||
| bool zcbor_float16_encode(zcbor_state_t *state, const float *input) | ||
| { | ||
| return zcbor_float16_put(state, *input); | ||
| } | ||
| /* Float16: */ | ||
| #define F16_SIGN_OFFS 15 /* Bit offset of the sign bit. */ | ||
| #define F16_EXPO_OFFS 10 /* Bit offset of the exponent. */ | ||
| #define F16_EXPO_MSK 0x1F /* Bitmask for the exponent (right shifted by F16_EXPO_OFFS). */ | ||
| #define F16_MANTISSA_MSK 0x3FF /* Bitmask for the mantissa. */ | ||
| #define F16_MAX 65520 /* Lowest float32 value that rounds up to float16 infinity. | ||
| * (65519.996 rounds to 65504) */ | ||
| #define F16_MIN_EXPO 24 /* Negative exponent of the non-zero float16 value closest to 0 (2^-24) */ | ||
| #define F16_MIN (1.0 / (1 << F16_MIN_EXPO)) /* The non-zero float16 value closest to 0 (2^-24) */ | ||
| #define F16_MIN_NORM (1.0 / (1 << 14)) /* The normalized float16 value closest to 0 (2^-14) */ | ||
| #define F16_BIAS 15 /* The exponent bias of normalized float16 values. */ | ||
| /* Float32: */ | ||
| #define F32_SIGN_OFFS 31 /* Bit offset of the sign bit. */ | ||
| #define F32_EXPO_OFFS 23 /* Bit offset of the exponent. */ | ||
| #define F32_EXPO_MSK 0xFF /* Bitmask for the exponent (right shifted by F32_EXPO_OFFS). */ | ||
| #define F32_MANTISSA_MSK 0x7FFFFF /* Bitmask for the mantissa. */ | ||
| #define F32_BIAS 127 /* The exponent bias of normalized float32 values. */ | ||
| /* Rounding: */ | ||
| #define SUBNORM_ROUND_MSK (F32_MANTISSA_MSK | (1 << F32_EXPO_OFFS)) /* mantissa + lsb of expo for | ||
| * tiebreak. */ | ||
| #define SUBNORM_ROUND_BIT_MSK (1 << (F32_EXPO_OFFS - 1)) /* msb of mantissa (0x400000) */ | ||
| #define NORM_ROUND_MSK (F32_MANTISSA_MSK >> (F16_EXPO_OFFS - 1)) /* excess mantissa when going from | ||
| * float32 to float16 + 1 extra bit | ||
| * for tiebreak. */ | ||
| #define NORM_ROUND_BIT_MSK (1 << (F32_EXPO_OFFS - F16_EXPO_OFFS - 1)) /* bit 12 (0x1000) */ | ||
| bool zcbor_float16_put(zcbor_state_t *state, float input) | ||
| { | ||
| uint32_t value32 = *(uint32_t *)&input; | ||
| uint32_t sign = value32 >> F32_SIGN_OFFS; | ||
| uint32_t expo = (value32 >> F32_EXPO_OFFS) & F32_EXPO_MSK; | ||
| uint32_t mantissa = value32 & F32_MANTISSA_MSK; | ||
| uint16_t value16 = (uint16_t)(sign << F16_SIGN_OFFS); | ||
| float abs_input; | ||
| *(uint32_t *)&abs_input = value32 & ~(1 << F32_SIGN_OFFS); | ||
| if (abs_input <= (F16_MIN / 2)) { | ||
| /* 0 or too small for float16. Round down to 0. value16 is already correct. */ | ||
| } else if (abs_input < F16_MIN) { | ||
| /* Round up to 2^(-24) (F16_MIN), has other rounding rules than larger values. */ | ||
| value16 |= 0x0001; | ||
| } else if (abs_input < F16_MIN_NORM) { | ||
| /* Subnormal float16 (normal float32) */ | ||
| uint32_t adjusted_mantissa = | ||
| /* Adjust for the purposes of checking rounding. */ | ||
| /* The lsb of expo is needed for the cases where expo is 103 (minimum). */ | ||
| ((value32 << (expo - (F32_BIAS - F16_MIN_EXPO))) & SUBNORM_ROUND_MSK); | ||
| uint16_t rounding_bit = | ||
| /* "Round to nearest, ties to even". */ | ||
| /* 0x400000 means ties go down towards even. (0xC00000 means ties go up.) */ | ||
| (adjusted_mantissa & SUBNORM_ROUND_BIT_MSK) | ||
| && (adjusted_mantissa != SUBNORM_ROUND_BIT_MSK); | ||
| value16 |= ((uint16_t)(abs_input * (1 << 24)) + rounding_bit); /* expo is 0 */ | ||
| } else if (abs_input < F16_MAX) { | ||
| /* Normal float16 (normal float32) */ | ||
| uint16_t rounding_bit = | ||
| /* Bit 13 of the mantissa represents which way to round, except for the */ | ||
| /* special case where bits 0-12 and 14 are 0. */ | ||
| /* This is because of "Round to nearest, ties to even". */ | ||
| /* 0x1000 means ties go down towards even. (0x3000 means ties go up.) */ | ||
| ((mantissa & NORM_ROUND_BIT_MSK) | ||
| && ((mantissa & NORM_ROUND_MSK) != NORM_ROUND_BIT_MSK)); | ||
| value16 |= (uint16_t)((expo - (F32_BIAS - F16_BIAS)) << F16_EXPO_OFFS); | ||
| value16 |= (uint16_t)(mantissa >> (F32_EXPO_OFFS - F16_EXPO_OFFS)); | ||
| value16 += rounding_bit; /* Might propagate to exponent. */ | ||
| } else if (expo != F32_EXPO_MSK || !mantissa) { | ||
| /* Infinite, or finite normal float32 too large for float16. Round up to inf. */ | ||
| value16 |= (F16_EXPO_MSK << F16_EXPO_OFFS); | ||
| } else { | ||
| /* NaN */ | ||
| /* Preserve msbit of mantissa. */ | ||
| uint16_t new_mantissa = (uint16_t)(mantissa >> (F32_EXPO_OFFS - F16_EXPO_OFFS)); | ||
| value16 |= (F16_EXPO_MSK << F16_EXPO_OFFS) | (new_mantissa ? new_mantissa : 1); | ||
| } | ||
| return zcbor_float16_bytes_put(state, value16); | ||
| } | ||
| bool zcbor_float16_bytes_encode(zcbor_state_t *state, const uint16_t *input) | ||
| { | ||
| if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, | ||
| sizeof(*input))) { | ||
| ZCBOR_FAIL(); | ||
| } | ||
| return true; | ||
| } | ||
| bool zcbor_float16_bytes_put(zcbor_state_t *state, uint16_t input) | ||
| { | ||
| return zcbor_float16_bytes_encode(state, &input); | ||
| } | ||
| bool zcbor_tag_encode(zcbor_state_t *state, uint32_t tag) | ||
@@ -617,9 +714,9 @@ { | ||
| bool zcbor_multi_encode_minmax(uint_fast32_t min_encode, | ||
| uint_fast32_t max_encode, | ||
| const uint_fast32_t *num_encode, | ||
| bool zcbor_multi_encode_minmax(size_t min_encode, | ||
| size_t max_encode, | ||
| const size_t *num_encode, | ||
| zcbor_encoder_t encoder, | ||
| zcbor_state_t *state, | ||
| const void *input, | ||
| uint_fast32_t result_len) | ||
| size_t result_len) | ||
| { | ||
@@ -634,10 +731,11 @@ | ||
| bool zcbor_multi_encode(uint_fast32_t num_encode, | ||
| bool zcbor_multi_encode(size_t num_encode, | ||
| zcbor_encoder_t encoder, | ||
| zcbor_state_t *state, | ||
| const void *input, | ||
| uint_fast32_t result_len) | ||
| size_t result_len) | ||
| { | ||
| ZCBOR_CHECK_ERROR(); | ||
| for (uint_fast32_t i = 0; i < num_encode; i++) { | ||
| for (size_t i = 0; i < num_encode; i++) { | ||
| if (!encoder(state, (const uint8_t *)input + i*result_len)) { | ||
@@ -652,3 +750,3 @@ ZCBOR_FAIL(); | ||
| bool zcbor_present_encode(const uint_fast32_t *present, | ||
| bool zcbor_present_encode(const bool *present, | ||
| zcbor_encoder_t encoder, | ||
@@ -662,6 +760,6 @@ zcbor_state_t *state, | ||
| void zcbor_new_encode_state(zcbor_state_t *state_array, uint_fast32_t n_states, | ||
| uint8_t *payload, size_t payload_len, uint_fast32_t elem_count) | ||
| void zcbor_new_encode_state(zcbor_state_t *state_array, size_t n_states, | ||
| uint8_t *payload, size_t payload_len, size_t elem_count) | ||
| { | ||
| zcbor_new_state(state_array, n_states, payload, payload_len, elem_count); | ||
| } |
+255
-228
| Metadata-Version: 2.1 | ||
| Name: zcbor | ||
| Version: 0.6.0 | ||
| Version: 0.7.0 | ||
| Summary: zcbor | ||
@@ -19,13 +19,15 @@ Home-page: https://github.com/NordicSemiconductor/zcbor | ||
| zcbor is a low footprint [CBOR](https://en.wikipedia.org/wiki/CBOR) library in the C language that comes with a schema-driven script tool that can validate your data, or even generate code for you. | ||
| Aside from the script, the CBOR library is a standalone library which is tailored for use in microcontrollers. | ||
| zcbor is a low footprint [CBOR](https://en.wikipedia.org/wiki/CBOR) library in the C language (C++ compatible), tailored for use in microcontrollers. | ||
| It comes with a schema-driven script tool that can validate your data, or even generate code. | ||
| The schema language (CDDL) allows creating very advanced and detailed schemas. | ||
| The validation/conversion part of the script works with YAML and JSON data, in addition to CBOR. | ||
| The validation and conversion part of the tool works with YAML and JSON data, in addition to CBOR. | ||
| It can for example validate a YAML file against a schema and convert it into CBOR. | ||
| The code generation part of the tool generates C code based on the given schema. | ||
| The generated code performs CBOR encoding and decoding using the C library, while also validating the data against all the rules in the schema. | ||
| The schema language used by zcbor is CDDL (Consise Data Definition Language) which is a powerful human-readable data description language defined in [IETF RFC 8610](https://datatracker.ietf.org/doc/rfc8610/). | ||
| zcbor was previously called "cddl-gen". | ||
| Features | ||
@@ -36,18 +38,49 @@ ======== | ||
| - Python script and module: | ||
| - C code: | ||
| - As a low-footprint CBOR decoding/encoding library similar to TinyCBOR/QCBOR/NanoCBOR. The library can be used independently of the Python script. ([More information](#cbor-decodingencoding-library)) | ||
| - To generate C code (using the Python script) for validating and decoding or encoding CBOR, for use in optimized or constrained environments, such as microcontrollers. ([More information](#code-generation)) | ||
| - Python script and module ([More information](#python-script-and-module)): | ||
| - Validate a YAML/JSON file and translate it into CBOR e.g. for transmission. | ||
| - Validate a YAML/JSON/CBOR file before processing it with some other tool | ||
| - Decode and validate incoming CBOR data into human-readable YAML/JSON. | ||
| - As part of a python script that processes YAML/JSON/CBOR files. zcbor is compatible with PyYAML and can additionally provide validation and/or easier inspection via named tuples. | ||
| - C code: | ||
| - Generate C code for validating and decoding or encoding CBOR, for use in optimized or constrained environments, such as microcontrollers. | ||
| - Provide a low-footprint CBOR decoding/encoding library similar to TinyCBOR/QCBOR/NanoCBOR. | ||
| - As part of a python script that processes YAML/JSON/CBOR files. | ||
| - Uses the same internal representation used by the PyYAML/json/cbor2 libraries. | ||
| - Do validation against a CDDL schema. | ||
| - Create a read-only representation via named tuples (with names taken from the CDDL schema). | ||
| Getting started | ||
| =============== | ||
| There are samples in the [samples](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/samples) directory that demonstrate different ways to use zcbor, both the script tool and the C code. | ||
| 1. The [hello_world sample](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/samples/hello_world/README.md) is a minimum examples of encoding and decoding using the C library. | ||
| 2. The [pet sample](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/samples/pet/README.md) shows a how to use the C library together with generated code, and how to use the script tool to do code generation and data conversion. | ||
| The [tests](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests) also demonstrate how to use zcbor in different ways. The [encoding](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/encode), [decoding](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/decode), and [unit](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/unit) tests run using [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (the samples do not use Zephyr). | ||
| Should I use code generation or the library directly? | ||
| ----------------------------------------------------- | ||
| The benefit of using code generation is greater for decoding than encoding. | ||
| This is because decoding is generally more complex than encoding, since when decoding you have to gracefully handle all possible payloads. | ||
| The code generation will provide a number of checks that are tedious to write manually. | ||
| These checks ensure that the payload is well-formed. | ||
| CBOR decoding/encoding library | ||
| ============================== | ||
| The CBOR library found at [headers](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/include) and [source](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/src) is used by the generated code, but can also be used directly. | ||
| To use it, instantiate a `zcbor_state_t` object, which is most easily done using the `zcbor_new_*_state()` functions or the `ZCBOR_STATE_*()` macros. | ||
| The CBOR library can be found in [include/](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/include) and [src/](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/src) and can be used directly, by including the files in your project. | ||
| If using zcbor with Zephyr, the library will be available when the [CONFIG_ZCBOR](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_ZCBOR) config is enabled. | ||
| The library is also used by generated code. See the the [Code generation](#code-generation) section for more info about code generation. | ||
| The C library is C++ compatible. | ||
| The zcbor state object | ||
| ---------------------- | ||
| To do encoding or decoding with the library, instantiate a `zcbor_state_t` object, which is most easily done using the `ZCBOR_STATE_*()` macros, look below or in the [hello_world](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/samples/hello_world/src/main.c) sample for example code. | ||
| The `elem_count` member refers to the number of encoded objects in the current list or map. | ||
@@ -57,31 +90,15 @@ `elem_count` starts again when entering a nested list or map, and is restored when exiting. | ||
| `elem_count` is one reason for needing "backup" states (the other is to allow rollback of the payload). | ||
| You need a number of backups corresponding to the maximum number of nested levels in your data. | ||
| Backups are needed for _decoding_ if there are any lists, maps, or CBOR-encoded strings (`zcbor_bstr_*_decode`) in the data. | ||
| Backups are needed for _encoding_ if there are any lists or maps *and* you are using canonical encoding (`ZCBOR_CANONICAL`), or when using the `zcbor_bstr_*_encode` functions. | ||
| Backups are needed for encoding if you are using canonical encoding (`ZCBOR_CANONICAL`), or using the `bstrx_cbor_*` functions. | ||
| Backups are needed for decoding if there are any lists, maps, or CBOR-encoded strings in the data. | ||
| Note that the benefits of using the library directly is greater for encoding than for decoding. | ||
| For decoding, the code generation will provide a number of checks that are tedious to write manually, and easy to forget. | ||
| ```c | ||
| /** The number of states must be at least equal to one more than the maximum | ||
| * nested depth of the data. | ||
| */ | ||
| zcbor_state_t states[n]; | ||
| /** Initialize a decoding state (could include an array of backup states). | ||
| * After calling this, decode_state[0] is ready to be used with the decoding APIs. */ | ||
| ZCBOR_STATE_D(decode_state, n, payload, payload_len, elem_count); | ||
| /** Initialize the states. After calling this, states[0] is ready to be used | ||
| * with the encoding/decoding APIs. | ||
| * elem_count must be the maximum expected number of top-level elements when | ||
| * decoding (1 if the data is wrapped in a list). | ||
| * When encoding, elem_count must be 0. | ||
| */ | ||
| zcbor_new_state(states, n, payload, payload_len, elem_count); | ||
| /** Alternatively, use one of the following convenience macros. */ | ||
| ZCBOR_STATE_D(decode_state, n, payload, payload_len, elem_count); | ||
| /** Initialize an encoding state (could include an array of backup states). | ||
| * After calling this, encode_state[0] is ready to be used with the encoding APIs. */ | ||
| ZCBOR_STATE_E(encode_state, n, payload, payload_len, 0); | ||
| ``` | ||
| The CBOR libraries assume little-endianness by default, but you can define ZCBOR_BIG_ENDIAN to change this. | ||
| Configuration | ||
@@ -92,2 +109,3 @@ ------------- | ||
| These configuration options can be enabled by adding them as compile definitions to the build. | ||
| If using zcbor with Zephyr, use the [Kconfig options](https://github.com/zephyrproject-rtos/zephyr/blob/main/modules/zcbor/Kconfig) instead. | ||
@@ -100,3 +118,3 @@ Name | Description | ||
| `ZCBOR_STOP_ON_ERROR` | Enable the `stop_on_error` functionality. This makes all functions abort their execution if called when an error has already happened. | ||
| `ZCBOR_BIG_ENDIAN` | All decoded values are returned as big-endian. | ||
| `ZCBOR_BIG_ENDIAN` | All decoded values are returned as big-endian. The default is little-endian. | ||
@@ -107,5 +125,2 @@ | ||
| Invoking zcbor.py from the command line | ||
| --------------------------------------- | ||
| The zcbor.py script can directly read CBOR, YAML, or JSON data and validate it against a CDDL description. | ||
@@ -115,2 +130,7 @@ It can also freely convert the data between CBOR/YAML/JSON. | ||
| Invoking zcbor.py from the command line | ||
| --------------------------------------- | ||
| zcbor.py can be installed via [`pip`](https://pypi.org/project/zcbor/), or alternatively invoked directly from its location in this repo. | ||
| Following are some generalized examples for validating, and for converting (which also validates) data from the command line. | ||
@@ -121,21 +141,15 @@ The script infers the data format from the file extension, but the format can also be specified explicitly. | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| zcbor validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| zcbor convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| ``` | ||
| Or invoke its command line executable (if installed via `pip`): | ||
| Or directly from within the repo. | ||
| ```sh | ||
| zcbor validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| zcbor convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c <CDDL description file> -t <which CDDL type to expect> -i <input data file> -o <output data file> | ||
| ``` | ||
| Note that since CBOR supports more data types than YAML and JSON, zcbor uses an idiomatic format when converting to/from YAML/JSON. | ||
| This is relevant when handling YAML/JSON conversions of data that uses the unsupported features. | ||
| The following data types are supported by CBOR, but not by YAML (and JSON which is a subset of YAML): | ||
| You can see an example of the conversions in [tests/cases/yaml_compatibility.yaml](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.yaml) and its CDDL file [tests/cases/yaml_compatibility.cddl](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.cddl). | ||
| 1. bytestrings: YAML supports only text strings. In YAML, bytestrings ('<bytestring>') are represented as {"bstr": "<hex-formatted bytestring>"}, or as {"bstr": <any type>} if the CBOR bytestring contains CBOR-formatted data, in which the data is decoded into <any type>. | ||
| 2. map keys other than text string: In YAML, such key value pairs are represented as {"keyval<unique int>": {"key": <key, not text>, "val": <value>}} | ||
| 3. tags: In cbor2, tags are represented by a special type, cbor2.CBORTag. In YAML, these are represented as {"tag": <tag number>, "val": <tagged data>}. | ||
| Importing zcbor in a Python script | ||
@@ -148,3 +162,3 @@ ---------------------------------- | ||
| 1. The format provided by the cbor2, yaml (pyyaml), and json packages. | ||
| 1. The format provided by the [cbor2](https://pypi.org/project/cbor2/), [yaml (PyYAML)](https://pypi.org/project/PyYAML/), and [json](https://docs.python.org/3/library/json.html) packages. | ||
| This is a format where the serialization types (map, list, string, number etc.) are mapped directly to the corresponding Python types. | ||
@@ -156,30 +170,128 @@ This format is common between these packages, which makes translation very simple. | ||
| Making CBOR YAML-/JSON-compatible | ||
| --------------------------------- | ||
| Since CBOR supports more data types than YAML and JSON, zcbor can optionally use a bespoke format when converting to/from YAML/JSON. | ||
| This is controlled with the `--yaml-compatibility` option to `convert` and `validate`. | ||
| This is relevant when handling YAML/JSON conversions of data that uses the unsupported features. | ||
| The following data types are supported by CBOR, but not by YAML (or JSON which is a subset of YAML): | ||
| 1. bytestrings: YAML supports only text strings. In YAML, bytestrings are represented as `{"zcbor_bstr": "<hex-formatted bytestring>"}`, or as `{"zcbor_bstr": <any type>}` if the CBOR bytestring contains CBOR-formatted data, in which the data is decoded into `<any type>`. | ||
| 2. map keys other than text string: In YAML, such key value pairs are represented as `{"zcbor_keyval<unique int>": {"key": <key, not text>, "val": <value>}}`. | ||
| 3. tags: In cbor2, tags are represented by a special type, `cbor2.CBORTag`. In YAML, these are represented as `{"zcbor_tag": <tag number>, "zcbor_tag_val": <tagged data>}`. | ||
| 4. undefined: In cbor2, undefined has its own value `cbor2.types.undefined`. In YAML, undefined is represented as: `["zcbor_undefined"]`. | ||
| You can see an example of the conversions in [tests/cases/yaml_compatibility.yaml](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.yaml) and its CDDL file [tests/cases/yaml_compatibility.cddl](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/yaml_compatibility.cddl). | ||
| Code generation | ||
| =============== | ||
| The generated code consists of: | ||
| - A header file containing typedefs for the types defined in the CDDL, as well as declarations for decoding functions for some types (those specified as entry types). The typedefs are the same for both encoding and decoding. | ||
| - A C file containing all the encoding/decoding code. | ||
| The code is split across multiple functions, and each function contains a single `if` statement which "and"s and "or"s together calls into the cbor libraries or to other generated decoding functions. | ||
| Code generation is invoked with the `zcbor code` command: | ||
| CDDL allows placing restrictions on the members of your data structure. | ||
| Restrictions can be on type, on content (e.g. values/sizes of ints or strings), and repetition (e.g. the number of members in a list). | ||
| The generated code will validate the input (i.e. the structure if encoding, or the payload for decoding), which means that it will check all the restriction set in the CDDL description, and fail if a restriction is broken. | ||
| ```sh | ||
| zcbor code <--decode or --encode or both> -c <CDDL description file(s)> -t <which CDDL type(s) to expose in the API> --output-cmake <path to place the generated CMake file at> | ||
| zcbor code <--decode or --encode or both> -c <CDDL description file(s)> -t <which CDDL type(s) to expose in the API> --oc <path to the generated C file> --oh <path to the generated header file> --oht <path to the generated types header> | ||
| ``` | ||
| The cbor libraries do most of the actual translation and moving of bytes, and the validation of values. | ||
| When you call this, zcbor reads the CDDL files and creates C struct types to match the types described in the CDDL. | ||
| It then creates code that uses the C library to decode CBOR data into the structs, and/or encode CBOR from the data in the structs. | ||
| Finally, it takes the "entry types" (`-t`) and creates a public API function for each of them. | ||
| While doing these things, it will make a number of optimizations, e.g. inlining code for small types and removing ununsed functions. | ||
| It outputs the generated code into header and source files and optionally creates a CMake file to build them. | ||
| There are tests for the code generation in [tests/](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/). | ||
| The tests require [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (if your shell is set up to build Zephyr samples, the tests should also build). | ||
| The `zcbor code` command reads one or more CDDL file(s) and generates some or all of these files: | ||
| - A header file with types (always) | ||
| - A header file with declarations for decoding functions (if `--decode`/`-d` is specified) | ||
| - A C file with decoding functions (if `--decode`/`-d` is specified) | ||
| - A header file with declarations for encoding functions (if `--encode`/`-e` is specified) | ||
| - A C file with encoding functions (if `--encode`/`-e` is specified) | ||
| - A CMake file that creates a library with the generated code and the C library (if `--output-cmake` is specified). | ||
| CDDL allows placing restrictions on the members of your data. | ||
| Restrictions can be on type (int/string/list/bool etc.), on content (e.g. values/sizes of ints or strings), and repetition (e.g. the number of members in a list). | ||
| The generated code will validate the input, which means that it will check all the restriction set in the CDDL description, and fail if a restriction is broken. | ||
| There are tests for the code generation in [tests/decode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/decode) and [tests/encode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/encode). | ||
| The tests require [Zephyr](https://github.com/zephyrproject-rtos/zephyr) (if your system is set up to build Zephyr samples, the tests should also build). | ||
| The generated C code is C++ compatible. | ||
| Build system | ||
| ------------ | ||
| When calling zcbor with the argument `--output-cmake <file path>`, a cmake file will be created at that location. | ||
| The cmake file creates a cmake target and adds the generated and non-generated source files, and the include directories to the header files. | ||
| This cmake file can then be included in your project's `CMakeLists.txt` file, and the target can be linked into your project. | ||
| This is demonstrated in the tests, e.g. at tests/decode/test3_simple/CMakeLists.txt. | ||
| When calling zcbor with the argument `--output-cmake <file path>`, a CMake file will be created at that location. | ||
| The generated CMake file creates a target library and adds the generated and non-generated source files as well as required include directories to it. | ||
| This CMake file can then be included in your project's `CMakeLists.txt` file, and the target can be linked into your project. | ||
| This is demonstrated in the tests, e.g. at [tests/decode/test3_simple/CMakeLists.txt](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/decode/test3_simple/CMakeLists.txt). | ||
| zcbor can be instructed to copy the non-generated sources to the same location as the generated sources with `--copy-sources`. | ||
| Usage Example | ||
| ============= | ||
| There are buildable examples in the [samples](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/samples) directory. | ||
| To see how to use the C library directly, see the (hello_world)[samples/hello_world/src/main.c] sample, or the (pet)[samples/pet/src/main.c] sample (look for calls to functions prefixed with `zcbor_`). | ||
| To see how to use code generation, see the (pet)[samples/pet/src/main.c] sample. | ||
| Look at the (CMakeLists.txt)[samples/pet/CMakeLists.txt] file to see how zcbor is invoked for code generation (and for conversion). | ||
| To see how to do conversion, see the (pet)[samples/pet/CMakeLists.txt] sample. | ||
| Below are some additional examples of how to invoke zcbor for code generation and for converting/validating | ||
| Code generation | ||
| --------------- | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| # or | ||
| zcbor code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| ``` | ||
| Converting | ||
| ---------- | ||
| Here is an example call for converting from YAML to CBOR: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| # or | ||
| zcbor convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| ``` | ||
| Which takes a yaml structure from mypet.yaml, validates it against the Pet type in the CDDL description in pet.cddl, and writes binary CBOR data to mypet.cbor. | ||
| Validating | ||
| ---------- | ||
| Here is an example call for validating a JSON file: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py validate -c pet.cddl -t Pet --yaml-compatibility -i mypet.json | ||
| # or | ||
| zcbor validate -c pet.cddl -t Pet --yaml-compatibility -i mypet.json | ||
| ``` | ||
| Which takes the json structure in mypet.json, converts any [yaml-compatible](#making-cbor-yaml-json-compatible) values to their original form, and validates that against the Pet type in the CDDL description in pet.cddl. | ||
| Running tests | ||
| ============= | ||
| The tests for the generated code are based on the Zephyr ztest library. | ||
| These tests can be found in [tests/decode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/decode) and [tests/encode](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/tests/encode). | ||
| To set up the environment to run the ztest tests, follow [Zephyr's Getting Started Guide](https://docs.zephyrproject.org/latest/getting_started/index.html), or see the workflow in the [`.github`](https://github.com/NordicSemiconductor/zcbor/tree/0.7.0/.github) directory. | ||
| Tests for `convert` and `verify` are implemented with the unittest module. | ||
| These tests can be found in [tests/scripts/test_zcbor.py](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/scripts/test_zcbor.py). | ||
| In this file there are also tests for code style of all python scripts, using the `pycodestyle` library. | ||
| Tests for the docs, samples, etc. can be found in [tests/scripts/test_repo_files.py](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/scripts/test_repo_files.py). | ||
| For running the tests locally, there is [`tests/test.sh`](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/test.sh) which runs all above tests. | ||
| Introduction to CDDL | ||
@@ -234,3 +346,3 @@ ==================== | ||
| See [test3_simple](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/decode/test3_simple/) for CDDL example code. | ||
| See [pet.cddl](https://github.com/NordicSemiconductor/zcbor/blob/0.7.0/tests/cases/pet.cddl) for CDDL example code. | ||
@@ -257,3 +369,3 @@ | ||
| Major types `pint`, `nint`, `tag`, and `prim` elements have no payload, only _Value_. | ||
| Major types `pint` (0), `nint` (1), `tag` (6), and `simple` (7) elements have no payload, only _Value_. | ||
@@ -263,8 +375,10 @@ * `pint`: Interpret the _Value_ as a positive integer. | ||
| * `tag`: The _Value_ says something about the next non-tag element. | ||
| See the [CBOR tag documentation](See https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) for details. | ||
| * `prim`: Different _Additional info_ mean different things: | ||
| * 20: `false` | ||
| * 21: `true` | ||
| * 22: `null` | ||
| * 23: `undefined` | ||
| See the [CBOR tag documentation](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) for details. | ||
| * `simple`: Different _Additional info_ mean different things: | ||
| * 0-19: Unassigned simple values. | ||
| * 20: `false` simple value | ||
| * 21: `true` simple value | ||
| * 22: `null` simple value | ||
| * 23: `undefined` simple value | ||
| * 24: Interpret the _Value_ as a 1 byte simple value. These simple values are currently unassigned. | ||
| * 25: Interpret the _Value_ as an IEEE 754 float16. | ||
@@ -275,3 +389,3 @@ * 26: Interpret the _Value_ as an IEEE 754 float32. | ||
| For `bstr`, `tstr`, `list`, and `map`, the _Value_ describes the length of the _Payload_. | ||
| For `bstr` (2), `tstr` (3), `list` (4), and `map` (5), the _Value_ describes the length of the _Payload_. | ||
| For `bstr` and `tstr`, the length is in bytes, for `list`, the length is in number of elements, and for `map`, the length is in number of key/value element pairs. | ||
@@ -281,124 +395,25 @@ | ||
| If a `list` or `map` has _Additional info_ 31, it is "indefinite-length", which means it has an "unknown" number of elements. | ||
| Instead, its end is marked by a `prim` with _Additional info_ 31 (byte value 0xFF). | ||
| Instead, its end is marked by a `simple` with _Additional info_ 31 (byte value 0xFF). | ||
| Usage Example | ||
| ============= | ||
| Code generation | ||
| --------------- | ||
| History | ||
| ======= | ||
| This example is is taken from [test3_simple](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/decode/test3_simple/). | ||
| zcbor (then "cddl-gen") was initially conceived as a code generation project. | ||
| It was inspired by the need to securely decode the complex manifest data structures in the [IETF SUIT specification](https://datatracker.ietf.org/doc/draft-ietf-suit-manifest/). | ||
| This is reflected in the fact that there are multiple zcbor tests that use the CDDL and examples from various revisions of that specification. | ||
| Decoding/deserializing data securely requires doing some quite repetitive checks on each data element, to be sure that you are not decoding gibberish. | ||
| This is where code generation could pull a lot of weight. | ||
| Later it was discovered that the CBOR library that was designed to used by generated code could be useful by itself. | ||
| The script was also expanded so it could directly manipulate CBOR data. | ||
| Since CBOR, YAML, and JSON are all represented in roughly the same way internally in Python, it was easy to expand that data manipulation to support YAML and JSON. | ||
| If your CDDL file contains the following code: | ||
| Some places where zcbor is currently used: | ||
| - [MCUboot's serial recovery mechanism](https://github.com/mcu-tools/mcuboot/blob/main/boot/boot_serial/src/boot_serial.c) | ||
| - [Zephyr's mcumgr](https://github.com/zephyrproject-rtos/zephyr/blob/main/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c) | ||
| - [Zephyr's LwM2M SenML](https://github.com/zephyrproject-rtos/zephyr/blob/main/subsys/net/lib/lwm2m/lwm2m_rw_senml_cbor.c) | ||
| - [nRF Connect SDK's full modem update mechanism](https://github.com/nrfconnect/sdk-nrf/blob/main/subsys/mgmt/fmfu/src/fmfu_mgmt.c) | ||
| - [nRF Connect SDK's nrf_rpc](https://github.com/nrfconnect/sdk-nrfxlib/blob/main/nrf_rpc/nrf_rpc_cbor.c) | ||
| ```cddl | ||
| Timestamp = bstr .size 8 | ||
| ; Comments are denoted with a semicolon | ||
| Pet = [ | ||
| name: [ +tstr ], | ||
| birthday: Timestamp, | ||
| species: (cat: 1) / (dog: 2) / (other: 3), | ||
| ] | ||
| ``` | ||
| Call the Python script: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| # or | ||
| zcbor code -c pet.cddl -d -t Pet --oc pet_decode.c --oh pet_decode.h | ||
| ``` | ||
| And use the generated code with | ||
| ```c | ||
| #include <pet_decode.h> /* The name of the header file is taken from the name of | ||
| the cddl file, but can also be specifiec when calling | ||
| the script. */ | ||
| /* ... */ | ||
| /* The following type and function refer to the Pet type in the CDDL, which | ||
| * has been specified as an --entry-types (-t) when invoking zcbor. */ | ||
| Pet_t pet; | ||
| size_t decode_len; | ||
| bool success = cbor_decode_Pet(input, sizeof(input), &pet, &decode_len); | ||
| ``` | ||
| The process is the same for encoding, except: | ||
| - Change `-d` to `-e` when invoking zcbor | ||
| - Input parameters become output parameters and vice versa in the code: | ||
| ```c | ||
| #include <pet_encode.h> /* The name of the header file is taken from the name of | ||
| the cddl file, but can also be specifiec when calling | ||
| the script. */ | ||
| /* ... */ | ||
| /* The following type and function refer to the Pet type in the CDDL, which | ||
| * has been specified as an --entry-types (-t) when invoking zcbor. */ | ||
| Pet_t pet = { /* Initialize with desired data. */ }; | ||
| uint8_t output[100]; /* 100 is an example. Must be large enough for data to fit. */ | ||
| size_t out_len; | ||
| bool success = cbor_encode_Pet(output, sizeof(output), &pet, &out_len); | ||
| ``` | ||
| CBOR decoding/encoding library | ||
| ------------------------------ | ||
| For encoding: | ||
| ```c | ||
| #include <zcbor_encode.h> | ||
| uint8_t payload[100]; | ||
| zcbor_state_t state; | ||
| zcbor_new_state(&state, 1, payload, sizeof(payload), 0); | ||
| res = res && zcbor_list_start_encode(&state, 0); | ||
| res = res && zcbor_tstr_put(&state, "first"); | ||
| res = res && zcbor_tstr_put(&state, "second"); | ||
| res = res && zcbor_list_end_encode(&state, 0); | ||
| uint8_t timestamp[8] = {1, 2, 3, 4, 5, 6, 7, 8}; | ||
| struct zcbor_string timestamp_str = { | ||
| .value = timestamp, | ||
| .len = sizeof(timestamp), | ||
| }; | ||
| res = res && zcbor_bstr_encode(&state, ×tamp_str); | ||
| res = res && zcbor_uint32_put(&state, 2 /* dog */); | ||
| res = res && zcbor_list_end_encode(&state, 0); | ||
| ``` | ||
| Converting | ||
| ---------- | ||
| Here is an example call for converting from YAML to CBOR: | ||
| ```sh | ||
| python3 <zcbor base>/zcbor/zcbor.py convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| # or | ||
| zcbor convert -c pet.cddl -t Pet -i mypet.yaml -o mypet.cbor | ||
| ``` | ||
| Which takes a yaml structure from mypet.yaml, validates it against the Pet type in the CDDL description in pet.cddl, and writes binary CBOR data to mypet.cbor. | ||
| See the tests in <zcbor base>/tests/ for examples of using the python module | ||
| Running tests | ||
| ============= | ||
| The tests for the generated code are based on Zephyr ztests. | ||
| Tests for the conversion functions in the script are implemented with the unittest module. | ||
| There are also test.sh scripts to quickly run all tests. | ||
| [`tests/test.sh`](https://github.com/NordicSemiconductor/zcbor/blob/0.6.0/tests/test.sh) runs all tests, including python tests in [`tests/scripts`](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/tests/scripts). | ||
| These tests are dependent upon the `pycodestyle` package from `pip`. | ||
| Run these scripts with no arguments. | ||
| To set up the environment to run the ztest tests, follow [Zephyr's Getting Started Guide](https://docs.zephyrproject.org/latest/getting_started/index.html), or see the workflow in the [`.github`](https://github.com/NordicSemiconductor/zcbor/tree/0.6.0/.github) directory. | ||
| Command line documentation | ||
@@ -413,3 +428,3 @@ ========================== | ||
| ``` | ||
| usage: zcbor [-h] {code,validate,convert} ... | ||
| usage: zcbor [-h] [--version] {code,validate,convert} ... | ||
@@ -424,2 +439,3 @@ Parse a CDDL file and validate/convert between YAML, JSON, and CBOR. Can also | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
@@ -432,3 +448,3 @@ ``` | ||
| ``` | ||
| usage: zcbor code [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| usage: zcbor code [-h] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] [--output-c OUTPUT_C] | ||
@@ -440,2 +456,3 @@ [--output-h OUTPUT_H] [--output-h-types OUTPUT_H_TYPES] | ||
| [--include-prefix INCLUDE_PREFIX] [-s] | ||
| [--file-header FILE_HEADER] | ||
@@ -459,3 +476,2 @@ Parse a CDDL file and produce C code that validates and xcodes CBOR. | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -538,2 +554,5 @@ multiple files is equivalent to concatenating them. | ||
| union members. | ||
| --file-header FILE_HEADER | ||
| Header to be included in the comment at the top of | ||
| generated C files, e.g. copyright. | ||
@@ -546,5 +565,6 @@ ``` | ||
| ``` | ||
| usage: zcbor validate [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] -i INPUT | ||
| usage: zcbor validate [-h] -c CDDL [--no-prelude] [-v] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE | ||
| [--default-max-qty DEFAULT_MAX_QTY] | ||
| [--yaml-compatibility] | ||
@@ -556,3 +576,2 @@ Read CBOR, YAML, or JSON data from file or stdin and validate it against a | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -565,7 +584,2 @@ multiple files is equivalent to concatenating them. | ||
| generating code. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| -i INPUT, --input INPUT | ||
@@ -582,2 +596,15 @@ Input data file. The option --input-as specifies how | ||
| as. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| --yaml-compatibility Whether to convert CBOR-only values to YAML-compatible | ||
| ones (when converting from CBOR), or vice versa (when | ||
| converting to CBOR). When this is enabled, all CBOR | ||
| data is guaranteed to convert into YAML/JSON. JSON and | ||
| YAML do not support all data types that CBOR/CDDL | ||
| supports. bytestrings (BSTR), tags, undefined, and | ||
| maps with non-text keys need special handling. See the | ||
| zcbor README for more information. | ||
@@ -590,7 +617,9 @@ ``` | ||
| ``` | ||
| usage: zcbor convert [-h] [--version] -c CDDL [--no-prelude] [-v] | ||
| [--default-max-qty DEFAULT_MAX_QTY] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE -o | ||
| OUTPUT [--output-as {yaml,json,cbor,cborhex,c_code}] | ||
| usage: zcbor convert [-h] -c CDDL [--no-prelude] [-v] -i INPUT | ||
| [--input-as {yaml,json,cbor,cborhex}] -t ENTRY_TYPE | ||
| [--default-max-qty DEFAULT_MAX_QTY] | ||
| [--yaml-compatibility] -o OUTPUT | ||
| [--output-as {yaml,json,cbor,cborhex,c_code}] | ||
| [--c-code-var-name C_CODE_VAR_NAME] | ||
| [--c-code-columns C_CODE_COLUMNS] | ||
@@ -600,21 +629,6 @@ Parse a CDDL file and validate/convert between CBOR and YAML/JSON. The script | ||
| conforms to the CDDL description. The script fails if the data does not | ||
| conform. 'zcbor validate' can be used if only validate is needed. JSON and | ||
| YAML do not support all data types that CBOR/CDDL supports. bytestrings | ||
| (BSTR), tags, and maps with non-text keys need special handling: All strings | ||
| in JSON/YAML are text strings. If a BSTR is needed, use a dict with a single | ||
| entry, with "bstr" as the key, and the byte string (as a hex string) as the | ||
| value, e.g. {"bstr": "0123456789abcdef"}. The value can also be another type, | ||
| e.g. which will be interpreted as a BSTR with the given value as contents (in | ||
| cddl: 'bstr .cbor SomeType'). E.g. {"bstr": ["first element", 2, [3]]} Dicts | ||
| in JSON/YAML only support text strings for keys, so if a dict needs other | ||
| types of keys, encapsulate the key and value into a dict (n is an arbitrary | ||
| integer): e.g. {"name": "foo", "keyvaln": {"key": 123, "val": "bar"}} which | ||
| will conform to the CDDL {tstr => tstr, int => tstr}. Tags are specified by a | ||
| dict with two elements, e.g. {"tag": 1234, "value": ["tagged string within | ||
| list"]} 'undefined' is specified as a list with a single text entry: | ||
| "zcbor_undefined". | ||
| conform. 'zcbor validate' can be used if only validate is needed. | ||
| options: | ||
| -h, --help show this help message and exit | ||
| --version show program's version number and exit | ||
| -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing | ||
@@ -627,7 +641,2 @@ multiple files is equivalent to concatenating them. | ||
| generating code. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| -i INPUT, --input INPUT | ||
@@ -644,2 +653,15 @@ Input data file. The option --input-as specifies how | ||
| as. | ||
| --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY | ||
| Default maximum number of repetitions when no maximum | ||
| is specified. It is only relevant when handling data | ||
| that will be decoded by generated code. If omitted, a | ||
| large number will be used. | ||
| --yaml-compatibility Whether to convert CBOR-only values to YAML-compatible | ||
| ones (when converting from CBOR), or vice versa (when | ||
| converting to CBOR). When this is enabled, all CBOR | ||
| data is guaranteed to convert into YAML/JSON. JSON and | ||
| YAML do not support all data types that CBOR/CDDL | ||
| supports. bytestrings (BSTR), tags, undefined, and | ||
| maps with non-text keys need special handling. See the | ||
| zcbor README for more information. | ||
| -o OUTPUT, --output OUTPUT | ||
@@ -657,3 +679,8 @@ Output data file. The option --output-as specifies how | ||
| files. | ||
| --c-code-columns C_CODE_COLUMNS | ||
| Only relevant together with '--output-as c_code' or .c | ||
| files. The number of bytes per line in the variable | ||
| instantiation. If omitted, the entire declaration is a | ||
| single line. | ||
| ``` |
@@ -9,2 +9,3 @@ LICENSE | ||
| include/zcbor_encode.h | ||
| include/zcbor_tags.h | ||
| scripts/requirements-base.txt | ||
@@ -11,0 +12,0 @@ src/zcbor_common.c |
+1
-1
@@ -1,1 +0,1 @@ | ||
| 0.6.0 | ||
| 0.7.0 |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
364467
12.35%25
4.17%2857
2.84%