diff --git a/Doxyfile b/Doxyfile index ec78171..fbfd5ed 100644 --- a/Doxyfile +++ b/Doxyfile @@ -21,8 +21,9 @@ GENERATE_LATEX = no STRIP_FROM_PATH = . ./src PREDEFINED = \ - static= \ inline= \ + MPACK_INLINE= \ + MPACK_ALWAYS_INLINE= \ \ MPACK_READER=1 \ MPACK_WRITER=1 \ diff --git a/README.md b/README.md index 3d16289..a231df3 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Conceptually, MessagePack stores data similarly to JSON: they are both composed - Binary data is not supported by JSON at all. Small binary blobs such as icons and thumbnails need to be Base64 encoded or passed out-of-band. -The above issues greatly increase the complexity of the decoder. Full-featured JSON decoders are quite large, and minimal decoders tend to leave out such features as string unescaping and float parsing, instead leaving these up to the user or platform. This can lead to hard-to-find and/or platform-specific bugs. This also significantly decreases performance, making JSON unattractive for use in applications such as mobile games. +The above issues greatly increase the complexity of the decoder. Full-featured JSON decoders are quite large, and minimal decoders tend to leave out such features as string unescaping and float parsing, instead leaving these up to the user or platform. This can lead to hard-to-find platform-specific and locale-specific bugs. This also significantly decreases performance, making JSON unattractive for use in applications such as mobile games. While the space inefficiencies of JSON can be partially mitigated through minification and compression, the performance inefficiencies cannot. More importantly, if you are minifying and compressing the data, then why use a human-readable format in the first place? diff --git a/SConstruct b/SConstruct index e0aeeab..be5a007 100644 --- a/SConstruct +++ b/SConstruct @@ -11,7 +11,7 @@ for x in os.environ.keys(): env.Append(CPPFLAGS = [ "-Wall", "-Wextra", "-Werror", - "-Wconversion", "-Wno-sign-conversion", + "-Wconversion", "-Wno-sign-conversion", "-Wundef", "-Isrc", "-Itest", "-DMPACK_SCONS=1", "-g", @@ -36,14 +36,13 @@ allfeatures = [ ] noioconfigs = [ "-DMPACK_STDLIB=1", - "-DMPACK_SETJMP=1", "-DMPACK_MALLOC=test_malloc", "-DMPACK_FREE=test_free", ] allconfigs = noioconfigs + ["-DMPACK_STDIO=1"] debugflags = ["-DDEBUG", "-O0"] -releaseflags = ["-Os"] +releaseflags = ["-Os"] # If you change this, also change the MPACK_OPTIMIZE_FOR_SIZE below to test the opposite cflags = ["-std=c99", "-Wc++-compat"] @@ -74,6 +73,8 @@ AddBuild("debug", allfeatures + allconfigs + debugflags + cflags, []) if ARGUMENTS.get('all'): AddBuild("release", allfeatures + allconfigs + releaseflags + cflags, []) + AddBuild("release-speed", ["-DMPACK_OPTIMIZE_FOR_SIZE=0"] + + allfeatures + allconfigs + releaseflags + cflags, []) # feature subsets with default configuration AddBuilds("empty", allconfigs + cflags, []) diff --git a/src/mpack-config.h.sample b/src/mpack-config.h.sample index cc4f75a..d853e82 100644 --- a/src/mpack-config.h.sample +++ b/src/mpack-config.h.sample @@ -2,7 +2,10 @@ /** * This is a sample MPack configuration file. Copy it to mpack-config.h somewhere * in your project's include tree and, optionally, edit it to suit your setup. + * * In most cases you can leave this file with the default config. + * + * You can also override the default configuration by pre-defining options to 0 or 1. */ #ifndef MPACK_CONFIG_H @@ -14,16 +17,24 @@ */ /** Enables compilation of the base Tag Reader. */ +#ifndef MPACK_READER #define MPACK_READER 1 +#endif /** Enables compilation of the static Expect API. */ +#ifndef MPACK_EXPECT #define MPACK_EXPECT 1 +#endif /** Enables compilation of the dynamic Node API. */ +#ifndef MPACK_NODE #define MPACK_NODE 1 +#endif /** Enables compilation of the Writer. */ +#ifndef MPACK_WRITER #define MPACK_WRITER 1 +#endif /* @@ -34,13 +45,17 @@ * Enables the use of C stdlib. This allows the library to use malloc * for debugging and in allocation helpers. */ +#ifndef MPACK_STDLIB #define MPACK_STDLIB 1 +#endif /** * Enables the use of C stdio. This adds helpers for easily * reading/writing C files and makes debugging easier. */ +#ifndef MPACK_STDIO #define MPACK_STDIO 1 +#endif /** * \def MPACK_MALLOC @@ -80,16 +95,6 @@ #define MPACK_FREE free #endif -/** - * Enables the setjmp()/longjmp() error handling option. MPACK_MALLOC is required. - * - * Note that you don't have to use it; this just enables the option. It can be - * disabled to avoid the dependency on setjmp.h . - */ -#if defined(MPACK_MALLOC) -#define MPACK_SETJMP 1 -#endif - /* * Debugging options @@ -107,10 +112,8 @@ * files. Your entire project must be compiled with the same value of * MPACK_DEBUG. (This is why NDEBUG is not used.) */ -#if defined(DEBUG) || defined(_DEBUG) +#if !defined(MPACK_DEBUG) && (defined(DEBUG) || defined(_DEBUG)) #define MPACK_DEBUG 1 -#else -#define MPACK_DEBUG 0 #endif /** @@ -122,7 +125,9 @@ * Asserts are only used when MPACK_DEBUG is enabled, and can be triggered * by bugs in mpack or bugs due to incorrect usage of mpack. */ +#ifndef MPACK_CUSTOM_ASSERT #define MPACK_CUSTOM_ASSERT 0 +#endif /** * \def MPACK_READ_TRACKING @@ -133,7 +138,10 @@ * This is enabled by default in debug builds (provided a malloc() is * available.) */ -#if MPACK_DEBUG && MPACK_READER && defined(MPACK_MALLOC) +#if !defined(MPACK_READ_TRACKING) && \ + defined(MPACK_DEBUG) && MPACK_DEBUG && \ + defined(MPACK_READER) && MPACK_READER && \ + defined(MPACK_MALLOC) #define MPACK_READ_TRACKING 1 #endif @@ -151,7 +159,10 @@ * This is enabled by default in debug builds (provided a malloc() is * available.) */ -#if MPACK_DEBUG && MPACK_WRITER && defined(MPACK_MALLOC) +#if !defined(MPACK_WRITE_TRACKING) && \ + defined(MPACK_DEBUG) && MPACK_DEBUG && \ + defined(MPACK_WRITER) && MPACK_WRITER && \ + defined(MPACK_MALLOC) #define MPACK_WRITE_TRACKING 1 #endif @@ -160,16 +171,39 @@ * Miscellaneous */ +/** + * Whether to optimize for size or speed. Optimizing for size causes + * very few functions to be declared inline, and can save a couple + * kilobytes of space in the resulting executable. + * + * This automatically detects -Os with GCC/Clang. Unfortunately there + * doesn't seem to be a macro defined for /Os under MSVC. + * + * This feature is currently experimental and may be removed in a + * future release. + */ +#ifndef MPACK_OPTIMIZE_FOR_SIZE +#ifdef __OPTIMIZE_SIZE__ +#define MPACK_OPTIMIZE_FOR_SIZE 1 +#else +#define MPACK_OPTIMIZE_FOR_SIZE 0 +#endif +#endif + /** * Stack space to use when initializing a reader or writer with a * stack-allocated buffer. */ +#ifndef MPACK_STACK_SIZE #define MPACK_STACK_SIZE 4096 +#endif /** * Buffer size to use for allocated buffers (such as for a file writer.) */ +#ifndef MPACK_BUFFER_SIZE #define MPACK_BUFFER_SIZE 65536 +#endif /** * Number of nodes in each allocated node page. @@ -181,20 +215,26 @@ * best performance, and has very little waste when parsing small * messages. */ +#ifndef MPACK_NODE_PAGE_SIZE #define MPACK_NODE_PAGE_SIZE (4096 / sizeof(mpack_node_t)) +#endif /** * The initial depth for the node parser. When MPACK_MALLOC is available, * the node parser has no practical depth limit, and it is not recursive * so there is no risk of overflowing the call stack. */ +#ifndef MPACK_NODE_INITIAL_DEPTH #define MPACK_NODE_INITIAL_DEPTH 8 +#endif /** * The maximum depth for the node parser if MPACK_MALLOC is not available. * The parsing stack is placed on the call stack. */ +#ifndef MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC #define MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC 32 +#endif #endif diff --git a/src/mpack/mpack-common.c b/src/mpack/mpack-common.c index 2577280..56c5114 100644 --- a/src/mpack/mpack-common.c +++ b/src/mpack/mpack-common.c @@ -162,7 +162,7 @@ int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right) { #define MPACK_TRACKING_INITIAL_CAPACITY 8 #endif -MPACK_INTERNAL_STATIC mpack_error_t mpack_track_init(mpack_track_t* track) { +mpack_error_t mpack_track_init(mpack_track_t* track) { track->count = 0; track->capacity = MPACK_TRACKING_INITIAL_CAPACITY; track->elements = (mpack_track_element_t*)MPACK_MALLOC(sizeof(mpack_track_element_t) * track->capacity); @@ -171,7 +171,7 @@ MPACK_INTERNAL_STATIC mpack_error_t mpack_track_init(mpack_track_t* track) { return mpack_ok; } -MPACK_INTERNAL_STATIC mpack_error_t mpack_track_grow(mpack_track_t* track) { +mpack_error_t mpack_track_grow(mpack_track_t* track) { mpack_assert(track->elements, "null track elements!"); mpack_assert(track->count == track->capacity, "incorrect growing?"); @@ -189,3 +189,32 @@ MPACK_INTERNAL_STATIC mpack_error_t mpack_track_grow(mpack_track_t* track) { #endif + + +/* The below code is from Bjoern Hoehrmann's Flexible and Economical */ +/* UTF-8 decoder, modified to support MPack inlining and add the mpack prefix. */ + +/* Copyright (c) 2008-2010 Bjoern Hoehrmann */ +/* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. */ + +const uint8_t mpack_utf8d[] = { + /* The first part of the table maps bytes to character classes that */ + /* to reduce the size of the transition table and create bitmasks. */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + + /* The second part is a transition table that maps a combination */ + /* of a state of the automaton and a character class to a state. */ + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12, +}; + diff --git a/src/mpack/mpack-common.h b/src/mpack/mpack-common.h index 3b2c55a..65a2f56 100644 --- a/src/mpack/mpack-common.h +++ b/src/mpack/mpack-common.h @@ -30,19 +30,13 @@ #include "mpack-platform.h" -/** @cond */ -#ifndef MPACK_STACK_SIZE -#define MPACK_STACK_SIZE 4096 -#endif -/** @endcond */ - /* Version information */ #define MPACK_VERSION_MAJOR 0 /**< The major version number of MPack. */ -#define MPACK_VERSION_MINOR 5 /**< The minor version number of MPack. */ -#define MPACK_VERSION_PATCH 1 /**< The patch version number of MPack. */ +#define MPACK_VERSION_MINOR 6 /**< The minor version number of MPack. */ +#define MPACK_VERSION_PATCH 0 /**< The patch version number of MPack. */ /** A number containing the version number of MPack for comparison purposes. */ #define MPACK_VERSION ((MPACK_VERSION_MAJOR * 10000) + \ @@ -181,7 +175,7 @@ typedef struct mpack_tag_t { } mpack_tag_t; /** Generates a nil tag. */ -static inline mpack_tag_t mpack_tag_nil(void) { +MPACK_INLINE mpack_tag_t mpack_tag_nil(void) { mpack_tag_t ret; mpack_memset(&ret, 0, sizeof(ret)); ret.type = mpack_type_nil; @@ -189,7 +183,7 @@ static inline mpack_tag_t mpack_tag_nil(void) { } /** Generates a signed int tag. */ -static inline mpack_tag_t mpack_tag_int(int64_t value) { +MPACK_INLINE mpack_tag_t mpack_tag_int(int64_t value) { mpack_tag_t ret; mpack_memset(&ret, 0, sizeof(ret)); ret.type = mpack_type_int; @@ -198,7 +192,7 @@ static inline mpack_tag_t mpack_tag_int(int64_t value) { } /** Generates an unsigned int tag. */ -static inline mpack_tag_t mpack_tag_uint(uint64_t value) { +MPACK_INLINE mpack_tag_t mpack_tag_uint(uint64_t value) { mpack_tag_t ret; mpack_memset(&ret, 0, sizeof(ret)); ret.type = mpack_type_uint; @@ -207,7 +201,7 @@ static inline mpack_tag_t mpack_tag_uint(uint64_t value) { } /** Generates a bool tag. */ -static inline mpack_tag_t mpack_tag_bool(bool value) { +MPACK_INLINE mpack_tag_t mpack_tag_bool(bool value) { mpack_tag_t ret; mpack_memset(&ret, 0, sizeof(ret)); ret.type = mpack_type_bool; @@ -216,7 +210,7 @@ static inline mpack_tag_t mpack_tag_bool(bool value) { } /** Generates a float tag. */ -static inline mpack_tag_t mpack_tag_float(float value) { +MPACK_INLINE mpack_tag_t mpack_tag_float(float value) { mpack_tag_t ret; mpack_memset(&ret, 0, sizeof(ret)); ret.type = mpack_type_float; @@ -225,7 +219,7 @@ static inline mpack_tag_t mpack_tag_float(float value) { } /** Generates a double tag. */ -static inline mpack_tag_t mpack_tag_double(double value) { +MPACK_INLINE mpack_tag_t mpack_tag_double(double value) { mpack_tag_t ret; mpack_memset(&ret, 0, sizeof(ret)); ret.type = mpack_type_double; @@ -260,7 +254,7 @@ int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right); * * Floating point numbers are compared bit-for-bit, not using the language's operator==. */ -static inline bool mpack_tag_equal(mpack_tag_t left, mpack_tag_t right) { +MPACK_INLINE bool mpack_tag_equal(mpack_tag_t left, mpack_tag_t right) { return mpack_tag_cmp(left, right) == 0; } @@ -323,14 +317,22 @@ typedef struct mpack_track_t { } mpack_track_t; #if MPACK_INTERNAL -MPACK_INTERNAL_STATIC mpack_error_t mpack_track_init(mpack_track_t* track); -MPACK_INTERNAL_STATIC mpack_error_t mpack_track_grow(mpack_track_t* track); +mpack_error_t mpack_track_init(mpack_track_t* track); +mpack_error_t mpack_track_grow(mpack_track_t* track); // These look like some overly large inline functions, but really // they are mostly asserts. They boil down to just a few checks // and assignments. -static inline mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint64_t count) { +MPACK_INLINE_SPEED mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint64_t count); +MPACK_INLINE_SPEED mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type); +MPACK_INLINE_SPEED mpack_error_t mpack_track_element(mpack_track_t* track, bool read); +MPACK_INLINE_SPEED mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, uint64_t count); +MPACK_INLINE_SPEED mpack_error_t mpack_track_check_empty(mpack_track_t* track); +MPACK_INLINE_SPEED mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint64_t count) { mpack_assert(track->elements, "null track elements!"); // maps have twice the number of elements (key/value pairs) @@ -351,7 +353,7 @@ static inline mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t return mpack_ok; } -static inline mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) { +MPACK_INLINE_SPEED mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) { mpack_assert(track->elements, "null track elements!"); if (track->count == 0) { @@ -378,7 +380,7 @@ static inline mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t t return mpack_ok; } -static inline mpack_error_t mpack_track_element(mpack_track_t* track, bool read) { +MPACK_INLINE_SPEED mpack_error_t mpack_track_element(mpack_track_t* track, bool read) { MPACK_UNUSED(read); mpack_assert(track->elements, "null track elements!"); @@ -404,7 +406,7 @@ static inline mpack_error_t mpack_track_element(mpack_track_t* track, bool read) return mpack_ok; } -static inline mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, uint64_t count) { +MPACK_INLINE_SPEED mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, uint64_t count) { MPACK_UNUSED(read); mpack_assert(track->elements, "null track elements!"); @@ -431,7 +433,7 @@ static inline mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, u return mpack_ok; } -static inline mpack_error_t mpack_track_check_empty(mpack_track_t* track) { +MPACK_INLINE_SPEED mpack_error_t mpack_track_check_empty(mpack_track_t* track) { if (track->count != 0) { mpack_assert(0, "unclosed %s", mpack_type_to_string(track->elements[0].type)); return mpack_error_bug; @@ -439,12 +441,13 @@ static inline mpack_error_t mpack_track_check_empty(mpack_track_t* track) { return mpack_ok; } -static inline mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel) { +MPACK_INLINE_SPEED mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel) { mpack_error_t error = cancel ? mpack_ok : mpack_track_check_empty(track); MPACK_FREE(track->elements); track->elements = NULL; return error; } +#endif #endif /** @endcond */ @@ -455,7 +458,7 @@ static inline mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cance #if MPACK_INTERNAL /* The below code is from Bjoern Hoehrmann's Flexible and Economical */ -/* UTF-8 decoder, modified to make it static and add the mpack prefix. */ +/* UTF-8 decoder, modified to support MPack inlining and add the mpack prefix. */ /* Copyright (c) 2008-2010 Bjoern Hoehrmann */ /* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. */ @@ -463,29 +466,12 @@ static inline mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cance #define MPACK_UTF8_ACCEPT 0 #define MPACK_UTF8_REJECT 12 -static const uint8_t mpack_utf8d[] = { - /* The first part of the table maps bytes to character classes that */ - /* to reduce the size of the transition table and create bitmasks. */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - - /* The second part is a transition table that maps a combination */ - /* of a state of the automaton and a character class to a state. */ - 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, - 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, - 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, - 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, - 12,36,12,12,12,12,12,12,12,12,12,12, -}; - -static inline -uint32_t mpack_utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) { +MPACK_INLINE_SPEED uint32_t mpack_utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte); + +#if MPACK_DEFINE_INLINE_SPEED +extern const uint8_t mpack_utf8d[]; + +MPACK_INLINE_SPEED uint32_t mpack_utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) { uint32_t type = mpack_utf8d[byte]; *codep = (*state != MPACK_UTF8_ACCEPT) ? @@ -495,6 +481,7 @@ uint32_t mpack_utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) { *state = mpack_utf8d[256 + *state + type]; return *state; } +#endif #endif diff --git a/src/mpack/mpack-expect.c b/src/mpack/mpack-expect.c index 2738cc1..27adda5 100644 --- a/src/mpack/mpack-expect.c +++ b/src/mpack/mpack-expect.c @@ -580,6 +580,7 @@ char* mpack_expect_cstr_alloc(mpack_reader_t* reader, size_t maxsize) { if (mpack_reader_error(reader)) { MPACK_FREE(str); + reader->error_fn(reader, mpack_reader_error(reader)); return NULL; } str[length] = 0; diff --git a/src/mpack/mpack-expect.h b/src/mpack/mpack-expect.h index 0abef3b..a649c4d 100644 --- a/src/mpack/mpack-expect.h +++ b/src/mpack/mpack-expect.h @@ -230,19 +230,19 @@ uint32_t mpack_expect_u32_range(mpack_reader_t* reader, uint32_t min_value, uint */ uint64_t mpack_expect_u64_range(mpack_reader_t* reader, uint64_t min_value, uint64_t max_value); -static inline uint8_t mpack_expect_u8_max(mpack_reader_t* reader, uint8_t max_value) { +MPACK_INLINE uint8_t mpack_expect_u8_max(mpack_reader_t* reader, uint8_t max_value) { return mpack_expect_u8_range(reader, 0, max_value); } -static inline uint16_t mpack_expect_u16_max(mpack_reader_t* reader, uint16_t max_value) { +MPACK_INLINE uint16_t mpack_expect_u16_max(mpack_reader_t* reader, uint16_t max_value) { return mpack_expect_u16_range(reader, 0, max_value); } -static inline uint32_t mpack_expect_u32_max(mpack_reader_t* reader, uint32_t max_value) { +MPACK_INLINE uint32_t mpack_expect_u32_max(mpack_reader_t* reader, uint32_t max_value) { return mpack_expect_u32_range(reader, 0, max_value); } -static inline uint64_t mpack_expect_u64_max(mpack_reader_t* reader, uint64_t max_value) { +MPACK_INLINE uint64_t mpack_expect_u64_max(mpack_reader_t* reader, uint64_t max_value) { return mpack_expect_u64_range(reader, 0, max_value); } @@ -441,7 +441,7 @@ uint32_t mpack_expect_array(mpack_reader_t* reader); */ uint32_t mpack_expect_array_range(mpack_reader_t* reader, uint32_t min_count, uint32_t max_count); -static inline uint32_t mpack_expect_array_max(mpack_reader_t* reader, uint32_t max_count) { +MPACK_INLINE uint32_t mpack_expect_array_max(mpack_reader_t* reader, uint32_t max_count) { return mpack_expect_array_range(reader, 0, max_count); } @@ -517,10 +517,14 @@ size_t mpack_expect_str_buf(mpack_reader_t* reader, char* buf, size_t bufsize); * mpack_error_type is raised if the value is not a string or if its * length does not match. */ -static inline void mpack_expect_str_max(mpack_reader_t* reader, uint32_t maxsize) { +MPACK_INLINE_SPEED void mpack_expect_str_max(mpack_reader_t* reader, uint32_t maxsize); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_expect_str_max(mpack_reader_t* reader, uint32_t maxsize) { if (mpack_expect_str(reader) > maxsize) mpack_reader_flag_error(reader, mpack_error_type); } +#endif /** * Reads the start of a string, raising an error if its length is not @@ -533,10 +537,14 @@ static inline void mpack_expect_str_max(mpack_reader_t* reader, uint32_t maxsize * mpack_error_type is raised if the value is not a string or if its * length does not match. */ -static inline void mpack_expect_str_length(mpack_reader_t* reader, uint32_t count) { +MPACK_INLINE_SPEED void mpack_expect_str_length(mpack_reader_t* reader, uint32_t count); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_expect_str_length(mpack_reader_t* reader, uint32_t count) { if (mpack_expect_str(reader) != count) mpack_reader_flag_error(reader, mpack_error_type); } +#endif /** @@ -624,10 +632,14 @@ uint32_t mpack_expect_bin(mpack_reader_t* reader); * mpack_error_type is raised if the value is not a binary blob or if its * length does not match. */ -static inline void mpack_expect_bin_max(mpack_reader_t* reader, uint32_t maxsize) { +MPACK_INLINE_SPEED void mpack_expect_bin_max(mpack_reader_t* reader, uint32_t maxsize); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_expect_bin_max(mpack_reader_t* reader, uint32_t maxsize) { if (mpack_expect_str(reader) > maxsize) mpack_reader_flag_error(reader, mpack_error_type); } +#endif /** * Reads the start of a binary blob, raising an error if its length is not @@ -640,10 +652,14 @@ static inline void mpack_expect_bin_max(mpack_reader_t* reader, uint32_t maxsize * mpack_error_type is raised if the value is not a binary blob or if its * length does not match. */ -static inline void mpack_expect_bin_size(mpack_reader_t* reader, uint32_t count) { +MPACK_INLINE_SPEED void mpack_expect_bin_size(mpack_reader_t* reader, uint32_t count); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_expect_bin_size(mpack_reader_t* reader, uint32_t count) { if (mpack_expect_str(reader) != count) mpack_reader_flag_error(reader, mpack_error_type); } +#endif /** * Reads a binary blob into the given buffer, returning its size in bytes. diff --git a/src/mpack/mpack-node.c b/src/mpack/mpack-node.c index 2f4affc..4419116 100644 --- a/src/mpack/mpack-node.c +++ b/src/mpack/mpack-node.c @@ -48,7 +48,7 @@ typedef struct mpack_tree_parser_t { bool stack_allocated; } mpack_tree_parser_t; -static inline uint8_t mpack_tree_u8(mpack_tree_parser_t* parser) { +MPACK_STATIC_INLINE_SPEED uint8_t mpack_tree_u8(mpack_tree_parser_t* parser) { if (parser->possible_nodes_left < sizeof(uint8_t)) { mpack_tree_flag_error(parser->tree, mpack_error_io); return 0; @@ -60,7 +60,7 @@ static inline uint8_t mpack_tree_u8(mpack_tree_parser_t* parser) { return val; } -static inline uint16_t mpack_tree_u16(mpack_tree_parser_t* parser) { +MPACK_STATIC_INLINE_SPEED uint16_t mpack_tree_u16(mpack_tree_parser_t* parser) { if (parser->possible_nodes_left < sizeof(uint16_t)) { mpack_tree_flag_error(parser->tree, mpack_error_io); return 0; @@ -72,7 +72,7 @@ static inline uint16_t mpack_tree_u16(mpack_tree_parser_t* parser) { return val; } -static inline uint32_t mpack_tree_u32(mpack_tree_parser_t* parser) { +MPACK_STATIC_INLINE_SPEED uint32_t mpack_tree_u32(mpack_tree_parser_t* parser) { if (parser->possible_nodes_left < sizeof(uint32_t)) { mpack_tree_flag_error(parser->tree, mpack_error_io); return 0; @@ -84,7 +84,7 @@ static inline uint32_t mpack_tree_u32(mpack_tree_parser_t* parser) { return val; } -static inline uint64_t mpack_tree_u64(mpack_tree_parser_t* parser) { +MPACK_STATIC_INLINE_SPEED uint64_t mpack_tree_u64(mpack_tree_parser_t* parser) { if (parser->possible_nodes_left < sizeof(uint64_t)) { mpack_tree_flag_error(parser->tree, mpack_error_io); return 0; @@ -96,12 +96,12 @@ static inline uint64_t mpack_tree_u64(mpack_tree_parser_t* parser) { return val; } -static inline int8_t mpack_tree_i8 (mpack_tree_parser_t* parser) {return (int8_t) mpack_tree_u8(parser); } -static inline int16_t mpack_tree_i16(mpack_tree_parser_t* parser) {return (int16_t)mpack_tree_u16(parser);} -static inline int32_t mpack_tree_i32(mpack_tree_parser_t* parser) {return (int32_t)mpack_tree_u32(parser);} -static inline int64_t mpack_tree_i64(mpack_tree_parser_t* parser) {return (int64_t)mpack_tree_u64(parser);} +MPACK_STATIC_INLINE int8_t mpack_tree_i8 (mpack_tree_parser_t* parser) {return (int8_t) mpack_tree_u8(parser); } +MPACK_STATIC_INLINE int16_t mpack_tree_i16(mpack_tree_parser_t* parser) {return (int16_t)mpack_tree_u16(parser);} +MPACK_STATIC_INLINE int32_t mpack_tree_i32(mpack_tree_parser_t* parser) {return (int32_t)mpack_tree_u32(parser);} +MPACK_STATIC_INLINE int64_t mpack_tree_i64(mpack_tree_parser_t* parser) {return (int64_t)mpack_tree_u64(parser);} -static inline float mpack_tree_float(mpack_tree_parser_t* parser) { +MPACK_STATIC_INLINE_SPEED float mpack_tree_float(mpack_tree_parser_t* parser) { union { float f; uint32_t i; @@ -110,7 +110,7 @@ static inline float mpack_tree_float(mpack_tree_parser_t* parser) { return u.f; } -static inline double mpack_tree_double(mpack_tree_parser_t* parser) { +MPACK_STATIC_INLINE_SPEED double mpack_tree_double(mpack_tree_parser_t* parser) { union { double d; uint64_t i; @@ -821,12 +821,6 @@ mpack_error_t mpack_tree_destroy(mpack_tree_t* tree) { tree->teardown(tree); tree->teardown = NULL; - #if MPACK_SETJMP - if (tree->jump_env) - MPACK_FREE(tree->jump_env); - tree->jump_env = NULL; - #endif - return tree->error; } @@ -835,10 +829,8 @@ void mpack_tree_flag_error(mpack_tree_t* tree, mpack_error_t error) { if (tree->error == mpack_ok) { tree->error = error; - #if MPACK_SETJMP - if (tree->jump_env) - longjmp(*tree->jump_env, 1); - #endif + if (tree->error_fn) + tree->error_fn(tree, error); } } @@ -853,7 +845,27 @@ void mpack_node_flag_error(mpack_node_t node, mpack_error_t error) { mpack_tree_flag_error(node.tree, error); } -#if MPACK_DEBUG && MPACK_STDIO && MPACK_SETJMP && !MPACK_NO_PRINT +mpack_tag_t mpack_node_tag(mpack_node_t node) { + mpack_tag_t tag; + mpack_memset(&tag, 0, sizeof(tag)); + tag.type = node.data->type; + switch (node.data->type) { + case mpack_type_nil: break; + case mpack_type_bool: tag.v.b = node.data->value.b; break; + case mpack_type_float: tag.v.f = node.data->value.f; break; + case mpack_type_double: tag.v.d = node.data->value.d; break; + case mpack_type_int: tag.v.i = node.data->value.i; break; + case mpack_type_uint: tag.v.u = node.data->value.u; break; + case mpack_type_str: tag.v.l = node.data->value.data.l; break; + case mpack_type_bin: tag.v.l = node.data->value.data.l; break; + case mpack_type_ext: tag.v.l = node.data->value.data.l; break; + case mpack_type_array: tag.v.n = node.data->value.content.n; break; + case mpack_type_map: tag.v.n = node.data->value.content.n; break; + } + return tag; +} + +#if MPACK_DEBUG && MPACK_STDIO && !MPACK_NO_PRINT static void mpack_node_print_element(mpack_node_t node, size_t depth) { mpack_node_data_t* data = node.data; switch (data->type) { diff --git a/src/mpack/mpack-node.h b/src/mpack/mpack-node.h index b11bda9..86de7bc 100644 --- a/src/mpack/mpack-node.h +++ b/src/mpack/mpack-node.h @@ -72,6 +72,32 @@ typedef struct mpack_node_data_t mpack_node_data_t; */ typedef struct mpack_tree_t mpack_tree_t; +/** + * An error handler function to be called when an error is flagged on + * the tree. + * + * The error handler will only be called once on the first error flagged; + * any subsequent node reads and errors are ignored, and the tree is + * permanently in that error state. + * + * MPack is safe against non-local jumps out of error handler callbacks. + * This means you are allowed to longjmp or throw an exception (in C++ + * or with SEH) out of this callback. + * + * Bear in mind when using longjmp that local non-volatile variables that + * have changed are undefined when setjmp() returns, so you can't put the + * tree on the stack in the same activation frame as the setjmp without + * declaring it volatile.) + * + * You must still eventually destroy the tree. It is not destroyed + * automatically when an error is flagged. It is safe to destroy the + * tree within this error callback, but you will either need to perform + * a non-local jump, or store something in your context to identify + * that the tree is destroyed since any future accesses to it cause + * undefined behavior. + */ +typedef void (*mpack_tree_error_t)(mpack_tree_t* tree, mpack_error_t error); + /** * A teardown function to be called when the tree is destroyed. */ @@ -129,6 +155,7 @@ struct mpack_node_data_t { }; struct mpack_tree_t { + mpack_tree_error_t error_fn; /* Function to call on error */ mpack_tree_teardown_t teardown; /* Function to teardown the context on destroy */ void* context; /* Context for tree callbacks */ @@ -143,28 +170,22 @@ struct mpack_tree_t { #ifdef MPACK_MALLOC bool owned; #endif - - #if MPACK_SETJMP - /* Optional jump target in case of error (pointer because it's - * very large and may be unused) */ - jmp_buf* jump_env; - #endif }; // internal functions -static inline mpack_node_t mpack_node(mpack_tree_t* tree, mpack_node_data_t* data) { +MPACK_INLINE mpack_node_t mpack_node(mpack_tree_t* tree, mpack_node_data_t* data) { mpack_node_t node; node.data = data; node.tree = tree; return node; } -static inline mpack_node_data_t* mpack_node_child(mpack_node_t node, size_t child) { +MPACK_INLINE mpack_node_data_t* mpack_node_child(mpack_node_t node, size_t child) { return node.data->value.content.children + child; } -static inline mpack_node_t mpack_tree_nil_node(mpack_tree_t* tree) { +MPACK_INLINE mpack_node_t mpack_tree_nil_node(mpack_tree_t* tree) { return mpack_node(tree, &tree->nil_node); } @@ -231,7 +252,7 @@ mpack_node_t mpack_tree_root(mpack_tree_t* tree); /** * Returns the error state of the tree. */ -static inline mpack_error_t mpack_tree_error(mpack_tree_t* tree) { +MPACK_INLINE mpack_error_t mpack_tree_error(mpack_tree_t* tree) { return tree->error; } @@ -240,7 +261,7 @@ static inline mpack_error_t mpack_tree_error(mpack_tree_t* tree) { * parsed. If there is something in the buffer after the MessagePack * object (such as another object), this can be used to find it. */ -static inline size_t mpack_tree_size(mpack_tree_t* tree) { +MPACK_INLINE size_t mpack_tree_size(mpack_tree_t* tree) { return tree->size; } @@ -255,59 +276,39 @@ mpack_error_t mpack_tree_destroy(mpack_tree_t* tree); * @param tree The MPack tree. * @param context User data to pass to the tree callbacks. */ -static inline void mpack_tree_set_context(mpack_tree_t* tree, void* context) { +MPACK_INLINE void mpack_tree_set_context(mpack_tree_t* tree, void* context) { tree->context = context; } /** - * Sets the teardown function to call when the tree is destroyed. + * Sets the error function to call when an error is flagged on the tree. * * This should normally be used with mpack_tree_set_context() to register - * a custom pointer to pass to the teardown function. + * a custom pointer to pass to the error function. + * + * See the definition of mpack_tree_error_t for more information about + * what you can do from an error callback. * + * @see mpack_tree_error_t * @param tree The MPack tree. - * @param teardown The function to call when the tree is destroyed. + * @param error The function to call when an error is flagged on the tree. */ -static inline void mpack_tree_set_teardown(mpack_tree_t* tree, mpack_tree_teardown_t teardown) { - tree->teardown = teardown; +MPACK_INLINE void mpack_tree_set_error_handler(mpack_tree_t* tree, mpack_tree_error_t error_fn) { + tree->error_fn = error_fn; } -#if MPACK_SETJMP - /** - * @hideinitializer - * - * Registers a jump target in case of error. - * - * If the tree is in an error state, 1 is returned when this is called. Otherwise - * 0 is returned when this is called, and when the first error occurs, control flow - * will jump to the point where this was called, resuming as though it returned 1. - * This ensures an error handling block runs exactly once in case of error. - * - * A tree that jumps still needs to be destroyed. You must call - * mpack_tree_destroy() in your jump handler after getting the final error state. + * Sets the teardown function to call when the tree is destroyed. * - * The argument may be evaluated multiple times. + * This should normally be used with mpack_tree_set_context() to register + * a custom pointer to pass to the teardown function. * - * @returns 0 if the tree is not in an error state; 1 if and when an error occurs. - * @see mpack_tree_destroy() - */ -#define MPACK_TREE_SETJMP(tree) \ - (mpack_assert((tree)->jump_env == NULL, "already have a jump set!"), \ - ((tree)->error != mpack_ok) ? 1 : \ - !((tree)->jump_env = (jmp_buf*)MPACK_MALLOC(sizeof(jmp_buf))) ? \ - ((tree)->error = mpack_error_memory, 1) : \ - (setjmp(*(tree)->jump_env))) - -/** - * Clears a jump target. Subsequent tree reading errors will not cause a jump. + * @param tree The MPack tree. + * @param teardown The function to call when the tree is destroyed. */ -static inline void mpack_tree_clearjmp(mpack_tree_t* tree) { - if (tree->jump_env) - MPACK_FREE(tree->jump_env); - tree->jump_env = NULL; +MPACK_INLINE void mpack_tree_set_teardown(mpack_tree_t* tree, mpack_tree_teardown_t teardown) { + tree->teardown = teardown; } -#endif /** * Places the tree in the given error state, jumping if a jump target is set. @@ -343,34 +344,16 @@ void mpack_node_flag_error(mpack_node_t node, mpack_error_t error); /** * Returns the error state of the node's tree. */ -static inline mpack_error_t mpack_node_error(mpack_node_t node) { +MPACK_INLINE mpack_error_t mpack_node_error(mpack_node_t node) { return mpack_tree_error(node.tree); } /** * Returns a tag describing the given node. */ -static inline mpack_tag_t mpack_node_tag(mpack_node_t node) { - mpack_tag_t tag; - mpack_memset(&tag, 0, sizeof(tag)); - tag.type = node.data->type; - switch (node.data->type) { - case mpack_type_nil: break; - case mpack_type_bool: tag.v.b = node.data->value.b; break; - case mpack_type_float: tag.v.f = node.data->value.f; break; - case mpack_type_double: tag.v.d = node.data->value.d; break; - case mpack_type_int: tag.v.i = node.data->value.i; break; - case mpack_type_uint: tag.v.u = node.data->value.u; break; - case mpack_type_str: tag.v.l = node.data->value.data.l; break; - case mpack_type_bin: tag.v.l = node.data->value.data.l; break; - case mpack_type_ext: tag.v.l = node.data->value.data.l; break; - case mpack_type_array: tag.v.n = node.data->value.content.n; break; - case mpack_type_map: tag.v.n = node.data->value.content.n; break; - } - return tag; -} +mpack_tag_t mpack_node_tag(mpack_node_t node); -#if MPACK_DEBUG && MPACK_STDIO && MPACK_SETJMP && !MPACK_NO_PRINT +#if MPACK_DEBUG && MPACK_STDIO && !MPACK_NO_PRINT /** * Converts a node to JSON and pretty-prints it to stdout. * @@ -391,27 +374,38 @@ void mpack_node_print(mpack_node_t node); /** * Returns the type of the node. */ -static inline mpack_type_t mpack_node_type(mpack_node_t node) { +MPACK_INLINE_SPEED mpack_type_t mpack_node_type(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED mpack_type_t mpack_node_type(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return mpack_type_nil; return node.data->type; } +#endif /** * Checks if the given node is of nil type, raising mpack_error_type otherwise. */ -static inline void mpack_node_nil(mpack_node_t node) { +MPACK_INLINE_SPEED void mpack_node_nil(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_node_nil(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return; if (node.data->type != mpack_type_nil) mpack_node_flag_error(node, mpack_error_type); } +#endif /** * Returns the bool value of the node. If this node is not of the correct * type, mpack_error_type is raised, and the return value should be discarded. */ -static inline bool mpack_node_bool(mpack_node_t node) { +MPACK_INLINE_SPEED bool mpack_node_bool(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED bool mpack_node_bool(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return false; @@ -421,31 +415,43 @@ static inline bool mpack_node_bool(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return false; } +#endif /** * Checks if the given node is of bool type with value true, raising * mpack_error_type otherwise. */ -static inline void mpack_node_true(mpack_node_t node) { +MPACK_INLINE_SPEED void mpack_node_true(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_node_true(mpack_node_t node) { if (mpack_node_bool(node) != true) mpack_node_flag_error(node, mpack_error_type); } +#endif /** * Checks if the given node is of bool type with value false, raising * mpack_error_type otherwise. */ -static inline void mpack_node_false(mpack_node_t node) { +MPACK_INLINE_SPEED void mpack_node_false(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_node_false(mpack_node_t node) { if (mpack_node_bool(node) != false) mpack_node_flag_error(node, mpack_error_type); } +#endif /** * Returns the 8-bit unsigned value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline uint8_t mpack_node_u8(mpack_node_t node) { +MPACK_INLINE_SPEED uint8_t mpack_node_u8(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED uint8_t mpack_node_u8(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -460,13 +466,17 @@ static inline uint8_t mpack_node_u8(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the 8-bit signed value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline int8_t mpack_node_i8(mpack_node_t node) { +MPACK_INLINE_SPEED int8_t mpack_node_i8(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED int8_t mpack_node_i8(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -481,13 +491,17 @@ static inline int8_t mpack_node_i8(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the 16-bit unsigned value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline uint16_t mpack_node_u16(mpack_node_t node) { +MPACK_INLINE_SPEED uint16_t mpack_node_u16(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED uint16_t mpack_node_u16(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -502,13 +516,17 @@ static inline uint16_t mpack_node_u16(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the 16-bit signed value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline int16_t mpack_node_i16(mpack_node_t node) { +MPACK_INLINE_SPEED int16_t mpack_node_i16(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED int16_t mpack_node_i16(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -523,13 +541,17 @@ static inline int16_t mpack_node_i16(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the 32-bit unsigned value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline uint32_t mpack_node_u32(mpack_node_t node) { +MPACK_INLINE_SPEED uint32_t mpack_node_u32(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED uint32_t mpack_node_u32(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -544,13 +566,17 @@ static inline uint32_t mpack_node_u32(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the 32-bit signed value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline int32_t mpack_node_i32(mpack_node_t node) { +MPACK_INLINE_SPEED int32_t mpack_node_i32(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED int32_t mpack_node_i32(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -565,13 +591,17 @@ static inline int32_t mpack_node_i32(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the 64-bit unsigned value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline uint64_t mpack_node_u64(mpack_node_t node) { +MPACK_INLINE_SPEED uint64_t mpack_node_u64(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED uint64_t mpack_node_u64(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -585,13 +615,17 @@ static inline uint64_t mpack_node_u64(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the 64-bit signed value of the node. If this node is not * of a compatible type, mpack_error_type is raised, and the * return value should be discarded. */ -static inline int64_t mpack_node_i64(mpack_node_t node) { +MPACK_INLINE_SPEED int64_t mpack_node_i64(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED int64_t mpack_node_i64(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -605,6 +639,7 @@ static inline int64_t mpack_node_i64(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the float value of the node. The underlying value can be an @@ -615,7 +650,10 @@ static inline int64_t mpack_node_i64(mpack_node_t node) { * * @throws mpack_error_type if the underlying value is not a float, double or integer. */ -static inline float mpack_node_float(mpack_node_t node) { +MPACK_INLINE_SPEED float mpack_node_float(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED float mpack_node_float(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0.0f; @@ -631,6 +669,7 @@ static inline float mpack_node_float(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0.0f; } +#endif /** * Returns the double value of the node. The underlying value can be an @@ -641,7 +680,10 @@ static inline float mpack_node_float(mpack_node_t node) { * * @throws mpack_error_type if the underlying value is not a float, double or integer. */ -static inline double mpack_node_double(mpack_node_t node) { +MPACK_INLINE_SPEED double mpack_node_double(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED double mpack_node_double(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0.0; @@ -657,6 +699,7 @@ static inline double mpack_node_double(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0.0; } +#endif /** * Returns the float value of the node. The underlying value must be a float, @@ -664,7 +707,10 @@ static inline double mpack_node_double(mpack_node_t node) { * * @throws mpack_error_type if the underlying value is not a float. */ -static inline float mpack_node_float_strict(mpack_node_t node) { +MPACK_INLINE_SPEED float mpack_node_float_strict(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED float mpack_node_float_strict(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0.0f; @@ -674,6 +720,7 @@ static inline float mpack_node_float_strict(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0.0f; } +#endif /** * Returns the double value of the node. The underlying value must be a float @@ -681,7 +728,10 @@ static inline float mpack_node_float_strict(mpack_node_t node) { * * @throws mpack_error_type if the underlying value is not a float or double. */ -static inline double mpack_node_double_strict(mpack_node_t node) { +MPACK_INLINE_SPEED double mpack_node_double_strict(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED double mpack_node_double_strict(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0.0; @@ -693,6 +743,7 @@ static inline double mpack_node_double_strict(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0.0; } +#endif /** * @} @@ -706,7 +757,10 @@ static inline double mpack_node_double_strict(mpack_node_t node) { /** * Returns the extension type of the given ext node. */ -static inline int8_t mpack_node_exttype(mpack_node_t node) { +MPACK_INLINE_SPEED int8_t mpack_node_exttype(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED int8_t mpack_node_exttype(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -716,11 +770,15 @@ static inline int8_t mpack_node_exttype(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the length of the given str, bin or ext node. */ -static inline size_t mpack_node_data_len(mpack_node_t node) { +MPACK_INLINE_SPEED size_t mpack_node_data_len(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED size_t mpack_node_data_len(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -731,12 +789,16 @@ static inline size_t mpack_node_data_len(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns the length in bytes of the given string node. This does not * include any null-terminator. */ -static inline size_t mpack_node_strlen(mpack_node_t node) { +MPACK_INLINE_SPEED size_t mpack_node_strlen(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED size_t mpack_node_strlen(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -746,6 +808,7 @@ static inline size_t mpack_node_strlen(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return 0; } +#endif /** * Returns a pointer to the data contained by this node. @@ -758,7 +821,10 @@ static inline size_t mpack_node_strlen(mpack_node_t node) { * If this node is not of a str, bin or map, mpack_error_type is raised, and * NULL is returned. */ -static inline const char* mpack_node_data(mpack_node_t node) { +MPACK_INLINE_SPEED const char* mpack_node_data(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED const char* mpack_node_data(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return NULL; @@ -769,6 +835,7 @@ static inline const char* mpack_node_data(mpack_node_t node) { mpack_node_flag_error(node, mpack_error_type); return NULL; } +#endif /** * Copies the bytes contained by this node into the given buffer, returning the @@ -843,7 +910,10 @@ mpack_node_t mpack_node_map_uint_impl(mpack_node_t node, uint64_t num, bool opti * Returns the length of the given array node. Raises mpack_error_type * and returns 0 if the given node is not an array. */ -static inline size_t mpack_node_array_length(mpack_node_t node) { +MPACK_INLINE_SPEED size_t mpack_node_array_length(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED size_t mpack_node_array_length(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -854,6 +924,7 @@ static inline size_t mpack_node_array_length(mpack_node_t node) { return (size_t)node.data->value.content.n; } +#endif /** * Returns the node in the given array at the given index. If the node @@ -861,7 +932,10 @@ static inline size_t mpack_node_array_length(mpack_node_t node) { * If the given index is out of bounds, mpack_error_data is raised and * a nil node is returned. */ -static inline mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index) { +MPACK_INLINE_SPEED mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index) { if (mpack_node_error(node) != mpack_ok) return mpack_tree_nil_node(node.tree); @@ -877,12 +951,16 @@ static inline mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index) return mpack_node(node.tree, mpack_node_child(node, index)); } +#endif /** * Returns the number of key/value pairs in the given map node. Raises * mpack_error_type and returns 0 if the given node is not a map. */ -static inline size_t mpack_node_map_count(mpack_node_t node) { +MPACK_INLINE_SPEED size_t mpack_node_map_count(mpack_node_t node); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED size_t mpack_node_map_count(mpack_node_t node) { if (mpack_node_error(node) != mpack_ok) return 0; @@ -893,9 +971,13 @@ static inline size_t mpack_node_map_count(mpack_node_t node) { return node.data->value.content.n; } +#endif // internal node map lookup -static inline mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, size_t offset) { +MPACK_INLINE_SPEED mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, size_t offset); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, size_t offset) { if (mpack_node_error(node) != mpack_ok) return mpack_tree_nil_node(node.tree); @@ -911,6 +993,7 @@ static inline mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, si return mpack_node(node.tree, mpack_node_child(node, index * 2 + offset)); } +#endif /** * Returns the key node in the given map at the given index. @@ -920,7 +1003,7 @@ static inline mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, si * @throws mpack_error_type if the node is not a map * @throws mpack_error_data if the given index is out of bounds */ -static inline mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index) { +MPACK_INLINE mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index) { return mpack_node_map_at(node, index, 0); } @@ -932,7 +1015,7 @@ static inline mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index * @throws mpack_error_type if the node is not a map * @throws mpack_error_data if the given index is out of bounds */ -static inline mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t index) { +MPACK_INLINE mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t index) { return mpack_node_map_at(node, index, 1); } @@ -942,7 +1025,7 @@ static inline mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t ind * returned. If the given key does not exist in the map, mpack_error_data * is raised and a nil node is returned. */ -static inline mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num) { +MPACK_INLINE mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num) { return mpack_node_map_int_impl(node, num, false); } @@ -952,7 +1035,7 @@ static inline mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num) { * * @throws mpack_error_type if the node is not a map */ -static inline mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_t num) { +MPACK_INLINE mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_t num) { return mpack_node_map_int_impl(node, num, true); } @@ -962,7 +1045,7 @@ static inline mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_ * returned. If the given key does not exist in the map, mpack_error_data * is raised and a nil node is returned. */ -static inline mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num) { +MPACK_INLINE mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num) { return mpack_node_map_uint_impl(node, num, false); } @@ -972,7 +1055,7 @@ static inline mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num) * * @throws mpack_error_type if the node is not a map */ -static inline mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint64_t num) { +MPACK_INLINE mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint64_t num) { return mpack_node_map_uint_impl(node, num, true); } @@ -982,7 +1065,7 @@ static inline mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint6 * returned. If the given key does not exist in the map, mpack_error_data * is raised and a nil node is returned. */ -static inline mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str, size_t length) { +MPACK_INLINE mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str, size_t length) { return mpack_node_map_str_impl(node, str, length, false); } @@ -992,7 +1075,7 @@ static inline mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str * * @throws mpack_error_type if the node is not a map */ -static inline mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const char* str, size_t length) { +MPACK_INLINE mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const char* str, size_t length) { return mpack_node_map_str_impl(node, str, length, true); } @@ -1002,7 +1085,7 @@ static inline mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const * returned. If the given key does not exist in the map, mpack_error_data * is raised and a nil node is returned. */ -static inline mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cstr) { +MPACK_INLINE mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cstr) { return mpack_node_map_str(node, cstr, mpack_strlen(cstr)); } @@ -1012,7 +1095,7 @@ static inline mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cs * * @throws mpack_error_type if the node is not a map */ -static inline mpack_node_t mpack_node_map_cstr_optional(mpack_node_t node, const char* cstr) { +MPACK_INLINE mpack_node_t mpack_node_map_cstr_optional(mpack_node_t node, const char* cstr) { return mpack_node_map_str_optional(node, cstr, mpack_strlen(cstr)); } @@ -1028,7 +1111,7 @@ bool mpack_node_map_contains_str(mpack_node_t node, const char* str, size_t leng * null-terminated string key. If the given node is not a map, mpack_error_type * is raised and null is returned. */ -static inline bool mpack_node_map_contains_cstr(mpack_node_t node, const char* cstr) { +MPACK_INLINE bool mpack_node_map_contains_cstr(mpack_node_t node, const char* cstr) { return mpack_node_map_contains_str(node, cstr, mpack_strlen(cstr)); } diff --git a/src/mpack/mpack-platform.c b/src/mpack/mpack-platform.c index d552f5c..01a703d 100644 --- a/src/mpack/mpack-platform.c +++ b/src/mpack/mpack-platform.c @@ -19,9 +19,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +// We define MPACK_EMIT_INLINE_DEFS and include mpack.h to emit +// standalone definitions of all (non-static) inline functions in MPack. + #define MPACK_INTERNAL 1 +#define MPACK_EMIT_INLINE_DEFS 1 #include "mpack-platform.h" +#include "mpack.h" #if MPACK_DEBUG && MPACK_STDIO #include @@ -161,7 +166,9 @@ size_t mpack_strlen(const char *s) { #if defined(MPACK_MALLOC) && !defined(MPACK_REALLOC) void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) { - void* new_ptr = malloc(new_size); + if (new_size == 0) + return NULL; + void* new_ptr = MPACK_MALLOC(new_size); if (new_ptr == NULL) return NULL; mpack_memcpy(new_ptr, old_ptr, used_size); diff --git a/src/mpack/mpack-platform.h b/src/mpack/mpack-platform.h index 860c747..eed90ca 100644 --- a/src/mpack/mpack-platform.h +++ b/src/mpack/mpack-platform.h @@ -30,14 +30,83 @@ #ifndef MPACK_PLATFORM_H #define MPACK_PLATFORM_H 1 -#if defined(WIN32) && MPACK_INTERNAL + + +/* For now, nothing in here should be seen by Doxygen. */ +/** @cond */ + + + +#if defined(WIN32) && defined(MPACK_INTERNAL) && MPACK_INTERNAL #define _CRT_SECURE_NO_WARNINGS 1 #endif + + #include "mpack-config.h" -/* For now, nothing in here should be seen by Doxygen. */ -/** @cond */ +/* + * Now that the config is included, we define to 0 any of the configuration + * options and other switches that aren't defined. This supports -Wundef + * without us having to write "#if defined(X) && X" everywhere (and while + * allowing configs to be pre-defined to 0.) + */ +#ifndef MPACK_READER +#define MPACK_READER 0 +#endif +#ifndef MPACK_EXPECT +#define MPACK_EXPECT 0 +#endif +#ifndef MPACK_NODE +#define MPACK_NODE 0 +#endif +#ifndef MPACK_WRITER +#define MPACK_WRITER 0 +#endif + +#ifndef MPACK_STDLIB +#define MPACK_STDLIB 0 +#endif +#ifndef MPACK_STDIO +#define MPACK_STDIO 0 +#endif + +#ifndef MPACK_DEBUG +#define MPACK_DEBUG 0 +#endif +#ifndef MPACK_CUSTOM_ASSERT +#define MPACK_CUSTOM_ASSERT 0 +#endif + +#ifndef MPACK_READ_TRACKING +#define MPACK_READ_TRACKING 0 +#endif +#ifndef MPACK_WRITE_TRACKING +#define MPACK_WRITE_TRACKING 0 +#endif +#ifndef MPACK_NO_TRACKING +#define MPACK_NO_TRACKING 0 +#endif +#ifndef MPACK_OPTIMIZE_FOR_SIZE +#define MPACK_OPTIMIZE_FOR_SIZE 0 +#endif + +#ifndef MPACK_EMIT_INLINE_DEFS +#define MPACK_EMIT_INLINE_DEFS 0 +#endif +#ifndef MPACK_AMALGAMATED +#define MPACK_AMALGAMATED 0 +#endif +#ifndef MPACK_INTERNAL +#define MPACK_INTERNAL 0 +#endif +#ifndef MPACK_NO_PRINT +#define MPACK_NO_PRINT 0 +#endif + + + +/* System headers (based on configuration) */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 @@ -62,9 +131,6 @@ #if MPACK_STDIO #include #endif -#if MPACK_SETJMP -#include -#endif #ifdef __cplusplus extern "C" { @@ -72,37 +138,109 @@ extern "C" { +/* Miscellaneous helper macros */ + #define MPACK_UNUSED(var) ((void)(var)) -#if MPACK_AMALGAMATED -#define MPACK_INTERNAL_STATIC static +#define MPACK_STRINGIFY_IMPL(arg) #arg +#define MPACK_STRINGIFY(arg) MPACK_STRINGIFY_IMPL(arg) + + + +/* + * Definition of inline macros. + * + * MPack supports several different modes for inline functions: + * - functions declared with a platform-specific always-inline (MPACK_ALWAYS_INLINE) + * - functions declared inline regardless of optimization options (MPACK_INLINE) + * - functions declared inline only in builds optimized for speed (MPACK_INLINE_SPEED) + * + * MPack does not use static inline in header files; only one non-inline definition + * of each function should exist in the final build. This can reduce the binary size + * in cases where the compiler cannot or chooses not to inline a function. + * The addresses of functions should also compare equal across translation units + * regardless of whether they are declared inline. + * + * The above requirements mean that the declaration and definition of non-trivial + * inline functions must be separated so that the definitions will only + * appear when necessary. In addition, three different linkage models need + * to be supported: + * + * - The C99 model, where "inline" does not emit a definition and "extern inline" does + * - The GNU model, where "inline" emits a definition and "extern inline" does not + * - The C++ model, where "inline" emits a definition with weak linkage + * + * The macros below wrap up everything above. All inline functions defined in header + * files have a single non-inline definition emitted in the compilation of + * mpack-platform.c. + * + * Inline functions in source files are defined static, so MPACK_STATIC_INLINE + * is used for small functions and MPACK_STATIC_INLINE_SPEED is used for + * larger optionally inline functions. + */ + +#if defined(__cplusplus) + // C++ rules + // The linker will need weak symbol support to link C++ object files, + // so we don't need to worry about emitting a single definition. + #define MPACK_INLINE inline +#elif defined(__GNUC__) && (defined(__GNUC_GNU_INLINE__) || \ + !defined(__GNUC_STDC_INLINE__) && !defined(__GNUC_GNU_INLINE__)) + // GNU rules + #if MPACK_EMIT_INLINE_DEFS + #define MPACK_INLINE inline + #else + #define MPACK_INLINE extern inline + #endif #else -#define MPACK_INTERNAL_STATIC + // C99 rules + #if MPACK_EMIT_INLINE_DEFS + #define MPACK_INLINE extern inline + #else + #define MPACK_INLINE inline + #endif #endif -#define MPACK_STRINGIFY_IMPL(arg) #arg -#define MPACK_STRINGIFY(arg) MPACK_STRINGIFY_IMPL(arg) +#define MPACK_STATIC_INLINE static inline + +#if MPACK_OPTIMIZE_FOR_SIZE + #define MPACK_STATIC_INLINE_SPEED static + #define MPACK_INLINE_SPEED /* nothing */ + #if MPACK_EMIT_INLINE_DEFS + #define MPACK_DEFINE_INLINE_SPEED 1 + #else + #define MPACK_DEFINE_INLINE_SPEED 0 + #endif +#else + #define MPACK_STATIC_INLINE_SPEED static inline + #define MPACK_INLINE_SPEED MPACK_INLINE + #define MPACK_DEFINE_INLINE_SPEED 1 +#endif + +#ifdef MPACK_OPTIMIZE_FOR_SPEED +#error "You should define MPACK_OPTIMIZE_FOR_SIZE, not MPACK_OPTIMIZE_FOR_SPEED." +#endif /* Some compiler-specific keywords and builtins */ + #if defined(__GNUC__) || defined(__clang__) #define MPACK_UNREACHABLE __builtin_unreachable() #define MPACK_NORETURN(fn) fn __attribute__((noreturn)) - #define MPACK_ALWAYS_INLINE __attribute__((always_inline)) static inline + #define MPACK_ALWAYS_INLINE __attribute__((always_inline)) MPACK_INLINE #elif defined(_MSC_VER) #define MPACK_UNREACHABLE __assume(0) #define MPACK_NORETURN(fn) __declspec(noreturn) fn - #define MPACK_ALWAYS_INLINE __forceinline static + #define MPACK_ALWAYS_INLINE __forceinline #else #define MPACK_UNREACHABLE ((void)0) #define MPACK_NORETURN(fn) fn - #define MPACK_ALWAYS_INLINE static inline + #define MPACK_ALWAYS_INLINE MPACK_INLINE #endif - /* * Here we define mpack_assert() and mpack_break(). They both work like a normal * assertion function in debug mode, causing a trap or abort. However, on some platforms @@ -112,10 +250,10 @@ extern "C" { * In release mode, mpack_assert() is converted to an assurance to the compiler * that the expression cannot be false (via e.g. __assume() or __builtin_unreachable()) * to improve optimization where supported. There is thus no point in "safely" handling - * the case of this being false. Writing mpack_assert(0) rarely makes sense; - * the compiler will throw away any code after it. If at any time an mpack_assert() - * is not true, the behaviour is undefined. This also means the expression is - * evaluated even in release. + * the case of this being false. Writing mpack_assert(0) rarely makes sense (except + * possibly as a default handler in a switch) since the compiler will throw away any + * code after it. If at any time an mpack_assert() is not true, the behaviour is + * undefined. This also means the expression is evaluated even in release. * * mpack_break() on the other hand is compiled to nothing in release. It is * used in situations where we want to highlight a programming error as early as @@ -169,6 +307,8 @@ extern "C" { + +/* Wrap some needed libc functions */ #if MPACK_STDLIB #define mpack_memset memset #define mpack_memcpy memcpy @@ -201,10 +341,10 @@ size_t mpack_strlen(const char *s); #if !defined(MPACK_MALLOC) && defined(MPACK_FREE) #error "MPACK_FREE requires MPACK_MALLOC." #endif -#if MPACK_READ_TRACKING && (!defined(MPACK_READER) || !MPACK_READER) +#if MPACK_READ_TRACKING && !defined(MPACK_READER) #error "MPACK_READ_TRACKING requires MPACK_READER." #endif -#if MPACK_WRITE_TRACKING && (!defined(MPACK_WRITER) || !MPACK_WRITER) +#if MPACK_WRITE_TRACKING && !defined(MPACK_WRITER) #error "MPACK_WRITE_TRACKING requires MPACK_WRITER." #endif #ifndef MPACK_MALLOC @@ -224,7 +364,7 @@ size_t mpack_strlen(const char *s); /* Implement realloc if unavailable */ #ifdef MPACK_MALLOC #ifdef MPACK_REALLOC - static inline void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) { + MPACK_ALWAYS_INLINE void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) { MPACK_UNUSED(used_size); return MPACK_REALLOC(old_ptr, new_size); } diff --git a/src/mpack/mpack-reader.c b/src/mpack/mpack-reader.c index 50602f0..24f7076 100644 --- a/src/mpack/mpack-reader.c +++ b/src/mpack/mpack-reader.c @@ -30,7 +30,7 @@ void mpack_reader_init(mpack_reader_t* reader, char* buffer, size_t size, size_t reader->buffer = buffer; reader->size = size; reader->left = count; - MPACK_READER_TRACK(reader, mpack_track_init(&reader->track)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_init(&reader->track))); } void mpack_reader_init_error(mpack_reader_t* reader, mpack_error_t error) { @@ -52,7 +52,7 @@ void mpack_reader_init_data(mpack_reader_t* reader, const char* data, size_t cou reader->buffer = (char*)data; #endif - MPACK_READER_TRACK(reader, mpack_track_init(&reader->track)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_init(&reader->track))); } #if MPACK_STDIO @@ -102,18 +102,12 @@ void mpack_reader_init_file(mpack_reader_t* reader, const char* filename) { mpack_error_t mpack_reader_destroy_impl(mpack_reader_t* reader, bool cancel) { MPACK_UNUSED(cancel); - MPACK_READER_TRACK(reader, mpack_track_destroy(&reader->track, cancel)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_destroy(&reader->track, cancel))); if (reader->teardown) reader->teardown(reader); reader->teardown = NULL; - #if MPACK_SETJMP - if (reader->jump_env) - MPACK_FREE(reader->jump_env); - reader->jump_env = NULL; - #endif - return reader->error; } @@ -126,7 +120,7 @@ mpack_error_t mpack_reader_destroy(mpack_reader_t* reader) { } size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data) { - MPACK_READER_TRACK(reader, mpack_track_check_empty(&reader->track)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_check_empty(&reader->track))); if (data) *data = reader->buffer + reader->pos; return reader->left; @@ -137,16 +131,14 @@ void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error) { if (reader->error == mpack_ok) { reader->error = error; - #if MPACK_SETJMP - if (reader->jump_env) - longjmp(*reader->jump_env, 1); - #endif + if (reader->error_fn) + reader->error_fn(reader, error); } } // A helper to call the reader fill function. This makes sure it's // implemented and guards against overflow in case it returns -1. -static inline size_t mpack_fill(mpack_reader_t* reader, char* p, size_t count) { +MPACK_STATIC_INLINE_SPEED size_t mpack_fill(mpack_reader_t* reader, char* p, size_t count) { if (!reader->fill) return 0; size_t ret = reader->fill(reader, p, count); @@ -298,15 +290,14 @@ const char* mpack_read_bytes_inplace(mpack_reader_t* reader, size_t count) { } mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { - mpack_tag_t var; - mpack_memset(&var, 0, sizeof(var)); - var.type = mpack_type_nil; + mpack_tag_t var = mpack_tag_nil(); // get the type uint8_t type = mpack_read_native_u8(reader); if (mpack_reader_error(reader)) - return var; - mpack_reader_track_element(reader); + return mpack_tag_nil(); + if (mpack_reader_track_element(reader) != mpack_ok) + return mpack_tag_nil(); // unfortunately, by far the fastest way to parse a tag is to switch // on the first byte, and to explicitly list every possible byte. so for @@ -349,7 +340,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: var.type = mpack_type_map; var.v.n = type & ~0xf0; - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_map, var.v.n)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_map, var.v.n)) != mpack_ok) + return mpack_tag_nil(); return var; // fixarray @@ -357,7 +349,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: var.type = mpack_type_array; var.v.n = type & ~0xf0; - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_array, var.v.n)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_array, var.v.n)) != mpack_ok) + return mpack_tag_nil(); return var; // fixstr @@ -367,13 +360,13 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf: var.type = mpack_type_str; var.v.l = type & ~0xe0; - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // nil case 0xc0: - var.type = mpack_type_nil; - return var; + return mpack_tag_nil(); // bool case 0xc2: case 0xc3: @@ -385,21 +378,24 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { case 0xc4: var.type = mpack_type_bin; var.v.l = mpack_read_native_u8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_bin, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_bin, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // bin16 case 0xc5: var.type = mpack_type_bin; var.v.l = mpack_read_native_u16(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_bin, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_bin, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // bin32 case 0xc6: var.type = mpack_type_bin; var.v.l = mpack_read_native_u32(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_bin, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_bin, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // ext8 @@ -407,7 +403,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = mpack_read_native_u8(reader); var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // ext16 @@ -415,7 +412,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = mpack_read_native_u16(reader); var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // ext32 @@ -423,7 +421,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = mpack_read_native_u32(reader); var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // float @@ -491,7 +490,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = 1; var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // fixext2 @@ -499,7 +499,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = 2; var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // fixext4 @@ -507,7 +508,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = 4; var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // fixext8 @@ -515,7 +517,8 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = 8; var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // fixext16 @@ -523,56 +526,64 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { var.type = mpack_type_ext; var.v.l = 16; var.exttype = mpack_read_native_i8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_ext, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // str8 case 0xd9: var.type = mpack_type_str; var.v.l = mpack_read_native_u8(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // str16 case 0xda: var.type = mpack_type_str; var.v.l = mpack_read_native_u16(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // str32 case 0xdb: var.type = mpack_type_str; var.v.l = mpack_read_native_u32(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_str, var.v.l)) != mpack_ok) + return mpack_tag_nil(); return var; // array16 case 0xdc: var.type = mpack_type_array; var.v.n = mpack_read_native_u16(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_array, var.v.n)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_array, var.v.n)) != mpack_ok) + return mpack_tag_nil(); return var; // array32 case 0xdd: var.type = mpack_type_array; var.v.n = mpack_read_native_u32(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_array, var.v.n)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_array, var.v.n)) != mpack_ok) + return mpack_tag_nil(); return var; // map16 case 0xde: var.type = mpack_type_map; var.v.n = mpack_read_native_u16(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_map, var.v.n)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_map, var.v.n)) != mpack_ok) + return mpack_tag_nil(); return var; // map32 case 0xdf: var.type = mpack_type_map; var.v.n = mpack_read_native_u32(reader); - MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_map, var.v.n)); + if (MPACK_READER_TRACK(reader, mpack_track_push(&reader->track, mpack_type_map, var.v.n)) != mpack_ok) + return mpack_tag_nil(); return var; // reserved @@ -582,7 +593,7 @@ mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { // unrecognized type mpack_reader_flag_error(reader, mpack_error_invalid); - return var; + return mpack_tag_nil(); } void mpack_discard(mpack_reader_t* reader) { @@ -626,33 +637,35 @@ void mpack_discard(mpack_reader_t* reader) { #if MPACK_READ_TRACKING void mpack_done_array(mpack_reader_t* reader) { - MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_array)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_array))); } void mpack_done_map(mpack_reader_t* reader) { - MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_map)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_map))); } void mpack_done_str(mpack_reader_t* reader) { - MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_str)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_str))); } void mpack_done_bin(mpack_reader_t* reader) { - MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_bin)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_bin))); } void mpack_done_ext(mpack_reader_t* reader) { - MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_ext)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, mpack_type_ext))); } void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) { - MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, type)); + MPACK_UNUSED(MPACK_READER_TRACK(reader, mpack_track_pop(&reader->track, type))); } #endif -#if MPACK_DEBUG && MPACK_STDIO && MPACK_SETJMP && !MPACK_NO_PRINT +#if MPACK_DEBUG && MPACK_STDIO && !MPACK_NO_PRINT static void mpack_debug_print_element(mpack_reader_t* reader, size_t depth) { mpack_tag_t val = mpack_read_tag(reader); + if (mpack_reader_error(reader) != mpack_ok) + return; switch (val.type) { case mpack_type_nil: @@ -680,6 +693,8 @@ static void mpack_debug_print_element(mpack_reader_t* reader, size_t depth) { // skip data for (size_t i = 0; i < val.v.l; ++i) mpack_read_native_u8(reader); + if (mpack_reader_error(reader) != mpack_ok) + return; printf(""); mpack_done_bin(reader); break; @@ -688,6 +703,8 @@ static void mpack_debug_print_element(mpack_reader_t* reader, size_t depth) { // skip data for (size_t i = 0; i < val.v.l; ++i) mpack_read_native_u8(reader); + if (mpack_reader_error(reader) != mpack_ok) + return; printf("", val.exttype); mpack_done_ext(reader); break; @@ -697,6 +714,8 @@ static void mpack_debug_print_element(mpack_reader_t* reader, size_t depth) { for (size_t i = 0; i < val.v.l; ++i) { char c; mpack_read_bytes(reader, &c, 1); + if (mpack_reader_error(reader) != mpack_ok) + return; switch (c) { case '\n': printf("\\n"); break; case '\\': printf("\\\\"); break; @@ -711,9 +730,13 @@ static void mpack_debug_print_element(mpack_reader_t* reader, size_t depth) { case mpack_type_array: printf("[\n"); for (size_t i = 0; i < val.v.n; ++i) { + if (mpack_reader_error(reader) != mpack_ok) + return; for (size_t j = 0; j < depth + 1; ++j) printf(" "); mpack_debug_print_element(reader, depth + 1); + if (mpack_reader_error(reader) != mpack_ok) + return; if (i != val.v.n - 1) putchar(','); putchar('\n'); @@ -730,8 +753,12 @@ static void mpack_debug_print_element(mpack_reader_t* reader, size_t depth) { for (size_t j = 0; j < depth + 1; ++j) printf(" "); mpack_debug_print_element(reader, depth + 1); + if (mpack_reader_error(reader) != mpack_ok) + return; printf(": "); mpack_debug_print_element(reader, depth + 1); + if (mpack_reader_error(reader) != mpack_ok) + return; if (i != val.v.n - 1) putchar(','); putchar('\n'); @@ -747,10 +774,6 @@ static void mpack_debug_print_element(mpack_reader_t* reader, size_t depth) { void mpack_debug_print(const char* data, int len) { mpack_reader_t reader; mpack_reader_init_data(&reader, data, len); - if (MPACK_READER_SETJMP(&reader)) { - printf("\n", mpack_error_to_string(mpack_reader_error(&reader))); - return; - } int depth = 2; for (int i = 0; i < depth; ++i) @@ -758,7 +781,9 @@ void mpack_debug_print(const char* data, int len) { mpack_debug_print_element(&reader, depth); putchar('\n'); - if (mpack_reader_remaining(&reader, NULL) > 0) + if (mpack_reader_error(&reader) != mpack_ok) + printf("\n", mpack_error_to_string(mpack_reader_error(&reader))); + else if (mpack_reader_remaining(&reader, NULL) > 0) printf("<%i extra bytes at end of mpack>\n", (int)mpack_reader_remaining(&reader, NULL)); } #endif diff --git a/src/mpack/mpack-reader.h b/src/mpack/mpack-reader.h index c69326b..c8aa9d4 100644 --- a/src/mpack/mpack-reader.h +++ b/src/mpack/mpack-reader.h @@ -71,7 +71,33 @@ typedef struct mpack_reader_t mpack_reader_t; * * In case of error, it should flag an appropriate error on the reader. */ -typedef size_t (*mpack_fill_t)(mpack_reader_t* reader, char* buffer, size_t count); +typedef size_t (*mpack_reader_fill_t)(mpack_reader_t* reader, char* buffer, size_t count); + +/** + * An error handler function to be called when an error is flagged on + * the reader. + * + * The error handler will only be called once on the first error flagged; + * any subsequent reads and errors are ignored, and the reader is + * permanently in that error state. + * + * MPack is safe against non-local jumps out of error handler callbacks. + * This means you are allowed to longjmp or throw an exception (in C++ + * or with SEH) out of this callback. + * + * Bear in mind when using longjmp that local non-volatile variables that + * have changed are undefined when setjmp() returns, so you can't put the + * reader on the stack in the same activation frame as the setjmp without + * declaring it volatile.) + * + * You must still eventually destroy the reader. It is not destroyed + * automatically when an error is flagged. It is safe to destroy the + * reader within this error callback, but you will either need to perform + * a non-local jump, or store something in your context to identify + * that the reader is destroyed since any future accesses to it cause + * undefined behavior. + */ +typedef void (*mpack_reader_error_t)(mpack_reader_t* reader, mpack_error_t error); /** * A teardown function to be called when the reader is destroyed. @@ -79,7 +105,8 @@ typedef size_t (*mpack_fill_t)(mpack_reader_t* reader, char* buffer, size_t coun typedef void (*mpack_reader_teardown_t)(mpack_reader_t* reader); struct mpack_reader_t { - mpack_fill_t fill; /* Function to read bytes into the buffer */ + mpack_reader_fill_t fill; /* Function to read bytes into the buffer */ + mpack_reader_error_t error_fn; /* Function to call on error */ mpack_reader_teardown_t teardown; /* Function to teardown the context on destroy */ void* context; /* Context for reader callbacks */ @@ -89,55 +116,11 @@ struct mpack_reader_t { size_t pos; /* Position within the buffer */ mpack_error_t error; /* Error state */ - #if MPACK_SETJMP - /* Optional jump target in case of error (pointer because it's - * very large and may be unused) */ - jmp_buf* jump_env; - #endif - #if MPACK_READ_TRACKING mpack_track_t track; /* Stack of map/array/str/bin/ext reads */ #endif }; -#if MPACK_SETJMP - -/** - * @hideinitializer - * - * Registers a jump target in case of error. - * - * If the reader is in an error state, 1 is returned when this is called. Otherwise - * 0 is returned when this is called, and when the first error occurs, control flow - * will jump to the point where this was called, resuming as though it returned 1. - * This ensures an error handling block runs exactly once in case of error. - * - * A reader that jumps still needs to be destroyed. You must call - * mpack_reader_destroy() in your jump handler after getting the final error state. - * - * The argument may be evaluated multiple times. - * - * @returns 0 if the reader is not in an error state; 1 if and when an error occurs. - * @see mpack_reader_destroy() - */ -#define MPACK_READER_SETJMP(reader) \ - (mpack_assert((reader)->jump_env == NULL, "already have a jump set!"), \ - ((reader)->error != mpack_ok) ? 1 : \ - !((reader)->jump_env = (jmp_buf*)MPACK_MALLOC(sizeof(jmp_buf))) ? \ - ((reader)->error = mpack_error_memory, 1) : \ - (setjmp(*(reader)->jump_env))) - -/** - * Clears a jump target. Subsequent read errors will not cause the reader to - * jump. - */ -static inline void mpack_reader_clearjmp(mpack_reader_t* reader) { - if (reader->jump_env) - MPACK_FREE(reader->jump_env); - reader->jump_env = NULL; -} -#endif - /** * Initializes an mpack reader with the given buffer. The reader does * not assume ownership of the buffer, but the buffer must be writeable @@ -224,7 +207,7 @@ void mpack_reader_destroy_cancel(mpack_reader_t* reader); * @param reader The MPack reader. * @param context User data to pass to the reader callbacks. */ -static inline void mpack_reader_set_context(mpack_reader_t* reader, void* context) { +MPACK_INLINE void mpack_reader_set_context(mpack_reader_t* reader, void* context) { reader->context = context; } @@ -240,11 +223,28 @@ static inline void mpack_reader_set_context(mpack_reader_t* reader, void* contex * @param reader The MPack reader. * @param fill The function to fetch additional data into the buffer. */ -static inline void mpack_reader_set_fill(mpack_reader_t* reader, mpack_fill_t fill) { +MPACK_INLINE void mpack_reader_set_fill(mpack_reader_t* reader, mpack_reader_fill_t fill) { mpack_assert(reader->size != 0, "cannot use fill function without a writeable buffer!"); reader->fill = fill; } +/** + * Sets the error function to call when an error is flagged on the reader. + * + * This should normally be used with mpack_reader_set_context() to register + * a custom pointer to pass to the error function. + * + * See the definition of mpack_reader_error_t for more information about + * what you can do from an error callback. + * + * @see mpack_reader_error_t + * @param reader The MPack reader. + * @param error The function to call when an error is flagged on the reader. + */ +MPACK_INLINE void mpack_reader_set_error_handler(mpack_reader_t* reader, mpack_reader_error_t error_fn) { + reader->error_fn = error_fn; +} + /** * Sets the teardown function to call when the reader is destroyed. * @@ -254,10 +254,20 @@ static inline void mpack_reader_set_fill(mpack_reader_t* reader, mpack_fill_t fi * @param reader The MPack reader. * @param teardown The function to call when the reader is destroyed. */ -static inline void mpack_reader_set_teardown(mpack_reader_t* reader, mpack_reader_teardown_t teardown) { +MPACK_INLINE void mpack_reader_set_teardown(mpack_reader_t* reader, mpack_reader_teardown_t teardown) { reader->teardown = teardown; } +/** + * Queries the error state of the MPack reader. + * + * If a reader is in an error state, you should discard all data since the + * last time the error flag was checked. The error flag cannot be cleared. + */ +MPACK_INLINE mpack_error_t mpack_reader_error(mpack_reader_t* reader) { + return reader->error; +} + /** * Places the reader in the given error state, jumping if a jump target is set. * @@ -270,18 +280,24 @@ static inline void mpack_reader_set_teardown(mpack_reader_t* reader, mpack_reade void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error); /** - * Places the reader in the given error state if the given error is not mpack_ok. + * Places the reader in the given error state if the given error is not mpack_ok, + * returning the resulting error state of the reader. * * This allows you to externally flag errors, for example if you are validating * data as you read it. * - * If the error is mpack_ok, or if the reader is already in an error state, this - * call is ignored and no jump is performed. + * If the given error is mpack_ok or if the reader is already in an error state, + * this call is ignored and the actual error state of the reader is returned. */ -static inline void mpack_reader_flag_if_error(mpack_reader_t* reader, mpack_error_t error) { +MPACK_INLINE_SPEED mpack_error_t mpack_reader_flag_if_error(mpack_reader_t* reader, mpack_error_t error); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED mpack_error_t mpack_reader_flag_if_error(mpack_reader_t* reader, mpack_error_t error) { if (error != mpack_ok) mpack_reader_flag_error(reader, error); + return mpack_reader_error(reader); } +#endif /** * Returns bytes left in the reader's buffer. @@ -301,23 +317,12 @@ static inline void mpack_reader_flag_if_error(mpack_reader_t* reader, mpack_erro */ size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data); -/** - * Queries the error state of the MPack reader. - * - * If a reader is in an error state, you should discard all data since the - * last time the error flag was checked. The error flag cannot be cleared. - */ -static inline mpack_error_t mpack_reader_error(mpack_reader_t* reader) { - return reader->error; -} - /** * Reads a MessagePack object header (an MPack tag.) * - * If an error occurs, the mpack_reader_t is placed in an error state, a - * longjmp is performed (if set), and the return value is undefined. - * If the reader is already in an error state, the return value - * is undefined. + * If an error occurs, the mpack_reader_t is placed in an error state and + * a nil tag is returned. If the reader is already in an error state, a + * nil tag is returned. * * If the type is compound (i.e. is a map, array, string, binary or * extension type), additional reads are required to get the actual data, @@ -387,9 +392,13 @@ const char* mpack_read_bytes_inplace(mpack_reader_t* reader, size_t count); * * @see mpack_read_bytes_inplace() */ -static inline bool mpack_should_read_bytes_inplace(mpack_reader_t* reader, size_t count) { +MPACK_INLINE_SPEED bool mpack_should_read_bytes_inplace(mpack_reader_t* reader, size_t count); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED bool mpack_should_read_bytes_inplace(mpack_reader_t* reader, size_t count) { return (reader->size == 0 || count > reader->size / 8); } +#endif #if MPACK_READ_TRACKING /** @@ -443,12 +452,12 @@ void mpack_done_ext(mpack_reader_t* reader); */ void mpack_done_type(mpack_reader_t* reader, mpack_type_t type); #else -static inline void mpack_done_array(mpack_reader_t* reader) {MPACK_UNUSED(reader);} -static inline void mpack_done_map(mpack_reader_t* reader) {MPACK_UNUSED(reader);} -static inline void mpack_done_str(mpack_reader_t* reader) {MPACK_UNUSED(reader);} -static inline void mpack_done_bin(mpack_reader_t* reader) {MPACK_UNUSED(reader);} -static inline void mpack_done_ext(mpack_reader_t* reader) {MPACK_UNUSED(reader);} -static inline void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {MPACK_UNUSED(reader); MPACK_UNUSED(type);} +MPACK_INLINE void mpack_done_array(mpack_reader_t* reader) {MPACK_UNUSED(reader);} +MPACK_INLINE void mpack_done_map(mpack_reader_t* reader) {MPACK_UNUSED(reader);} +MPACK_INLINE void mpack_done_str(mpack_reader_t* reader) {MPACK_UNUSED(reader);} +MPACK_INLINE void mpack_done_bin(mpack_reader_t* reader) {MPACK_UNUSED(reader);} +MPACK_INLINE void mpack_done_ext(mpack_reader_t* reader) {MPACK_UNUSED(reader);} +MPACK_INLINE void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {MPACK_UNUSED(reader); MPACK_UNUSED(type);} #endif /** @@ -457,7 +466,7 @@ static inline void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {M */ void mpack_discard(mpack_reader_t* reader); -#if MPACK_DEBUG && MPACK_STDIO && MPACK_SETJMP && !MPACK_NO_PRINT +#if MPACK_DEBUG && MPACK_STDIO && !MPACK_NO_PRINT /*! Converts a chunk of messagepack to JSON and pretty-prints it to stdout. */ void mpack_debug_print(const char* data, int len); #endif @@ -474,7 +483,10 @@ void mpack_read_native_big(mpack_reader_t* reader, char* p, size_t count); // Reads count bytes into p, deferring to mpack_read_native_big() if more // bytes are needed than are available in the buffer. -static inline void mpack_read_native(mpack_reader_t* reader, char* p, size_t count) { +MPACK_INLINE_SPEED void mpack_read_native(mpack_reader_t* reader, char* p, size_t count); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_read_native(mpack_reader_t* reader, char* p, size_t count) { if (count > reader->left) { mpack_read_native_big(reader, p, count); } else { @@ -483,19 +495,21 @@ static inline void mpack_read_native(mpack_reader_t* reader, char* p, size_t cou reader->left -= count; } } +#endif -// Reads native bytes with jump disabled. This allows mpack reader functions -// to hold an allocated buffer and read native data into it without leaking it. -static inline void mpack_read_native_nojump(mpack_reader_t* reader, char* p, size_t count) { - #if MPACK_SETJMP - jmp_buf* jump_env = reader->jump_env; - reader->jump_env = NULL; - #endif +// Reads native bytes with error callback disabled. This allows mpack reader functions +// to hold an allocated buffer and read native data into it without leaking it in +// case of a non-local jump out of an error handler. +MPACK_INLINE_SPEED void mpack_read_native_nojump(mpack_reader_t* reader, char* p, size_t count); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED void mpack_read_native_nojump(mpack_reader_t* reader, char* p, size_t count) { + mpack_reader_error_t error_fn = reader->error_fn; + reader->error_fn = NULL; mpack_read_native(reader, p, count); - #if MPACK_SETJMP - reader->jump_env = jump_env; - #endif + reader->error_fn = error_fn; } +#endif MPACK_ALWAYS_INLINE uint8_t mpack_read_native_u8(mpack_reader_t* reader) { if (reader->left >= sizeof(uint8_t)) { @@ -573,19 +587,27 @@ MPACK_ALWAYS_INLINE double mpack_read_native_double(mpack_reader_t* reader) { } #if MPACK_READ_TRACKING -#define MPACK_READER_TRACK(reader, error) mpack_reader_flag_if_error(reader, error) +#define MPACK_READER_TRACK(reader, error) mpack_reader_flag_if_error((reader), (error)) #else -#define MPACK_READER_TRACK(reader, error) MPACK_UNUSED(reader) +#define MPACK_READER_TRACK(reader, error) (MPACK_UNUSED(reader), mpack_ok) #endif -static inline void mpack_reader_track_element(mpack_reader_t* reader) { - MPACK_READER_TRACK(reader, mpack_track_element(&reader->track, true)); +MPACK_INLINE_SPEED mpack_error_t mpack_reader_track_element(mpack_reader_t* reader); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED mpack_error_t mpack_reader_track_element(mpack_reader_t* reader) { + return MPACK_READER_TRACK(reader, mpack_track_element(&reader->track, true)); } +#endif -static inline void mpack_reader_track_bytes(mpack_reader_t* reader, uint64_t count) { - MPACK_READER_TRACK(reader, mpack_track_bytes(&reader->track, true, count)); +MPACK_INLINE_SPEED mpack_error_t mpack_reader_track_bytes(mpack_reader_t* reader, uint64_t count); + +#if MPACK_DEFINE_INLINE_SPEED +MPACK_INLINE_SPEED mpack_error_t mpack_reader_track_bytes(mpack_reader_t* reader, uint64_t count) { MPACK_UNUSED(count); + return MPACK_READER_TRACK(reader, mpack_track_bytes(&reader->track, true, count)); } +#endif #endif diff --git a/src/mpack/mpack-writer.c b/src/mpack/mpack-writer.c index 5d17e1c..9c102cf 100644 --- a/src/mpack/mpack-writer.c +++ b/src/mpack/mpack-writer.c @@ -28,7 +28,7 @@ #if MPACK_WRITE_TRACKING #define MPACK_WRITER_TRACK(writer, error) mpack_writer_flag_if_error(writer, error) -static inline void mpack_writer_flag_if_error(mpack_writer_t* writer, mpack_error_t error) { +MPACK_STATIC_INLINE_SPEED void mpack_writer_flag_if_error(mpack_writer_t* writer, mpack_error_t error) { if (error != mpack_ok) mpack_writer_flag_error(writer, error); } @@ -36,7 +36,7 @@ static inline void mpack_writer_flag_if_error(mpack_writer_t* writer, mpack_erro #define MPACK_WRITER_TRACK(writer, error) MPACK_UNUSED(writer) #endif -static inline void mpack_writer_track_element(mpack_writer_t* writer) { +MPACK_STATIC_INLINE_SPEED void mpack_writer_track_element(mpack_writer_t* writer) { MPACK_WRITER_TRACK(writer, mpack_track_element(&writer->track, true)); } @@ -213,10 +213,8 @@ void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error) { if (writer->error == mpack_ok) { writer->error = error; - #if MPACK_SETJMP - if (writer->jump_env) - longjmp(*writer->jump_env, 1); - #endif + if (writer->error_fn) + writer->error_fn(writer, writer->error); } } @@ -273,7 +271,7 @@ static void mpack_write_native_big(mpack_writer_t* writer, const char* p, size_t } } -static inline void mpack_write_native(mpack_writer_t* writer, const char* p, size_t count) { +MPACK_STATIC_INLINE_SPEED void mpack_write_native(mpack_writer_t* writer, const char* p, size_t count) { if (mpack_writer_error(writer) != mpack_ok) return; if (writer->size - writer->used < count) { @@ -315,7 +313,7 @@ MPACK_ALWAYS_INLINE void mpack_store_native_u64_at(char* p, uint64_t val) { u[7] = (uint8_t)( val & 0xFF); } -static inline void mpack_write_native_u8(mpack_writer_t* writer, uint8_t val) { +MPACK_STATIC_INLINE_SPEED void mpack_write_native_u8(mpack_writer_t* writer, uint8_t val) { if (writer->size - writer->used >= sizeof(val)) { mpack_store_native_u8_at(writer->buffer + writer->used, val); writer->used += sizeof(val); @@ -326,7 +324,7 @@ static inline void mpack_write_native_u8(mpack_writer_t* writer, uint8_t val) { } } -static inline void mpack_write_native_u16(mpack_writer_t* writer, uint16_t val) { +MPACK_STATIC_INLINE_SPEED void mpack_write_native_u16(mpack_writer_t* writer, uint16_t val) { if (writer->size - writer->used >= sizeof(val)) { mpack_store_native_u16_at(writer->buffer + writer->used, val); writer->used += sizeof(val); @@ -337,7 +335,7 @@ static inline void mpack_write_native_u16(mpack_writer_t* writer, uint16_t val) } } -static inline void mpack_write_native_u32(mpack_writer_t* writer, uint32_t val) { +MPACK_STATIC_INLINE_SPEED void mpack_write_native_u32(mpack_writer_t* writer, uint32_t val) { if (writer->size - writer->used >= sizeof(val)) { mpack_store_native_u32_at(writer->buffer + writer->used, val); writer->used += sizeof(val); @@ -348,7 +346,7 @@ static inline void mpack_write_native_u32(mpack_writer_t* writer, uint32_t val) } } -static inline void mpack_write_native_u64(mpack_writer_t* writer, uint64_t val) { +MPACK_STATIC_INLINE_SPEED void mpack_write_native_u64(mpack_writer_t* writer, uint64_t val) { if (writer->size - writer->used >= sizeof(val)) { mpack_store_native_u64_at(writer->buffer + writer->used, val); writer->used += sizeof(val); @@ -359,13 +357,13 @@ static inline void mpack_write_native_u64(mpack_writer_t* writer, uint64_t val) } } -static inline void mpack_write_native_i8 (mpack_writer_t* writer, int8_t val) {mpack_write_native_u8 (writer, (uint8_t )val);} -static inline void mpack_write_native_i16 (mpack_writer_t* writer, int16_t val) {mpack_write_native_u16 (writer, (uint16_t)val);} -static inline void mpack_write_native_i32 (mpack_writer_t* writer, int32_t val) {mpack_write_native_u32 (writer, (uint32_t)val);} -static inline void mpack_write_native_i64 (mpack_writer_t* writer, int64_t val) {mpack_write_native_u64 (writer, (uint64_t)val);} +MPACK_STATIC_INLINE void mpack_write_native_i8 (mpack_writer_t* writer, int8_t val) {mpack_write_native_u8 (writer, (uint8_t )val);} +MPACK_STATIC_INLINE void mpack_write_native_i16 (mpack_writer_t* writer, int16_t val) {mpack_write_native_u16 (writer, (uint16_t)val);} +MPACK_STATIC_INLINE void mpack_write_native_i32 (mpack_writer_t* writer, int32_t val) {mpack_write_native_u32 (writer, (uint32_t)val);} +MPACK_STATIC_INLINE void mpack_write_native_i64 (mpack_writer_t* writer, int64_t val) {mpack_write_native_u64 (writer, (uint64_t)val);} -static inline void mpack_write_native_float(mpack_writer_t* writer, float value) { +MPACK_STATIC_INLINE_SPEED void mpack_write_native_float(mpack_writer_t* writer, float value) { union { float f; uint32_t i; @@ -374,7 +372,7 @@ static inline void mpack_write_native_float(mpack_writer_t* writer, float value) mpack_write_native_u32(writer, u.i); } -static inline void mpack_write_native_double(mpack_writer_t* writer, double value) { +MPACK_STATIC_INLINE_SPEED void mpack_write_native_double(mpack_writer_t* writer, double value) { union { double d; uint64_t i; @@ -397,12 +395,6 @@ mpack_error_t mpack_writer_destroy(mpack_writer_t* writer) { writer->teardown = NULL; } - #if MPACK_SETJMP - if (writer->jump_env) - MPACK_FREE(writer->jump_env); - writer->jump_env = NULL; - #endif - return writer->error; } @@ -412,12 +404,6 @@ void mpack_writer_destroy_cancel(mpack_writer_t* writer) { if (writer->teardown) writer->teardown(writer); writer->teardown = NULL; - - #if MPACK_SETJMP - if (writer->jump_env) - MPACK_FREE(writer->jump_env); - writer->jump_env = NULL; - #endif } void mpack_write_tag(mpack_writer_t* writer, mpack_tag_t value) { diff --git a/src/mpack/mpack-writer.h b/src/mpack/mpack-writer.h index feef480..ceb2766 100644 --- a/src/mpack/mpack-writer.h +++ b/src/mpack/mpack-writer.h @@ -61,12 +61,38 @@ typedef struct mpack_writer_t mpack_writer_t; /** * The mpack writer's flush function to flush the buffer to the output stream. - * It should flag an appropriate error on the writer if flushing fails. - * Keep in mind that flagging an error may longjmp. + * It should flag an appropriate error on the writer if flushing fails (usually + * mpack_error_io.) * * The specified context for callbacks is at writer->context. */ -typedef void (*mpack_flush_t)(mpack_writer_t* writer, const char* buffer, size_t count); +typedef void (*mpack_writer_flush_t)(mpack_writer_t* writer, const char* buffer, size_t count); + +/** + * An error handler function to be called when an error is flagged on + * the writer. + * + * The error handler will only be called once on the first error flagged; + * any subsequent writes and errors are ignored, and the writer is + * permanently in that error state. + * + * MPack is safe against non-local jumps out of error handler callbacks. + * This means you are allowed to longjmp or throw an exception (in C++ + * or with SEH) out of this callback. + * + * Bear in mind when using longjmp that local non-volatile variables that + * have changed are undefined when setjmp() returns, so you can't put the + * writer on the stack in the same activation frame as the setjmp without + * declaring it volatile.) + * + * You must still eventually destroy the writer. It is not destroyed + * automatically when an error is flagged. It is safe to destroy the + * writer within this error callback, but you will either need to perform + * a non-local jump, or store something in your context to identify + * that the writer is destroyed since any future accesses to it cause + * undefined behavior. + */ +typedef void (*mpack_writer_error_t)(mpack_writer_t* writer, mpack_error_t error); /** * A teardown function to be called when the writer is destroyed. @@ -74,7 +100,8 @@ typedef void (*mpack_flush_t)(mpack_writer_t* writer, const char* buffer, size_t typedef void (*mpack_writer_teardown_t)(mpack_writer_t* writer); struct mpack_writer_t { - mpack_flush_t flush; /* Function to write bytes to the output stream */ + mpack_writer_flush_t flush; /* Function to write bytes to the output stream */ + mpack_writer_error_t error_fn; /* Function to call on error */ mpack_writer_teardown_t teardown; /* Function to teardown the context on destroy */ void* context; /* Context for writer callbacks */ @@ -83,12 +110,6 @@ struct mpack_writer_t { size_t used; /* How many bytes have been written into the buffer */ mpack_error_t error; /* Error state */ - #if MPACK_SETJMP - /* Optional jump target in case of error (pointer because it's - * very large and may be unused) */ - jmp_buf* jump_env; - #endif - #if MPACK_WRITE_TRACKING mpack_track_t track; /* Stack of map/array/str/bin/ext writes */ #endif @@ -149,7 +170,8 @@ void mpack_writer_init_file(mpack_writer_t* writer, const char* filename); * @def mpack_writer_init_stack(writer, flush, context) * @hideinitializer * - * Initializes an mpack writer using stack space. + * Initializes an mpack writer using stack space as a buffer. A flush function + * should be added to the writer to flush the buffer. */ #define mpack_writer_init_stack_line_ex(line, writer) \ @@ -162,44 +184,6 @@ void mpack_writer_init_file(mpack_writer_t* writer, const char* filename); #define mpack_writer_init_stack(writer) \ mpack_writer_init_stack_line(__LINE__, (writer)) -#if MPACK_SETJMP - -/** - * @hideinitializer - * - * Registers a jump target in case of error. - * - * If the writer is in an error state, 1 is returned when this is called. Otherwise - * 0 is returned when this is called, and when the first error occurs, control flow - * will jump to the point where this was called, resuming as though it returned 1. - * This ensures an error handling block runs exactly once in case of error. - * - * A writer that jumps still needs to be destroyed. You must call - * mpack_writer_destroy() in your jump handler after getting the final error state. - * - * The argument may be evaluated multiple times. - * - * @returns 0 if the writer is not in an error state; 1 if and when an error occurs. - * @see mpack_writer_destroy() - */ -#define MPACK_WRITER_SETJMP(writer) \ - (mpack_assert((writer)->jump_env == NULL, "already have a jump set!"), \ - ((writer)->error != mpack_ok) ? 1 : \ - !((writer)->jump_env = (jmp_buf*)MPACK_MALLOC(sizeof(jmp_buf))) ? \ - ((writer)->error = mpack_error_memory, 1) : \ - (setjmp(*(writer)->jump_env))) - -/** - * Clears a jump target. Subsequent write errors will not cause the writer to - * jump. - */ -static inline void mpack_writer_clearjmp(mpack_writer_t* writer) { - if (writer->jump_env) - MPACK_FREE(writer->jump_env); - writer->jump_env = NULL; -} -#endif - /** * Cleans up the mpack writer, flushing any buffered bytes to the * underlying stream, if any. Returns the final error state of the @@ -230,7 +214,7 @@ void mpack_writer_destroy_cancel(mpack_writer_t* writer); * @param writer The MPack writer. * @param context User data to pass to the writer callbacks. */ -static inline void mpack_writer_set_context(mpack_writer_t* writer, void* context) { +MPACK_INLINE void mpack_writer_set_context(mpack_writer_t* writer, void* context) { writer->context = context; } @@ -246,11 +230,28 @@ static inline void mpack_writer_set_context(mpack_writer_t* writer, void* contex * @param writer The MPack writer. * @param flush The function to write out data from the buffer. */ -static inline void mpack_writer_set_flush(mpack_writer_t* writer, mpack_flush_t flush) { +MPACK_INLINE void mpack_writer_set_flush(mpack_writer_t* writer, mpack_writer_flush_t flush) { mpack_assert(writer->size != 0, "cannot use flush function without a writeable buffer!"); writer->flush = flush; } +/** + * Sets the error function to call when an error is flagged on the writer. + * + * This should normally be used with mpack_writer_set_context() to register + * a custom pointer to pass to the error function. + * + * See the definition of mpack_writer_error_t for more information about + * what you can do from an error callback. + * + * @see mpack_writer_error_t + * @param writer The MPack writer. + * @param error The function to call when an error is flagged on the writer. + */ +MPACK_INLINE void mpack_writer_set_error_handler(mpack_writer_t* writer, mpack_writer_error_t error_fn) { + writer->error_fn = error_fn; +} + /** * Sets the teardown function to call when the writer is destroyed. * @@ -260,7 +261,7 @@ static inline void mpack_writer_set_flush(mpack_writer_t* writer, mpack_flush_t * @param writer The MPack writer. * @param teardown The function to call when the writer is destroyed. */ -static inline void mpack_writer_set_teardown(mpack_writer_t* writer, mpack_writer_teardown_t teardown) { +MPACK_INLINE void mpack_writer_set_teardown(mpack_writer_t* writer, mpack_writer_teardown_t teardown) { writer->teardown = teardown; } @@ -269,7 +270,7 @@ static inline void mpack_writer_set_teardown(mpack_writer_t* writer, mpack_write * may be less than the total number of bytes written if bytes have * been flushed to an underlying stream. */ -static inline size_t mpack_writer_buffer_used(mpack_writer_t* writer) { +MPACK_INLINE size_t mpack_writer_buffer_used(mpack_writer_t* writer) { return writer->used; } @@ -291,7 +292,7 @@ void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error); * If a writer is in an error state, you should discard all data since the * last time the error flag was checked. The error flag cannot be cleared. */ -static inline mpack_error_t mpack_writer_error(mpack_writer_t* writer) { +MPACK_INLINE mpack_error_t mpack_writer_error(mpack_writer_t* writer) { return writer->error; } @@ -327,7 +328,7 @@ void mpack_write_i32(mpack_writer_t* writer, int32_t value); void mpack_write_i64(mpack_writer_t* writer, int64_t value); /*! Writes an integer in the most efficient packing available. */ -static inline void mpack_write_int(mpack_writer_t* writer, int64_t value) { +MPACK_INLINE void mpack_write_int(mpack_writer_t* writer, int64_t value) { mpack_write_i64(writer, value); } @@ -344,7 +345,7 @@ void mpack_write_u32(mpack_writer_t* writer, uint32_t value); void mpack_write_u64(mpack_writer_t* writer, uint64_t value); /*! Writes an unsigned integer in the most efficient packing available. */ -static inline void mpack_write_uint(mpack_writer_t* writer, uint64_t value) { +MPACK_INLINE void mpack_write_uint(mpack_writer_t* writer, uint64_t value) { mpack_write_u64(writer, value); } @@ -503,12 +504,12 @@ void mpack_finish_ext(mpack_writer_t* writer); */ void mpack_finish_type(mpack_writer_t* writer, mpack_type_t type); #else -static inline void mpack_finish_array(mpack_writer_t* writer) {MPACK_UNUSED(writer);} -static inline void mpack_finish_map(mpack_writer_t* writer) {MPACK_UNUSED(writer);} -static inline void mpack_finish_str(mpack_writer_t* writer) {MPACK_UNUSED(writer);} -static inline void mpack_finish_bin(mpack_writer_t* writer) {MPACK_UNUSED(writer);} -static inline void mpack_finish_ext(mpack_writer_t* writer) {MPACK_UNUSED(writer);} -static inline void mpack_finish_type(mpack_writer_t* writer, mpack_type_t type) {MPACK_UNUSED(writer); MPACK_UNUSED(type);} +MPACK_INLINE void mpack_finish_array(mpack_writer_t* writer) {MPACK_UNUSED(writer);} +MPACK_INLINE void mpack_finish_map(mpack_writer_t* writer) {MPACK_UNUSED(writer);} +MPACK_INLINE void mpack_finish_str(mpack_writer_t* writer) {MPACK_UNUSED(writer);} +MPACK_INLINE void mpack_finish_bin(mpack_writer_t* writer) {MPACK_UNUSED(writer);} +MPACK_INLINE void mpack_finish_ext(mpack_writer_t* writer) {MPACK_UNUSED(writer);} +MPACK_INLINE void mpack_finish_type(mpack_writer_t* writer, mpack_type_t type) {MPACK_UNUSED(writer); MPACK_UNUSED(type);} #endif /** diff --git a/test/main.c b/test/main.c index 0a8291f..943cca3 100644 --- a/test/main.c +++ b/test/main.c @@ -30,6 +30,8 @@ #include "test-node.h" #include "test-file.h" +mpack_tag_t (*fn_mpack_tag_nil)(void) = &mpack_tag_nil; + int passes; int tests; diff --git a/test/mpack-config.h b/test/mpack-config.h index 8b9a932..5387ad8 100644 --- a/test/mpack-config.h +++ b/test/mpack-config.h @@ -19,7 +19,6 @@ #define MPACK_STDLIB 1 #define MPACK_STDIO 1 - #define MPACK_SETJMP 1 #define MPACK_MALLOC test_malloc #define MPACK_FREE test_free #endif @@ -27,10 +26,10 @@ // Tracking matches the default config, except the test suite // also supports MPACK_NO_TRACKING to disable it. #if defined(MPACK_MALLOC) && !defined(MPACK_NO_TRACKING) - #if MPACK_DEBUG && MPACK_READER + #if defined(MPACK_DEBUG) && MPACK_DEBUG && defined(MPACK_READER) && MPACK_READER #define MPACK_READ_TRACKING 1 #endif - #if MPACK_WRITER + #if defined(MPACK_DEBUG) && MPACK_DEBUG && defined(MPACK_WRITER) && MPACK_WRITER #define MPACK_WRITE_TRACKING 1 #endif #endif diff --git a/test/test-expect.c b/test/test-expect.c index 1c9ec95..9df8349 100644 --- a/test/test-expect.c +++ b/test/test-expect.c @@ -29,13 +29,6 @@ static void test_expect_example_read() { mpack_reader_t reader; mpack_reader_init_data(&reader, test, sizeof(test) - 1); - #if MPACK_SETJMP - if (MPACK_READER_SETJMP(&reader)) { - test_assert(0, "jumped! error: %s", mpack_error_to_string(mpack_reader_error(&reader))); - return; - } - #endif - test_assert(2 == mpack_expect_map(&reader)); mpack_expect_cstr_match(&reader, "compact"); test_assert(true == mpack_expect_bool(&reader)); diff --git a/test/test-expect.h b/test/test-expect.h index 43da2c7..d008b70 100644 --- a/test/test-expect.h +++ b/test/test-expect.h @@ -72,7 +72,7 @@ extern "C" { "reader flagged error %i", (int)mpack_reader_error(reader)); \ } while (0) -#if MPACK_EXPECT +#ifdef MPACK_EXPECT void test_expect(void); #endif diff --git a/test/test-file.h b/test/test-file.h index 4f8ab01..2b073ec 100644 --- a/test/test-file.h +++ b/test/test-file.h @@ -32,7 +32,7 @@ extern "C" { #endif -#if MPACK_STDIO +#ifdef MPACK_STDIO void test_file(void); #endif diff --git a/test/test-node.c b/test/test-node.c index 080175b..01fd20e 100644 --- a/test/test-node.c +++ b/test/test-node.c @@ -32,13 +32,6 @@ static void test_example_node() { // non-simple tests use paging unless malloc is unavailable. mpack_node_data_t pool[128]; mpack_tree_init_pool(&tree, test, sizeof(test) - 1, pool, sizeof(pool) / sizeof(*pool)); - - #if MPACK_SETJMP - if (MPACK_TREE_SETJMP(&tree)) { - test_assert(0, "jumped! error: %s", mpack_error_to_string(mpack_tree_error(&tree))); - return; - } - #endif test_assert(mpack_tree_error(&tree) == mpack_ok); mpack_node_t map = mpack_tree_root(&tree); diff --git a/test/test-node.h b/test/test-node.h index bcd818f..869a6d5 100644 --- a/test/test-node.h +++ b/test/test-node.h @@ -28,7 +28,7 @@ extern "C" { #endif -#if MPACK_NODE +#ifdef MPACK_NODE #define test_tree_destroy_noerror(tree) do { \ mpack_error_t error = mpack_tree_destroy(tree); \ diff --git a/test/test-tag.c b/test/test-tag.c index e5f843c..c3ae502 100644 --- a/test/test-tag.c +++ b/test/test-tag.c @@ -26,6 +26,10 @@ void test_tags() { + // ensure there is only one inline definition (the other + // address here is in main) + test_assert(fn_mpack_tag_nil == &mpack_tag_nil); + // uints test_assert(mpack_tag_uint(0).v.u == 0); test_assert(mpack_tag_uint(1).v.u == 1); diff --git a/test/test-write.h b/test/test-write.h index 7c7f35c..2f89393 100644 --- a/test/test-write.h +++ b/test/test-write.h @@ -28,7 +28,7 @@ extern "C" { #endif -#if MPACK_WRITER +#ifdef MPACK_WRITER // these setup and destroy test writers and check them for errors. // they are generally macros so that the asserts are on the line of the test. diff --git a/test/test.h b/test/test.h index 48538cd..0284fc2 100644 --- a/test/test.h +++ b/test/test.h @@ -34,16 +34,16 @@ #include #include -#if WIN32 +#ifdef WIN32 #include #define isnanf _isnan #endif -#if __APPLE__ +#ifdef __APPLE__ #define isnanf isnan #endif -#if WIN32 +#ifdef WIN32 #define unlink _unlink #endif @@ -56,6 +56,8 @@ #include "mpack/mpack.h" +extern mpack_tag_t (*fn_mpack_tag_nil)(void); + #ifdef __cplusplus extern "C" { #endif diff --git a/tools/package.sh b/tools/package.sh index 07210b1..82f447e 100755 --- a/tools/package.sh +++ b/tools/package.sh @@ -48,7 +48,8 @@ done echo -e "#endif\n" >> $HEADER # assemble source -echo -e "#define MPACK_INTERNAL 1\n" >> $SOURCE +echo -e "#define MPACK_INTERNAL 1" >> $SOURCE +echo -e "#define MPACK_EMIT_INLINE_DEFS 1\n" >> $SOURCE echo -e "#include \"mpack.h\"\n" >> $SOURCE for f in $FILES; do echo -e "\n/* $f.c */" >> $SOURCE