checkpoint
diff --git a/.gitignore b/.gitignore
index 241e782..957b6a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
build
Testing/Temporary
*.pc
+*.bat
# Emacs temp files
*~
diff --git a/.travis.yml b/.travis.yml
index 0d7c96f..e07a385 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,7 @@
env:
- OPTIONS=-DCN_CBOR_USE_CONTEXT=OFF
- OPTIONS=-DCN_CBOR_USE_CONTEXT=ON
+ COVERALLS="-DCN_CBOR_COVERALLS_SEND=ON"
- OPTIONS=-DCN_CBOR_NO_FLOATS=ON
- OPTIONS=-DCN_CBOR_ALIGN_READS=ON
@@ -16,5 +17,12 @@
packages:
- cmake
- cmake-data
+
script:
-- "./build.sh all test"
+ - cmake --version
+ - mkdir build
+ - cd build && cmake $OPTIONS $COVERALLS .. && make all test
+
+after_success:
+ - make coveralls
+
diff --git a/include/cn-cbor/cn-cbor.h b/include/cn-cbor/cn-cbor.h
index 2dceae0..6d908eb 100644
--- a/include/cn-cbor/cn-cbor.h
+++ b/include/cn-cbor/cn-cbor.h
@@ -13,18 +13,18 @@
#endif
#ifndef MYLIB_EXPORT
-#if defined (_WIN32)
+#if defined(_WIN32)
#if defined(CN_CBOR_IS_DLL)
-#define MYLIB_EXPORT __declspec(dllimport)
+#define MYLIB_EXPORT __declspec(dllimport)
#else
#define MYLIB_EXPORT
#endif /* CN_CBOR_IS_DLL */
-#else /* defined (_WIN32) */
+#else /* defined (_WIN32) */
#define MYLIB_EXPORT
-#endif
+#endif
#endif
-#ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
#endif
#ifdef EMACS_INDENTATION_HELPER
@@ -53,42 +53,42 @@
* All of the different kinds of CBOR values.
*/
typedef enum cn_cbor_type {
- /** false */
- CN_CBOR_FALSE,
- /** true */
- CN_CBOR_TRUE,
- /** null */
- CN_CBOR_NULL,
- /** undefined */
- CN_CBOR_UNDEF,
- /** Positive integers */
- CN_CBOR_UINT,
- /** Negative integers */
- CN_CBOR_INT,
- /** Byte string */
- CN_CBOR_BYTES,
- /** UTF-8 string */
- CN_CBOR_TEXT,
- /** Byte string, in chunks. Each chunk is a child. */
- CN_CBOR_BYTES_CHUNKED,
- /** UTF-8 string, in chunks. Each chunk is a child */
- CN_CBOR_TEXT_CHUNKED,
- /** Array of CBOR values. Each array element is a child, in order */
- CN_CBOR_ARRAY,
- /** Map of key/value pairs. Each key and value is a child, alternating. */
- CN_CBOR_MAP,
- /** Tag describing the next value. The next value is the single child. */
- CN_CBOR_TAG,
- /** Simple value, other than the defined ones */
- CN_CBOR_SIMPLE,
+ /** false */
+ CN_CBOR_FALSE,
+ /** true */
+ CN_CBOR_TRUE,
+ /** null */
+ CN_CBOR_NULL,
+ /** undefined */
+ CN_CBOR_UNDEF,
+ /** Positive integers */
+ CN_CBOR_UINT,
+ /** Negative integers */
+ CN_CBOR_INT,
+ /** Byte string */
+ CN_CBOR_BYTES,
+ /** UTF-8 string */
+ CN_CBOR_TEXT,
+ /** Byte string, in chunks. Each chunk is a child. */
+ CN_CBOR_BYTES_CHUNKED,
+ /** UTF-8 string, in chunks. Each chunk is a child */
+ CN_CBOR_TEXT_CHUNKED,
+ /** Array of CBOR values. Each array element is a child, in order */
+ CN_CBOR_ARRAY,
+ /** Map of key/value pairs. Each key and value is a child, alternating. */
+ CN_CBOR_MAP,
+ /** Tag describing the next value. The next value is the single child. */
+ CN_CBOR_TAG,
+ /** Simple value, other than the defined ones */
+ CN_CBOR_SIMPLE,
#ifndef CBOR_NO_FLOAT
- /** Doubles, floats, and half-floats */
- CN_CBOR_DOUBLE,
- /** Floats, and half-floats */
- CN_CBOR_FLOAT,
+ /** Doubles, floats, and half-floats */
+ CN_CBOR_DOUBLE,
+ /** Floats, and half-floats */
+ CN_CBOR_FLOAT,
#endif
- /** An error has occurred */
- CN_CBOR_INVALID
+ /** An error has occurred */
+ CN_CBOR_INVALID
} cn_cbor_type;
/**
@@ -96,102 +96,97 @@
* `cn_cbor` structure.
*/
typedef enum cn_cbor_flags {
- /** The count field will be used for parsing */
- CN_CBOR_FL_COUNT = 1,
- /** An indefinite number of children */
- CN_CBOR_FL_INDEF = 2,
- /** Not used yet; the structure must free the v.str pointer when the
- structure is freed */
- CN_CBOR_FL_OWNER = 0x80, /* of str */
+ /** The count field will be used for parsing */
+ CN_CBOR_FL_COUNT = 1,
+ /** An indefinite number of children */
+ CN_CBOR_FL_INDEF = 2,
+ /** Don't compress floating points value to smaller */
+ CN_CBOR_FL_KEEP_FLOAT_SIZE = 4,
+ /** This node was not allocated - don't free */
+ CN_CBOR_FL_EXT_SELF = 0x40,
+ /** The data is not owned by this node - don't free */
+ CN_CBOR_FL_EXT_DATA = 0x80
} cn_cbor_flags;
/**
* A CBOR value
*/
typedef struct cn_cbor {
- /** The type of value */
- cn_cbor_type type;
- /** Flags used at parse time */
- cn_cbor_flags flags;
- /** Data associated with the value; different branches of the union are
- used depending on the `type` field. */
- union {
- /** CN_CBOR_BYTES */
- const uint8_t* bytes;
- /** CN_CBOR_TEXT */
- const char* str;
- /** CN_CBOR_INT */
+ /** The type of value */
+ cn_cbor_type type;
+ /** Flags used at parse time */
+ cn_cbor_flags flags;
+ /** Data associated with the value; different branches of the union are
+ used depending on the `type` field. */
+ union {
+ /** CN_CBOR_BYTES */
+ const uint8_t* bytes;
+ /** CN_CBOR_TEXT */
+ const char* str;
+ /** CN_CBOR_INT */
#ifdef _MSC_VER
- int64_t sint;
+ int64_t sint;
#else
- long sint;
+ long sint;
#endif
- /** CN_CBOR_UINT */
+ /** CN_CBOR_UINT */
#ifdef _MSC_VER
- uint64_t uint;
+ uint64_t uint;
#else
- unsigned long uint;
+ unsigned long uint;
#endif
#ifndef CBOR_NO_FLOAT
- /** CN_CBOR_DOUBLE */
- double dbl;
- /** CN_CBOR_FLOAT */
- float f;
+ /** CN_CBOR_DOUBLE */
+ double dbl;
+ /** CN_CBOR_FLOAT */
+ float f;
#endif
- /** for use during parsing */
-#ifdef _MSC_VER
- uint64_t count;
-#else
- unsigned long count;
-#endif
- } v; /* TBD: optimize immediate */
- /** Number of children.
- * @note: for maps, this is 2x the number of entries */
-#ifdef _MSC_VER
- size_t length;
-#else
- int length;
-#endif
- /** The first child value */
- struct cn_cbor* first_child;
- /** The last child value */
- struct cn_cbor* last_child;
- /** The sibling after this one, or NULL if this is the last */
- struct cn_cbor* next;
- /** The parent of this value, or NULL if this is the root */
- struct cn_cbor* parent;
+ /** for use during parsing */
+ size_t count;
+ } v; /* TBD: optimize immediate */
+ /** Number of children.
+ * @note: for maps, this is 2x the number of entries */
+ size_t length;
+ /** The first child value */
+ struct cn_cbor* first_child;
+ /** The last child value */
+ struct cn_cbor* last_child;
+ /** The sibling after this one, or NULL if this is the last */
+ struct cn_cbor* next;
+ /** The parent of this value, or NULL if this is the root */
+ struct cn_cbor* parent;
} cn_cbor;
/**
* All of the different kinds of errors
*/
typedef enum cn_cbor_error {
- /** No error has occurred */
- CN_CBOR_NO_ERROR,
- /** More data was expected while parsing */
- CN_CBOR_ERR_OUT_OF_DATA,
- /** Some extra data was left over at the end of parsing */
- CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED,
- /** A map should be alternating keys and values. A break was found
- when a value was expected */
- CN_CBOR_ERR_ODD_SIZE_INDEF_MAP,
- /** A break was found where it wasn't expected */
- CN_CBOR_ERR_BREAK_OUTSIDE_INDEF,
- /** Indefinite encoding works for bstrs, strings, arrays, and maps.
- A different major type tried to use it. */
- CN_CBOR_ERR_MT_UNDEF_FOR_INDEF,
- /** Additional Information values 28-30 are reserved */
- CN_CBOR_ERR_RESERVED_AI,
- /** A chunked encoding was used for a string or bstr, and one of the elements
- wasn't the expected (string/bstr) type */
- CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING,
- /** An invalid parameter was passed to a function */
- CN_CBOR_ERR_INVALID_PARAMETER,
- /** Allocation failed */
- CN_CBOR_ERR_OUT_OF_MEMORY,
- /** A float was encountered during parse but the library was built without
- support for float types. */
- CN_CBOR_ERR_FLOAT_NOT_SUPPORTED
+ /** No error has occurred */
+ CN_CBOR_NO_ERROR,
+ /** More data was expected while parsing */
+ CN_CBOR_ERR_OUT_OF_DATA,
+ /** Some extra data was left over at the end of parsing */
+ CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED,
+ /** A map should be alternating keys and values. A break was found
+ when a value was expected */
+ CN_CBOR_ERR_ODD_SIZE_INDEF_MAP,
+ /** A break was found where it wasn't expected */
+ CN_CBOR_ERR_BREAK_OUTSIDE_INDEF,
+ /** Indefinite encoding works for bstrs, strings, arrays, and maps.
+ A different major type tried to use it. */
+ CN_CBOR_ERR_MT_UNDEF_FOR_INDEF,
+ /** Additional Information values 28-30 are reserved */
+ CN_CBOR_ERR_RESERVED_AI,
+ /** A chunked encoding was used for a string or bstr, and one of the elements
+ wasn't the expected (string/bstr) type */
+ CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING,
+ /** An invalid parameter was passed to a function */
+ CN_CBOR_ERR_INVALID_PARAMETER,
+ /** Allocation failed */
+ CN_CBOR_ERR_OUT_OF_MEMORY,
+ /** A float was encountered during parse but the library was built without
+ support for float types. */
+ CN_CBOR_ERR_FLOAT_NOT_SUPPORTED
} cn_cbor_error;
/**
@@ -200,16 +195,16 @@
* @todo: turn into a function to make the type safety more clear?
*/
MYLIB_EXPORT
-extern const char *cn_cbor_error_str[];
+extern const char* cn_cbor_error_str[];
/**
* Errors
*/
typedef struct cn_cbor_errback {
- /** The position in the input where the erorr happened */
- int pos;
- /** The error, or CN_CBOR_NO_ERROR if none */
- cn_cbor_error err;
+ /** The position in the input where the error happened */
+ size_t pos;
+ /** The error, or CN_CBOR_NO_ERROR if none */
+ cn_cbor_error err;
} cn_cbor_errback;
#ifdef USE_CBOR_CONTEXT
@@ -223,7 +218,7 @@
* @param[in] size The size of each item
* @param[in] context The allocation context
*/
-typedef void* (*cn_calloc_func)(size_t count, size_t size, void *context);
+typedef void* (*cn_calloc_func)(size_t count, size_t size, void* context);
/**
* Free memory previously allocated with a context. If using a pool allocator,
@@ -236,26 +231,26 @@
* @param context [description]
* @return [description]
*/
-typedef void (*cn_free_func)(void *ptr, void *context);
+typedef void (*cn_free_func)(void* ptr, void* context);
/**
* The allocation context.
*/
typedef struct cn_cbor_context {
- /** The pool `calloc` routine. Must allocate and zero. */
- cn_calloc_func calloc_func;
- /** The pool `free` routine. Often a no-op, but required. */
- cn_free_func free_func;
- /** Typically, the pool object, to be used when calling `calloc_func`
- * and `free_func` */
- void *context;
+ /** The pool `calloc` routine. Must allocate and zero. */
+ cn_calloc_func calloc_func;
+ /** The pool `free` routine. Often a no-op, but required. */
+ cn_free_func free_func;
+ /** Typically, the pool object, to be used when calling `calloc_func`
+ * and `free_func` */
+ void* context;
} cn_cbor_context;
/** When USE_CBOR_CONTEXT is defined, many functions take an extra `context`
- * parameter */
-#define CBOR_CONTEXT , cn_cbor_context *context
+ * parameter */
+#define CBOR_CONTEXT , cn_cbor_context* context
/** When USE_CBOR_CONTEXT is defined, some functions take an extra `context`
- * parameter at the beginning */
+ * parameter at the beginning */
#define CBOR_CONTEXT_COMMA cn_cbor_context *context,
#else
@@ -266,8 +261,20 @@
#endif
/**
+ * Tag the data as not to be freed
+ *
+ */
+
+MYLIB_EXPORT
+void cn_cbor_dont_free_data(cn_cbor* cbor);
+
+/**
* Decode an array of CBOR bytes into structures.
*
+ * @note Pointers to the buffer are placed into the structure provided.
+ * This means that the buffer needs to be kept as long as the decoded
+ * structure is kept.
+ *
* @param[in] buf The array of bytes to parse
* @param[in] len The number of bytes in the array
* @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
@@ -275,7 +282,7 @@
* @return The parsed CBOR structure, or NULL on error
*/
MYLIB_EXPORT
-cn_cbor* cn_cbor_decode(const uint8_t *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp);
+cn_cbor* cn_cbor_decode(const uint8_t* buf, size_t len CBOR_CONTEXT, cn_cbor_errback* errp);
/**
* Get a value from a CBOR map that has the given string as a key.
@@ -331,10 +338,7 @@
* @param[in] cb [description]
* @return -1 on fail, or number of bytes written
*/
-ssize_t cn_cbor_encoder_write(uint8_t *buf,
- size_t buf_offset,
- size_t buf_size,
- const cn_cbor *cb);
+ssize_t cn_cbor_encoder_write(uint8_t* buf, size_t buf_offset, size_t buf_size, const cn_cbor* cb);
/**
* Create a CBOR map.
@@ -344,7 +348,7 @@
* @return The created map, or NULL on error
*/
MYLIB_EXPORT
-cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp);
+cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback* errp);
/**
* Create a CBOR byte string. The data in the byte string is *not* owned
@@ -357,9 +361,21 @@
* @return The created object, or NULL on error
*/
MYLIB_EXPORT
-cn_cbor* cn_cbor_data_create(const uint8_t* data, int len
- CBOR_CONTEXT,
- cn_cbor_errback *errp);
+cn_cbor* cn_cbor_data_create(const uint8_t* data, int len CBOR_CONTEXT, cn_cbor_errback* errp);
+
+/**
+ * Create a CBOR byte string. Ownership of the passed in data is
+ * controlled by the flags. Default is ownership is given up to CBOR.
+ *
+ * @param[in] data The data
+ * @param[in] len The number of bytes of data
+ * @param[in] flags CN_CBOR_FL_EXT_DATA or 0
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+MYLIB_EXPORT
+cn_cbor* cn_cbor_data_create2(const uint8_t* data, int len, int flags CBOR_CONTEXT, cn_cbor_errback* errp);
/**
* Create a CBOR UTF-8 string. The data is not checked for UTF-8 correctness.
@@ -375,9 +391,24 @@
* @return The created object, or NULL on error
*/
MYLIB_EXPORT
-cn_cbor* cn_cbor_string_create(const char* data
- CBOR_CONTEXT,
- cn_cbor_errback *errp);
+cn_cbor* cn_cbor_string_create(const char* data CBOR_CONTEXT, cn_cbor_errback* errp);
+
+/**
+ * Create a CBOR UTF-8 string. The data is not checked for UTF-8 correctness.
+ * Ownership of the passed in data is controlled by the flags.
+ * Default is ownership is given up to CBOR.
+ *
+ * @note: Do NOT use this function with untrusted data. It calls strlen, and
+ * relies on proper NULL-termination.
+ *
+ * @param[in] data NULL-terminated UTF-8 string
+ * @param[in] flags CN_CBOR_FL_EXT_DATA or 0
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+MYLIB_EXPORT
+cn_cbor* cn_cbor_string_create2(const char* data, int flags CBOR_CONTEXT, cn_cbor_errback* errp);
/**
* Create a CBOR integer (either positive or negative).
@@ -388,9 +419,7 @@
* @return The created object, or NULL on error
*/
MYLIB_EXPORT
-cn_cbor* cn_cbor_int_create(int64_t value
- CBOR_CONTEXT,
- cn_cbor_errback *errp);
+cn_cbor* cn_cbor_int_create(int64_t value CBOR_CONTEXT, cn_cbor_errback* errp);
#ifndef CBOR_NO_FLOAT
/**
@@ -401,9 +430,7 @@
* @param[out] errp Error, if NULL is returned
* @return The created object, or NULL on error
*/
-cn_cbor* cn_cbor_float_create(float value
- CBOR_CONTEXT,
- cn_cbor_errback *errp);
+cn_cbor* cn_cbor_float_create(float value CBOR_CONTEXT, cn_cbor_errback* errp);
/**
* Create a CBOR double.
@@ -413,25 +440,87 @@
* @param[out] errp Error, if NULL is returned
* @return The created object, or NULL on error
*/
-cn_cbor* cn_cbor_double_create(double value
- CBOR_CONTEXT,
- cn_cbor_errback *errp);
+cn_cbor* cn_cbor_double_create(double value CBOR_CONTEXT, cn_cbor_errback* errp);
#endif /* CBOR_NO_FLOAT */
/**
+ * Create a CBOR simple boolean.
+ *
+ * @note: Do NOT use this function with untrusted data. It calls strlen, and
+ * relies on proper NULL-termination.
+ *
+ * @param[in] simpleValue Simple value to have
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+MYLIB_EXPORT
+cn_cbor* cn_cbor_simple_create(int simpleValue CBOR_CONTEXT, cn_cbor_errback* errp);
+
+#define cn_cbor_null_create(context, error) cn_cbor_simple_create(22, context, error)
+
+/**
+ * Tag a CBOR object
+ *
+ * @param[in] tag Tag to be added
+ * @param[in] child child data to this object
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[in] perr Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+
+cn_cbor* cn_cbor_tag_create(int tag, cn_cbor* child, CBOR_CONTEXT_COMMA cn_cbor_errback* perr);
+
+/**
+ * Create a CBOR UTF-8 string. The data is not checked for UTF-8 correctness.
+ * The data being stored in the string is *not* owned the CBOR object, so it is
+ * not freed automatically.
+ *
+ * @note: Do NOT use this function with untrusted data. It calls strlen, and
+ * relies on proper NULL-termination.
+ *
+ * @param[in] value true or false
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+MYLIB_EXPORT
+cn_cbor* cn_cbor_bool_create(bool value CBOR_CONTEXT, cn_cbor_errback* errp);
+
+/**
+ * Create a chunked text or byte string.
+ *
+ * @param[in] type CN_CBOR_BYTES or CN_CBOR_TEXT
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[in] errp Error; if NULL is returned
+ * @return The created object or NULL on error
+ */
+MYLIB_EXPORT
+cn_cbor* cn_cbor_chunked_create(int type, CBOR_CONTEXT_COMMA cn_cbor_errback* errp);
+
+/**
+ * Append a node to a chunked list.
+ *
+ * @param[in] cb_array Node tagged as chunked
+ * @param[in] cb_value Item to be appended
+ * @param[in] errp Error; if FALSE is returned
+ * @return True if successfully appended
+ */
+MYLIB_EXPORT
+bool cn_cbor_chunked_append(cn_cbor* cb_array, cn_cbor* cb_value, cn_cbor_errback* errp);
+
+/**
* Put a CBOR object into a map with a CBOR object key. Duplicate checks are NOT
* currently performed.
*
* @param[in] cb_map The map to insert into
- * @param[in] key The key
+ * @param[in] cb_key The key
* @param[in] cb_value The value
* @param[out] errp Error
* @return True on success
*/
MYLIB_EXPORT
-bool cn_cbor_map_put(cn_cbor* cb_map,
- cn_cbor *cb_key, cn_cbor *cb_value,
- cn_cbor_errback *errp);
+bool cn_cbor_map_put(cn_cbor* cb_map, cn_cbor* cb_key, cn_cbor* cb_value, cn_cbor_errback* errp);
/**
* Put a CBOR object into a map with an integer key. Duplicate checks are NOT
@@ -445,14 +534,11 @@
* @return True on success
*/
MYLIB_EXPORT
-bool cn_cbor_mapput_int(cn_cbor* cb_map,
- int64_t key, cn_cbor* cb_value
- CBOR_CONTEXT,
- cn_cbor_errback *errp);
+bool cn_cbor_mapput_int(cn_cbor* cb_map, int64_t key, cn_cbor* cb_value CBOR_CONTEXT, cn_cbor_errback* errp);
/**
* Put a CBOR object into a map with a string key. Duplicate checks are NOT
- * currently performed.
+ * currently performed. The string value is not freed when the object is freed.
*
* @note: do not call this routine with untrusted string data. It calls
* strlen, and requires a properly NULL-terminated key.
@@ -465,10 +551,29 @@
* @return True on success
*/
MYLIB_EXPORT
-bool cn_cbor_mapput_string(cn_cbor* cb_map,
- const char* key, cn_cbor* cb_value
- CBOR_CONTEXT,
- cn_cbor_errback *errp);
+bool cn_cbor_mapput_string(cn_cbor* cb_map, const char* key, cn_cbor* cb_value CBOR_CONTEXT, cn_cbor_errback* errp);
+
+/**
+ * Put a CBOR object into a map with a string key. Duplicate checks are NOT
+ * currently performed. The string will be freed depending on the flags.
+ *
+ * @note: do not call this routine with untrusted string data. It calls
+ * strlen, and requires a properly NULL-terminated key.
+ *
+ * @param[in] cb_map The map to insert into
+ * @param[in] key The string key
+ * @param[in] cb_value The value
+ * @param[in] flags CN_CBOR_FL_EXT_DATA | 0
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error
+ * @return True on success
+ */
+MYLIB_EXPORT
+bool cn_cbor_mapput_string2(cn_cbor* cb_map,
+ const char* key,
+ cn_cbor* cb_value,
+ int flags CBOR_CONTEXT,
+ cn_cbor_errback* errp);
/**
* Create a CBOR array
@@ -478,7 +583,7 @@
* @return The created object, or NULL on error
*/
MYLIB_EXPORT
-cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp);
+cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback* errp);
/**
* Append an item to the end of a CBOR array.
@@ -489,9 +594,7 @@
* @return True on success
*/
MYLIB_EXPORT
-bool cn_cbor_array_append(cn_cbor* cb_array,
- cn_cbor* cb_value,
- cn_cbor_errback *errp);
+bool cn_cbor_array_append(cn_cbor* cb_array, cn_cbor* cb_value, cn_cbor_errback* errp);
/**
* Dump the object to a file pointer
@@ -499,28 +602,29 @@
*
* @param[in] buffer Location to place output
* @param[in] bufferSize Size of return buffer
- * @param[in] fp File pointer to print on
* @param[in] cb tree to be dumped
* @param[in] indent string to use for each level of indention
* @param[in] crlf string to use for end of line marker
* @return size of output generated, -1 if buffer is too small
*/
-extern ssize_t cn_cbor_printer_write(char * buffer, size_t bufferSize, const cn_cbor * cb, const char * indent, const char * crlf);
+extern ssize_t cn_cbor_printer_write(char* buffer,
+ size_t bufferSize,
+ const cn_cbor* cb,
+ const char* indent,
+ const char* crlf);
#ifdef __MBED__
-#define ntohs(a) ((uint16_t) (((((uint16_t) (a)) & 0xff) << 8) | (((uint16_t) (a)) & 0xff00) >> 8))
-#define htons(a) ntohs(a)
-#define ntohl(a) ((uint32_t) ( \
- ((((uint32_t)(a)) & 0x000000ff) << 24) | \
- ((((uint32_t)(a)) & 0x0000ff00) << 8) | \
- ((((uint32_t)(a)) & 0x00ff0000) >> 8) | \
- ((((uint32_t)(a)) & 0xff000000) >> 24)))
+#define ntohs(a) ((uint16_t)(((((uint16_t)(a)) & 0xff) << 8) | (((uint16_t)(a)) & 0xff00) >> 8))
+#define htons(a) ntohs(a)
+#define ntohl(a) \
+ ((uint32_t)(((((uint32_t)(a)) & 0x000000ff) << 24) | ((((uint32_t)(a)) & 0x0000ff00) << 8) | \
+ ((((uint32_t)(a)) & 0x00ff0000) >> 8) | ((((uint32_t)(a)) & 0xff000000) >> 24)))
#define htonl(a) ntohl(a)
-#endif // __MBED__
+#endif // __MBED__
-#ifdef __cplusplus
+#ifdef __cplusplus
}
#endif
-#endif /* CN_CBOR_H */
+#endif /* CN_CBOR_H */
diff --git a/src/cn-cbor.c b/src/cn-cbor.c
index 6219f97..d3eb317 100644
--- a/src/cn-cbor.c
+++ b/src/cn-cbor.c
@@ -33,6 +33,12 @@
} while (0)
MYLIB_EXPORT
+void cn_cbor_dont_free_data(cn_cbor *cbor)
+{
+ cbor->flags |= CN_CBOR_FL_EXT_DATA;
+}
+
+MYLIB_EXPORT
void cn_cbor_free(cn_cbor *cb CBOR_CONTEXT)
{
cn_cbor *p = (cn_cbor *)cb;
@@ -47,7 +53,21 @@
p1->first_child = 0;
}
}
- CN_CBOR_FREE_CONTEXT(p);
+
+ if ((p->flags & CN_CBOR_FL_EXT_DATA) == 0) {
+ switch (p->type) {
+ case CN_CBOR_BYTES:
+ case CN_CBOR_TEXT:
+ CN_CBOR_FREE_CONTEXT(p->v.bytes);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if ((p->flags & CN_CBOR_FL_EXT_SELF) == 0) {
+ CN_CBOR_FREE_CONTEXT(p);
+ }
p = p1;
}
}
@@ -128,10 +148,10 @@
unsigned char *pos = pb->buf;
unsigned char *ebuf = pb->ebuf;
cn_cbor *parent = top_parent;
- int ib;
- unsigned int mt;
- int ai;
- uint64_t val;
+ int ib = 0;
+ unsigned int mt = 0;
+ int ai = 0;
+ uint64_t val = 0;
cn_cbor *cb = NULL;
#ifndef CBOR_NO_FLOAT
union {
@@ -155,11 +175,18 @@
case CN_CBOR_TEXT:
parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */
break;
+
case CN_CBOR_MAP:
if (parent->length & 1) {
CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP);
}
- default:;
+ break;
+
+ case CN_CBOR_ARRAY:
+ break;
+
+ default:
+ CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING);
}
goto complete;
}
@@ -209,7 +236,10 @@
else {
CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF);
}
+ default:
+ break;
}
+
// process content
switch (mt) {
case MT_UNSIGNED:
@@ -223,6 +253,7 @@
cb->v.str = (char *)pos;
cb->length = (size_t)val;
TAKE(pos, ebuf, val, ;);
+ cb->flags |= CN_CBOR_FL_EXT_DATA;
break;
case MT_MAP:
val <<= 1;
@@ -278,8 +309,16 @@
break;
default:
cb->v.uint = val;
+ if (24 <= val && val < 32) {
+ CN_CBOR_FAIL(CN_CBOR_ERR_INVALID_PARAMETER);
+ }
+ break;
}
+
+ default:
+ CN_CBOR_FAIL(CN_CBOR_ERR_INVALID_PARAMETER);
}
+
fill: /* emulate loops */
if (parent->flags & CN_CBOR_FL_INDEF) {
if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT) {
@@ -320,7 +359,7 @@
{
cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL};
struct parse_buf pb;
- cn_cbor *ret;
+ cn_cbor *ret = NULL;
pb.buf = (unsigned char *)buf;
pb.ebuf = (unsigned char *)buf + len;
diff --git a/src/cn-create.c b/src/cn-create.c
index d292ed3..105dfa6 100644
--- a/src/cn-create.c
+++ b/src/cn-create.c
@@ -25,6 +25,79 @@
}
MYLIB_EXPORT
+cn_cbor* cn_cbor_bool_create(bool value CBOR_CONTEXT, cn_cbor_errback* errp)
+{
+ cn_cbor* pcn = CN_CALLOC(context);
+ if (pcn == NULL) {
+ if (errp != NULL) {
+ errp->err = CN_CBOR_ERR_OUT_OF_MEMORY;
+ }
+ return NULL;
+ }
+
+ pcn->type = CN_CBOR_FALSE + (value != 0);
+ return pcn;
+}
+
+MYLIB_EXPORT
+cn_cbor* cn_cbor_simple_create(int simpleValue, CBOR_CONTEXT_COMMA cn_cbor_errback* errp)
+{
+ if (24 <= simpleValue && simpleValue <= 31) {
+ if (errp != NULL) {
+ errp->err = CN_CBOR_ERR_INVALID_PARAMETER;
+ }
+ return NULL;
+ }
+
+ cn_cbor* pcn = CN_CALLOC(context);
+ if (pcn == NULL) {
+ if (errp != NULL) {
+ errp->err = CN_CBOR_ERR_OUT_OF_MEMORY;
+ }
+ return NULL;
+ }
+
+ switch (simpleValue) {
+ case 20:
+ pcn->type = CN_CBOR_FALSE;
+ break;
+
+ case 21:
+ pcn->type = CN_CBOR_TRUE;
+ break;
+
+ case 22:
+ pcn->type = CN_CBOR_NULL;
+ break;
+
+ default:
+ pcn->type = CN_CBOR_SIMPLE;
+ pcn->v.uint = simpleValue;
+ break;
+ }
+ return pcn;
+}
+
+MYLIB_EXPORT
+cn_cbor* cn_cbor_tag_create(int tag, cn_cbor* child, CBOR_CONTEXT_COMMA cn_cbor_errback* perr)
+{
+ cn_cbor* pcnTag = CN_CALLOC(context);
+ if (pcnTag == NULL) {
+ if (perr != NULL) {
+ perr->err = CN_CBOR_ERR_OUT_OF_MEMORY;
+ }
+ return NULL;
+ }
+
+ pcnTag->type = CN_CBOR_TAG;
+ pcnTag->v.sint = tag;
+ pcnTag->first_child = child;
+ child->parent = pcnTag;
+
+ return pcnTag;
+}
+
+MYLIB_EXPORT
cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback* errp)
{
cn_cbor* ret;
@@ -37,27 +110,40 @@
}
MYLIB_EXPORT
-cn_cbor* cn_cbor_data_create(const uint8_t* data, int len CBOR_CONTEXT, cn_cbor_errback* errp)
+cn_cbor* cn_cbor_data_create(const uint8_t* data, int len, CBOR_CONTEXT_COMMA cn_cbor_errback* errp)
{
- cn_cbor* ret;
+ return cn_cbor_data_create2(data, len, CN_CBOR_FL_EXT_DATA CBOR_CONTEXT_PARAM, errp);
+}
+
+MYLIB_EXPORT
+cn_cbor* cn_cbor_data_create2(const uint8_t* data, int len, int flags CBOR_CONTEXT, cn_cbor_errback* errp)
+{
+ cn_cbor* ret = NULL;
INIT_CB(ret);
ret->type = CN_CBOR_BYTES;
ret->length = len;
ret->v.str = (const char*)data; // TODO: add v.ustr to the union?
-
+ ret->flags |= flags;
return ret;
}
MYLIB_EXPORT
-cn_cbor* cn_cbor_string_create(const char* data CBOR_CONTEXT, cn_cbor_errback* errp)
+cn_cbor* cn_cbor_string_create(const char* data, CBOR_CONTEXT_COMMA cn_cbor_errback* errp)
{
- cn_cbor* ret;
+ return cn_cbor_string_create2(data, CN_CBOR_FL_EXT_DATA CBOR_CONTEXT_PARAM, errp);
+}
+
+MYLIB_EXPORT
+cn_cbor* cn_cbor_string_create2(const char* data, int flags CBOR_CONTEXT, cn_cbor_errback* errp)
+{
+ cn_cbor* ret = NULL;
INIT_CB(ret);
ret->type = CN_CBOR_TEXT;
ret->length = strlen(data);
ret->v.str = data;
+ ret->flags |= flags;
return ret;
}
@@ -160,8 +246,16 @@
MYLIB_EXPORT
bool cn_cbor_mapput_string(cn_cbor* cb_map, const char* key, cn_cbor* cb_value CBOR_CONTEXT, cn_cbor_errback* errp)
{
- cn_cbor* cb_key;
+ return cn_cbor_mapput_string2(cb_map, key, cb_value, CN_CBOR_FL_EXT_DATA CBOR_CONTEXT_PARAM, errp);
+}
+MYLIB_EXPORT
+bool cn_cbor_mapput_string2(cn_cbor* cb_map,
+ const char* key,
+ cn_cbor* cb_value,
+ int flags CBOR_CONTEXT,
+ cn_cbor_errback* errp)
+{
// Make sure input is a map. Otherwise
if (!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP) {
if (errp) {
@@ -170,7 +264,7 @@
return false;
}
- cb_key = cn_cbor_string_create(key CBOR_CONTEXT_PARAM, errp);
+ cn_cbor* cb_key = cn_cbor_string_create2(key, flags CBOR_CONTEXT_PARAM, errp);
if (!cb_key) {
return false;
}
@@ -213,6 +307,68 @@
return true;
}
+MYLIB_EXPORT
+cn_cbor* cn_cbor_chunked_create(int type, CBOR_CONTEXT_COMMA cn_cbor_errback* errp)
+{
+ cn_cbor* ret;
+ INIT_CB(ret);
+
+ switch (type) {
+ case CN_CBOR_BYTES:
+ ret->type = CN_CBOR_BYTES_CHUNKED;
+ break;
+
+ case CN_CBOR_TEXT:
+ ret->type = CN_CBOR_TEXT_CHUNKED;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ ret->flags |= CN_CBOR_FL_INDEF;
+
+ return ret;
+}
+
+MYLIB_EXPORT
+bool cn_cbor_chunked_append(cn_cbor* cb_array, cn_cbor* cb_value, cn_cbor_errback* errp)
+{
+ // Make sure input is an array.
+ if (!cb_array || !cb_value || (cb_array->type != CN_CBOR_BYTES_CHUNKED && cb_array->type != CN_CBOR_TEXT_CHUNKED)) {
+ if (errp) {
+ errp->err = CN_CBOR_ERR_INVALID_PARAMETER;
+ }
+ return false;
+ }
+
+ if (cb_array->type == CN_CBOR_BYTES_CHUNKED && cb_value->type != CN_CBOR_BYTES) {
+ if (errp) {
+ errp->err = CN_CBOR_ERR_INVALID_PARAMETER;
+ }
+ return false;
+ }
+
+ if (cb_array->type == CN_CBOR_TEXT_CHUNKED && cb_value->type != CN_CBOR_TEXT) {
+ if (errp) {
+ errp->err = CN_CBOR_ERR_INVALID_PARAMETER;
+ }
+ return false;
+ }
+
+ cb_value->parent = cb_array;
+ cb_value->next = NULL;
+ if (cb_array->last_child) {
+ cb_array->last_child->next = cb_value;
+ }
+ else {
+ cb_array->first_child = cb_value;
+ }
+ cb_array->last_child = cb_value;
+ cb_array->length++;
+ return true;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/src/cn-encoder.c b/src/cn-encoder.c
index 80d4968..e9392c0 100644
--- a/src/cn-encoder.c
+++ b/src/cn-encoder.c
@@ -43,14 +43,13 @@
typedef struct _write_state {
uint8_t *buf;
- ssize_t offset;
- ssize_t size;
+ size_t offset;
+ size_t size;
} cn_write_state;
#define ensure_writable(sz) \
if ((ws->buf != NULL) && ((ws->offset < 0) || (ws->offset + (sz) > ws->size))) { \
- ws->offset = -1; \
- return; \
+ return false; \
}
#define write_byte_and_data(b, data, sz) \
@@ -75,7 +74,7 @@
ensure_writable(1); \
write_byte(b);
-static uint8_t _xlate[] = {
+static const uint8_t _xlate[] = {
IB_FALSE, /* CN_CBOR_FALSE */
IB_TRUE, /* CN_CBOR_TRUE */
IB_NIL, /* CN_CBOR_NULL */
@@ -99,16 +98,13 @@
return (cb->flags & CN_CBOR_FL_INDEF) != 0;
}
-static void _write_positive(cn_write_state *ws, cn_cbor_type typ, uint64_t val)
+static bool _write_positive(cn_write_state *ws, cn_cbor_type typ, uint64_t val)
{
- uint8_t ib;
-
assert((size_t)typ < sizeof(_xlate));
- ib = _xlate[typ];
+ const uint8_t ib = _xlate[typ];
if (ib == 0xFF) {
- ws->offset = -1;
- return;
+ return false;
}
if (val < 24) {
@@ -139,13 +135,15 @@
be64 = hton64p((const uint8_t *)&val);
write_byte_and_data(ib | 27, (const void *)&be64, 8);
}
+
+ return true;
}
#ifndef CBOR_NO_FLOAT
-static void _write_double(cn_write_state *ws, double val)
+static bool _write_double(cn_write_state *ws, double val, int size)
{
- float float_val = val;
- if (float_val == val) { /* 32 bits is enough and we aren't NaN */
+ float float_val = (float)val;
+ if (float_val == val && (size != 64)) { /* 32 bits is enough and we aren't NaN */
uint32_t be32;
uint16_t be16, u16;
union {
@@ -153,12 +151,13 @@
uint32_t u;
} u32;
u32.f = float_val;
- if ((u32.u & 0x1FFF) == 0) { /* worth trying half */
+
+ if ((u32.u & 0x1FFF) == 0 && (size == 0)) { /* worth trying half */
int s16 = (u32.u >> 16) & 0x8000;
- int exp = (u32.u >> 23) & 0xff;
- int mant = u32.u & 0x7fffff;
+ int exp = (int)((u32.u >> 23) & 0xff);
+ int mant = (int)(u32.u & 0x7fffff);
if (exp == 0 && mant == 0) {
- ; /* 0.0, -0.0 */
+ ; /* 0.0, -0.0 */
}
else if (exp >= 113 && exp <= 142) {
/* normalized */
@@ -182,7 +181,7 @@
be16 = hton16p((const uint8_t *)&u16);
write_byte_and_data(IB_PRIM | 25, (const void *)&be16, 2);
- return;
+ return true;
}
float32:
ensure_writable(5);
@@ -209,18 +208,22 @@
write_byte_and_data(IB_PRIM | 27, (const void *)&be64, 8);
}
+ return true;
}
#endif /* CBOR_NO_FLOAT */
// TODO: make public?
-typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
-void _visit(const cn_cbor *cb, cn_visit_func visitor, cn_visit_func breaker, void *context)
+typedef bool (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
+bool _visit(const cn_cbor *cb, cn_visit_func visitor, cn_visit_func breaker, void *context)
{
const cn_cbor *p = cb;
int depth = 0;
while (p) {
visit:
- visitor(p, depth, context);
+ if (!visitor(p, depth, context)) {
+ return false;
+ }
+
if (p->first_child) {
p = p->first_child;
depth++;
@@ -228,10 +231,14 @@
else {
// Empty indefinite
#ifdef CN_INCLUDE_DUMPER
- breaker(p, depth, context);
+ if (!breaker(p, depth, context)) {
+ return false;
+ }
#else
if (is_indefinite(p)) {
- breaker(p, depth, context);
+ if (!breaker(p, depth, context)) {
+ return false;
+ }
}
#endif
if (p->next) {
@@ -241,10 +248,14 @@
while (p->parent) {
depth--;
#ifdef CN_INCLUDE_DUMPER
- breaker(p->parent, depth, context);
+ if (!breaker(p->parent, depth, context)) {
+ return false;
+ }
#else
if (is_indefinite(p->parent)) {
- breaker(p->parent, depth, context);
+ if (!breaker(p->parent, depth, context)) {
+ return false;
+ }
}
#endif
if (p->parent->next) {
@@ -253,19 +264,19 @@
}
p = p->parent;
}
- return;
+ return true;
}
}
}
+ return true;
}
-#define CHECK(st) \
- (st); \
- if (ws->offset < 0) { \
- return; \
+#define CHECK(st) \
+ if (!(st)) { \
+ return false; \
}
-void _encoder_visitor(const cn_cbor *cb, int depth, void *context)
+bool _encoder_visitor(const cn_cbor *cb, int depth, void *context)
{
cn_write_state *ws = context;
UNUSED_PARAM(depth);
@@ -279,6 +290,7 @@
CHECK(_write_positive(ws, CN_CBOR_ARRAY, cb->length));
}
break;
+
case CN_CBOR_MAP:
if (is_indefinite(cb)) {
write_byte_ensured(IB_MAP | AI_INDEF);
@@ -287,6 +299,7 @@
CHECK(_write_positive(ws, CN_CBOR_MAP, cb->length / 2));
}
break;
+
case CN_CBOR_BYTES_CHUNKED:
case CN_CBOR_TEXT_CHUNKED:
write_byte_ensured(_xlate[cb->type] | AI_INDEF);
@@ -322,21 +335,23 @@
#ifndef CBOR_NO_FLOAT
case CN_CBOR_DOUBLE:
- CHECK(_write_double(ws, cb->v.dbl));
+ CHECK(_write_double(ws, cb->v.dbl, (cb->flags & CN_CBOR_FL_KEEP_FLOAT_SIZE) ? 64 : 0));
break;
case CN_CBOR_FLOAT:
- CHECK(_write_double(ws, cb->v.f));
-#endif /* CBOR_NO_FLOAT */
+ CHECK(_write_double(ws, cb->v.f, (cb->flags & CN_CBOR_FL_KEEP_FLOAT_SIZE) ? 32 : 0));
break;
+#endif /* CBOR_NO_FLOAT */
case CN_CBOR_INVALID:
- ws->offset = -1;
- break;
+ default:
+ return false;
}
+
+ return true;
}
-void _encoder_breaker(const cn_cbor *cb, int depth, void *context)
+bool _encoder_breaker(const cn_cbor *cb, int depth, void *context)
{
cn_write_state *ws = context;
UNUSED_PARAM(cb);
@@ -348,19 +363,19 @@
#ifdef CN_INCLUDE_DUMPER
}
#endif
+ return true;
}
ssize_t cn_cbor_encoder_write(uint8_t *buf, size_t buf_offset, size_t buf_size, const cn_cbor *cb)
{
- cn_write_state ws = {buf, buf_offset, buf_size};
+ cn_write_state ws = {buf, buf_offset, buf_size - buf_offset};
if (!ws.buf && ws.size <= 0) {
ws.size = (ssize_t)(((size_t)-1) / 2);
}
- _visit(cb, _encoder_visitor, _encoder_breaker, &ws);
- if (ws.offset < 0) {
+ if (!_visit(cb, _encoder_visitor, _encoder_breaker, &ws)) {
return -1;
}
- return ws.offset - buf_offset;
+ return (ssize_t)(ws.offset - buf_offset);
}
#ifdef __cplusplus
diff --git a/src/cn-print.c b/src/cn-print.c
index 38f844a..ca0f065 100644
--- a/src/cn-print.c
+++ b/src/cn-print.c
@@ -29,15 +29,15 @@
typedef struct _write_state {
char *rgbOutput;
- ssize_t ib;
+ size_t ib;
size_t cbLeft;
uint8_t *rgFlags;
const char *szIndentWith;
const char *szEndOfLine;
} cn_write_state;
-typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
-extern void _visit(const cn_cbor *cb, cn_visit_func visitor, cn_visit_func breaker, void *context);
+typedef bool (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
+extern bool _visit(const cn_cbor *cb, cn_visit_func visitor, cn_visit_func breaker, void *context);
const char RgchHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
@@ -53,40 +53,44 @@
return true;
}
-void write_data(cn_write_state *ws, const char *sz, size_t cb)
+bool write_data(cn_write_state *ws, const char *sz, size_t cb)
{
if (_isWritable(ws, cb)) {
- if (ws->rgbOutput != NULL)
+ if (ws->rgbOutput != NULL) {
memcpy(ws->rgbOutput + ws->ib, sz, cb);
+ }
ws->ib += cb;
+ return true;
}
+ return false;
}
-void _doIndent(cn_write_state *ws, int depth)
+bool _doIndent(cn_write_state *ws, int depth)
{
int i;
char *sz = ws->rgbOutput + ws->ib;
- size_t cbIndentWith = strlen(ws->szIndentWith);
- int cbIndent = depth * cbIndentWith;
+ const size_t cbIndentWith = strlen(ws->szIndentWith);
+ const size_t cbIndent = depth * cbIndentWith;
if (ws->rgbOutput == NULL) {
ws->ib += cbIndent;
- return;
+ return true;
}
- if (_isWritable(ws, cbIndent)) {
- for (i = 0; i < depth; i++) {
- memcpy(sz, ws->szIndentWith, cbIndentWith);
- sz += cbIndentWith;
- }
+ if (!_isWritable(ws, cbIndent)) {
+ return false;
+ }
+ for (i = 0; i < depth; i++) {
+ memcpy(sz, ws->szIndentWith, cbIndentWith);
+ sz += cbIndentWith;
}
ws->ib += cbIndent;
- return;
+ return true;
}
-void _print_encoder(const cn_cbor *cb, int depth, void *context)
+bool _print_encoder(const cn_cbor *cb, int depth, void *context)
{
int i;
char rgchT[256];
@@ -207,17 +211,22 @@
}
}
}
+ return true;
}
-void _print_breaker(const cn_cbor *cb, int depth, void *context)
+bool _print_breaker(const cn_cbor *cb, int depth, void *context)
{
cn_write_state *ws = (cn_write_state *)context;
switch (cb->type) {
case CN_CBOR_ARRAY:
if (ws->szIndentWith) {
- write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
- _doIndent(ws, depth);
+ if (!write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine))) {
+ return false;
+ }
+ if (!_doIndent(ws, depth)) {
+ return false;
+ }
}
write_data(ws, "]", 1);
@@ -237,6 +246,7 @@
default:
break;
}
+ return true;
}
ssize_t cn_cbor_printer_write(char *rgbBuffer,
@@ -249,10 +259,14 @@
char rgchZero[1] = {0};
cn_write_state ws = {rgbBuffer, 0, cbBuffer, flags, szIndentWith, szEndOfLine};
- _visit(cb, _print_encoder, _print_breaker, &ws);
- write_data(&ws, rgchZero, 1);
+ if (!_visit(cb, _print_encoder, _print_breaker, &ws)) {
+ return -1;
+ }
+ if (!write_data(&ws, rgchZero, 1)) {
+ return -1;
+ }
- return ws.ib;
+ return (ssize_t)ws.ib;
}
#ifdef EMACS_INDENTATION_HELPER
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index c0b76d7..f413543 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -15,6 +15,9 @@
endfunction()
create_test(cbor)
+# create_test(file)
+create_test(memory)
+target_sources(memory_test PRIVATE context.c)
include(CTest)
if(APPLE)
diff --git a/test/context.c b/test/context.c
new file mode 100644
index 0000000..75596c4
--- /dev/null
+++ b/test/context.c
@@ -0,0 +1,172 @@
+#include <stdlib.h>
+#ifdef _MSC_VER
+#endif
+#include <stdio.h>
+#include <memory.h>
+#include <assert.h>
+
+#include <cn-cbor/cn-cbor.h>
+
+#ifdef USE_CBOR_CONTEXT
+#include "context.h"
+
+typedef unsigned char byte;
+
+typedef struct {
+ cn_cbor_context context;
+ byte *pFirst;
+ unsigned int iFailLeft;
+ int allocCount;
+} MyContext;
+
+typedef struct _MyItem {
+ int allocNumber;
+ struct _MyItem *pNext;
+ size_t size;
+ byte pad[4];
+ byte data[4];
+} MyItem;
+
+bool CheckMemory(MyContext *pContext)
+{
+ MyItem *p = NULL;
+ // Walk memory and check every block
+
+ for (p = (MyItem *)pContext->pFirst; p != NULL; p = p->pNext) {
+ if (p->pad[0] == (byte)0xab) {
+ // Block has been freed
+ for (unsigned i = 0; i < p->size + 8; i++) {
+ if (p->pad[i] != (byte)0xab) {
+ fprintf(stderr, "Freed block is modified");
+ assert(false);
+ }
+ }
+ }
+ else if (p->pad[0] == (byte)0xef) {
+ for (unsigned i = 0; i < 4; i++) {
+ if ((p->pad[i] != (byte)0xef) || (p->pad[i + 4 + p->size] != (byte)0xef)) {
+ fprintf(stderr, "Current block was overrun");
+ assert(false);
+ }
+ }
+ }
+ else {
+ fprintf(stderr, "Incorrect pad value");
+ assert(false);
+ }
+ }
+
+ return true;
+}
+
+void *MyCalloc(size_t count, size_t size, void *context)
+{
+ MyItem *pb = NULL;
+ MyContext *myContext = (MyContext *)context;
+
+ CheckMemory(myContext);
+
+ if (myContext->iFailLeft != -1) {
+ if (myContext->iFailLeft == 0) {
+ return NULL;
+ }
+ myContext->iFailLeft--;
+ }
+
+ pb = (MyItem *)malloc(sizeof(MyItem) + count * size);
+
+ memset(pb, 0xef, sizeof(MyItem) + count * size);
+ memset(&pb->data, 0, count * size);
+
+ pb->pNext = (struct _MyItem *)myContext->pFirst;
+ myContext->pFirst = (byte *)pb;
+ pb->size = count * size;
+ pb->allocNumber = myContext->allocCount++;
+
+ return &pb->data;
+}
+
+void MyFree(void *ptr, void *context)
+{
+ MyItem *pb = (MyItem *)((byte *)ptr - sizeof(MyItem) + 4);
+ MyContext *myContext = (MyContext *)context;
+ MyItem *pItem = NULL;
+
+ CheckMemory(myContext);
+ if (ptr == NULL) {
+ return;
+ }
+
+ for (pItem = (MyItem *)myContext->pFirst; pItem != NULL; pItem = pItem->pNext) {
+ if (pItem == pb) {
+ break;
+ }
+ }
+
+ if (pItem == NULL) {
+ // Not an item we allocated
+ assert(false);
+ }
+
+ if (pb->pad[0] == 0xab) {
+ // Item has already been freed
+ assert(false);
+ }
+
+ memset(&pb->pad, 0xab, pb->size + 8);
+}
+
+cn_cbor_context *CreateContext(unsigned int iFailPoint)
+{
+ MyContext *p = malloc(sizeof(MyContext));
+
+ p->context.calloc_func = MyCalloc;
+ p->context.free_func = MyFree;
+ p->context.context = p;
+ p->pFirst = NULL;
+ p->iFailLeft = iFailPoint;
+ p->allocCount = 0;
+
+ return &p->context;
+}
+
+void FreeContext(cn_cbor_context *pContext)
+{
+ MyContext *myContext = (MyContext *)pContext;
+ MyItem *pItem;
+ MyItem *pItem2;
+
+ CheckMemory(myContext);
+
+ for (pItem = (MyItem *)myContext->pFirst; pItem != NULL; pItem = pItem2) {
+ pItem2 = pItem->pNext;
+ free(pItem);
+ }
+
+ free(myContext);
+
+ return;
+}
+
+int IsContextEmpty(const cn_cbor_context *pContext)
+{
+ MyContext *myContext = (MyContext *)pContext;
+ MyItem *p;
+ int i = 0;
+
+ // Walk memory and check every block
+
+ for (p = (MyItem *)myContext->pFirst; p != NULL; p = p->pNext) {
+ if (p->pad[0] == (byte)0xab) {
+ // Block has been freed
+ }
+ else {
+ // This block has not been freed
+ i += 1;
+ }
+ }
+
+ return i;
+}
+
+#endif // USE_CBOR_CONTEXT
diff --git a/test/context.h b/test/context.h
new file mode 100644
index 0000000..96c2315
--- /dev/null
+++ b/test/context.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#ifdef USE_CBOR_CONTEXT
+
+extern cn_cbor_context* CreateContext(unsigned int failAt);
+extern void FreeContext(cn_cbor_context* pContext);
+int IsContextEmpty(const cn_cbor_context* pContext);
+
+#endif // USE_CBOR_CONTEXT
diff --git a/test/file_test.c b/test/file_test.c
new file mode 100644
index 0000000..bc0e820
--- /dev/null
+++ b/test/file_test.c
@@ -0,0 +1,179 @@
+#ifndef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "cn-cbor/cn-cbor.h"
+
+#ifdef USE_CBOR_CONTEXT
+#define CBOR_CONTEXT_PARAM , NULL
+#else
+#define CBOR_CONTEXT_PARAM
+#endif
+
+#define ERROR(msg, p) fprintf(stderr, "ERROR: " msg " %s\n", (p));
+
+static unsigned char *load_file(const char *filepath, unsigned char **end)
+{
+ struct stat st;
+ if (stat(filepath, &st) == -1) {
+ ERROR("can't find file", filepath);
+ return 0;
+ }
+ int fd = open(filepath, _O_RDONLY | _O_BINARY);
+ if (fd == -1) {
+ ERROR("can't open file", filepath);
+ return 0;
+ }
+ unsigned char *text = malloc(st.st_size + 1); // this is not going to be freed
+ if (st.st_size != read(fd, text, st.st_size)) {
+ ERROR("can't read file", filepath);
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ text[st.st_size] = '\0';
+ *end = text + st.st_size;
+ return text;
+}
+
+static void dump(const cn_cbor *cb, char *out, char **end, int indent)
+{
+ if (!cb)
+ goto done;
+ int i;
+ cn_cbor *cp;
+ char finchar = ')'; /* most likely */
+
+#define CPY(s, l) \
+ memcpy(out, s, l); \
+ out += l;
+#define OUT(s) CPY(s, sizeof(s) - 1)
+#define PRF(f, a) out += sprintf(out, f, a)
+
+ for (i = 0; i < indent; i++)
+ *out++ = ' ';
+ switch (cb->type) {
+ case CN_CBOR_TEXT_CHUNKED:
+ OUT("(_\n");
+ goto sequence;
+ case CN_CBOR_BYTES_CHUNKED:
+ OUT("(_\n\n");
+ goto sequence;
+ case CN_CBOR_TAG:
+ PRF("%ld(\n", cb->v.sint);
+ goto sequence;
+ case CN_CBOR_ARRAY:
+ finchar = ']';
+ OUT("[\n");
+ goto sequence;
+ case CN_CBOR_MAP:
+ finchar = '}';
+ OUT("{\n");
+ goto sequence;
+ sequence:
+ for (cp = cb->first_child; cp; cp = cp->next) {
+ dump(cp, out, &out, indent + 2);
+ }
+ for (i = 0; i < indent; i++)
+ *out++ = ' ';
+ *out++ = finchar;
+ break;
+ case CN_CBOR_BYTES:
+ OUT("h'");
+ for (i = 0; i < cb->length; i++)
+ PRF("%02x", cb->v.str[i] & 0xff);
+ *out++ = '\'';
+ break;
+ case CN_CBOR_TEXT:
+ *out++ = '"';
+ CPY(cb->v.str, cb->length); /* should escape stuff */
+ *out++ = '"';
+ break;
+ case CN_CBOR_NULL:
+ OUT("null");
+ break;
+ case CN_CBOR_TRUE:
+ OUT("true");
+ break;
+ case CN_CBOR_FALSE:
+ OUT("false");
+ break;
+ case CN_CBOR_UNDEF:
+ OUT("simple(23)");
+ break;
+ case CN_CBOR_INT:
+ PRF("%ld", cb->v.sint);
+ break;
+ case CN_CBOR_UINT:
+ PRF("%lu", cb->v.uint);
+ break;
+ case CN_CBOR_DOUBLE:
+ PRF("%e", cb->v.dbl);
+ break;
+ case CN_CBOR_SIMPLE:
+ PRF("simple(%ld)", cb->v.sint);
+ break;
+ default:
+ PRF("???%d???", cb->type);
+ break;
+ }
+ *out++ = '\n';
+done:
+ *end = out;
+}
+
+const char *err_name[] = {
+ "CN_CBOR_NO_ERROR",
+ "CN_CBOR_ERR_OUT_OF_DATA",
+ "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED",
+ "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP",
+ "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF",
+ "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF",
+ "CN_CBOR_ERR_RESERVED_AI",
+ "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
+ "CN_CBOR_ERR_OUT_OF_MEMORY",
+ "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED",
+};
+
+static void cn_cbor_decode_test(const unsigned char *buf, int len)
+{
+ struct cn_cbor_errback back;
+ const cn_cbor *ret = cn_cbor_decode(buf, len CBOR_CONTEXT_PARAM, &back);
+ if (ret)
+ printf("oops 1");
+ printf("%s at %d\n", err_name[back.err], back.pos);
+}
+
+int main(void)
+{
+ char buf[100000];
+ unsigned char *end;
+ char *bufend;
+ unsigned char *s = load_file("cases.cbor", &end);
+ printf("%zd\n", end - s);
+ cn_cbor *cb = cn_cbor_decode(s, end - s CBOR_CONTEXT_PARAM, 0);
+ if (cb) {
+ dump(cb, buf, &bufend, 0);
+ *bufend = 0;
+ printf("%s\n", buf);
+ cn_cbor_free(cb CBOR_CONTEXT_PARAM);
+ cb = 0; /* for leaks testing */
+ }
+ cn_cbor_decode_test((const unsigned char *)"\xff", 1); /* break outside indef */
+ cn_cbor_decode_test((const unsigned char *)"\x1f", 1); /* mt undef for indef */
+ cn_cbor_decode_test((const unsigned char *)"\x00\x00", 2); /* not all data consumed */
+ cn_cbor_decode_test((const unsigned char *)"\x81", 1); /* out of data */
+ cn_cbor_decode_test((const unsigned char *)"\x1c", 1); /* reserved ai */
+ cn_cbor_decode_test((const unsigned char *)"\xbf\x00\xff", 3); /* odd size indef map */
+ cn_cbor_decode_test((const unsigned char *)"\x7f\x40\xff", 3); /* wrong nesting in indef string */
+ // system("leaks test");
+}
+
+/* cn-cbor.c:112: CN_CBOR_FAIL("out of memory"); */
diff --git a/test/memory_test.c b/test/memory_test.c
new file mode 100644
index 0000000..88c66f7
--- /dev/null
+++ b/test/memory_test.c
@@ -0,0 +1,209 @@
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#else
+#endif
+#include <string.h>
+
+#include "cn-cbor/cn-cbor.h"
+#include "context.h"
+
+#ifdef USE_CBOR_CONTEXT
+#define CBOR_CONTEXT_PARAM , NULL
+#else
+#define CBOR_CONTEXT_PARAM
+#endif
+
+int CFails;
+
+void CreateTests()
+{
+ cn_cbor_context* context = CreateContext(-1);
+
+ // Check the simple create/delete for memory leaks.
+
+ cn_cbor* cbor = cn_cbor_map_create(context, NULL);
+ cn_cbor_free(cbor, context);
+
+ byte* pb = (byte*)context->calloc_func(10, 10, context);
+ cbor = cn_cbor_data_create2(pb, 10, 0, context, NULL);
+ cn_cbor_free(cbor, context);
+
+ char* sz = (char*)context->calloc_func(10, 1, context);
+ strcpy(sz, "ABC");
+ cbor = cn_cbor_string_create2(sz, 0, context, NULL);
+ cn_cbor_free(cbor, context);
+
+ cbor = cn_cbor_string_create("This is a string", context, NULL);
+ cn_cbor_dont_free_data(cbor);
+ cn_cbor_free(cbor, context);
+
+ cbor = cn_cbor_int_create(20, context, NULL);
+ cn_cbor_free(cbor, context);
+
+#ifndef CBOR_NO_FLOATS
+ cbor = cn_cbor_float_create((float)20.2, context, NULL);
+ cn_cbor_free(cbor, context);
+
+ cbor = cn_cbor_double_create(203.3, context, NULL);
+ cn_cbor_free(cbor, context);
+#endif
+
+ cbor = cn_cbor_array_create(context, NULL);
+ cn_cbor_free(cbor, context);
+
+ cbor = cn_cbor_bool_create(false, context, NULL);
+ cn_cbor_free(cbor, context);
+
+ cbor = cn_cbor_null_create(context, NULL);
+ cn_cbor_free(cbor, context);
+
+ cbor = cn_cbor_simple_create(23, context, NULL);
+ cn_cbor_free(cbor, context);
+
+ if (IsContextEmpty(context) > 0) {
+ CFails += 1;
+ }
+
+ // Test more complex stuctures
+
+ cn_cbor* cbor_map = cn_cbor_map_create(context, NULL);
+ cn_cbor* cbor_array = cn_cbor_array_create(context, NULL);
+ cn_cbor* cbor2 = NULL;
+
+ for (int i = 0; i < 10; i++) {
+ cbor2 = cn_cbor_int_create(i, context, NULL);
+ cbor = cn_cbor_int_create(i, context, NULL);
+ cn_cbor_map_put(cbor_map, cbor, cbor2, NULL);
+
+ cbor = cn_cbor_int_create(i, context, NULL);
+ cn_cbor_array_append(cbor_array, cbor, NULL);
+ }
+
+ cn_cbor_mapput_string(cbor_map, "KEY", cn_cbor_int_create(20, context, NULL), context, NULL);
+ sz = (char*)context->calloc_func(10, 1, context);
+ strcpy(sz, "ABC");
+ cn_cbor_mapput_string2(cbor_map, sz, cn_cbor_int_create(-20, context, NULL), 0, context, NULL);
+
+ cn_cbor_mapput_int(cbor_map, -22, cn_cbor_simple_create(99, context, NULL), context, NULL);
+
+ cn_cbor_array_append(cbor_array, cbor_map, NULL);
+ cbor_array = cn_cbor_tag_create(99, cbor_array, context, NULL);
+ cn_cbor_free(cbor_array, context);
+
+ if (IsContextEmpty(context) > 0) {
+ CFails += 1;
+ }
+}
+
+void DecoderTests() {}
+
+void EncoderTests()
+{
+ cn_cbor_context* context = CreateContext(-1);
+
+ cn_cbor* cborRoot = cn_cbor_array_create(context, NULL);
+
+ cn_cbor* cbor = cn_cbor_array_create(context, NULL);
+ cbor->flags |= CN_CBOR_FL_INDEF;
+
+ cn_cbor* cbor2 = cn_cbor_simple_create(22, context, NULL);
+ cn_cbor_array_append(cbor, cbor2, NULL);
+ cbor2 = cn_cbor_simple_create(21, context, NULL);
+ cn_cbor_array_append(cbor, cbor2, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_bool_create(true, context, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_map_create(context, NULL);
+ cbor2 = cn_cbor_string_create("Text1", context, NULL);
+ cn_cbor_mapput_int(cbor, 5, cbor2, context, NULL);
+ cbor2 = cn_cbor_int_create(99, context, NULL);
+ cn_cbor_mapput_string(cbor, "key", cbor2, context, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_map_create(context, NULL);
+ cbor->flags |= CN_CBOR_FL_INDEF;
+ cbor2 = cn_cbor_string_create("Text1", context, NULL);
+ cn_cbor_mapput_int(cbor, 5, cbor2, context, NULL);
+ cbor2 = cn_cbor_int_create(99, context, NULL);
+ cn_cbor_mapput_string(cbor, "key", cbor2, context, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_chunked_create(CN_CBOR_BYTES, context, NULL);
+ byte* pb = context->calloc_func(10, 10, context);
+ cbor2 = cn_cbor_data_create2(pb, 100, 0, context, NULL);
+ cn_cbor_chunked_append(cbor, cbor2, NULL);
+ byte data2[20] = {1, 2, 3, 4, 5, 6, 7};
+ cbor2 = cn_cbor_data_create(data2, 20, context, NULL);
+ cn_cbor_chunked_append(cbor, cbor2, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_chunked_create(CN_CBOR_TEXT, context, NULL);
+ cbor2 = cn_cbor_string_create("This is a string", context, NULL);
+ cn_cbor_chunked_append(cbor, cbor2, NULL);
+ char* s = context->calloc_func(20, 1, context);
+ strcpy(s, "Hi Mom");
+ cbor2 = cn_cbor_string_create2(s, 0, context, NULL);
+ cn_cbor_chunked_append(cbor, cbor2, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_simple_create(4, context, NULL);
+ cbor = cn_cbor_tag_create(99, cbor, context, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+#ifndef CBOR_NO_FLOATS
+ cbor = cn_cbor_float_create(9, context, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_double_create(33.225932523223, context, NULL);
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_float_create(9, context, NULL);
+ cbor->flags |= CN_CBOR_FL_KEEP_FLOAT_SIZE;
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+
+ cbor = cn_cbor_double_create(9, context, NULL);
+ cbor->flags |= CN_CBOR_FL_KEEP_FLOAT_SIZE;
+ cn_cbor_array_append(cborRoot, cbor, NULL);
+#endif
+
+ ssize_t cb = cn_cbor_encoder_write(NULL, 0, 0, cborRoot);
+ pb = (byte*)context->calloc_func(cb + 2, 1, context);
+
+ ssize_t cb2 = cn_cbor_encoder_write(pb, 0, cb - 1, cborRoot);
+ if (cb2 != -1) {
+ CFails += 1;
+ }
+
+ cb2 = cn_cbor_encoder_write(pb, 0, cb, cborRoot);
+ if (cb2 != cb) {
+ CFails += 1;
+ }
+
+ cb2 = cn_cbor_encoder_write(pb, 0, cb + 1, cborRoot);
+ if (cb != cb2) {
+ CFails += 1;
+ }
+
+ cn_cbor_free(cborRoot, context);
+
+ cborRoot = cn_cbor_decode(pb, cb2, context, NULL);
+
+ cn_cbor_free(cborRoot, context);
+
+ context->free_func(pb, context);
+
+ if (IsContextEmpty(context) > 0) {
+ CFails += 1;
+ }
+}
+
+int main(void)
+{
+ CreateTests();
+ EncoderTests();
+ DecoderTests();
+
+ return CFails;
+}