Giter Club home page Giter Club logo

mpack's Introduction

Introduction

MPack is a C implementation of an encoder and decoder for the MessagePack serialization format. It is:

The core of MPack contains a buffered reader and writer, and a tree-style parser that decodes into a tree of dynamically typed nodes. Helper functions can be enabled to read values of expected type, to work with files, to grow buffers or allocate strings automatically, to check UTF-8 encoding, and more.

The MPack code is small enough to be embedded directly into your codebase. Simply download the amalgamation package and add mpack.h and mpack.c to your project.

MPack supports all modern compilers, all desktop and smartphone OSes, WebAssembly, inside the Linux kernel, and even 8-bit microcontrollers such as Arduino. The MPack featureset can be customized at compile-time to set which features, components and debug checks are compiled, and what dependencies are available.

Build Status

Unit Tests Coverage

The Node API

The Node API parses a chunk of MessagePack data into an immutable tree of dynamically-typed nodes. A series of helper functions can be used to extract data of specific types from each node.

// parse a file into a node tree
mpack_tree_t tree;
mpack_tree_init_filename(&tree, "homepage-example.mp", 0);
mpack_tree_parse(&tree);
mpack_node_t root = mpack_tree_root(&tree);

// extract the example data on the msgpack homepage
bool compact = mpack_node_bool(mpack_node_map_cstr(root, "compact"));
int schema = mpack_node_i32(mpack_node_map_cstr(root, "schema"));

// clean up and check for errors
if (mpack_tree_destroy(&tree) != mpack_ok) {
    fprintf(stderr, "An error occurred decoding the data!\n");
    return;
}

Note that no additional error handling is needed in the above code. If the file is missing or corrupt, if map keys are missing or if nodes are not in the expected types, special "nil" nodes and false/zero values are returned and the tree is placed in an error state. An error check is only needed before using the data.

The above example allocates nodes automatically. A fixed node pool can be provided to the parser instead in memory-constrained environments. For maximum performance and minimal memory usage, the Expect API can be used to parse data of a predefined schema.

The Write API

The Write API encodes structured data to MessagePack.

// encode to memory buffer
char* data;
size_t size;
mpack_writer_t writer;
mpack_writer_init_growable(&writer, &data, &size);

// write the example on the msgpack homepage
mpack_build_map(&writer);
mpack_write_cstr(&writer, "compact");
mpack_write_bool(&writer, true);
mpack_write_cstr(&writer, "schema");
mpack_write_uint(&writer, 0);
mpack_complete_map(&writer);

// finish writing
if (mpack_writer_destroy(&writer) != mpack_ok) {
    fprintf(stderr, "An error occurred encoding the data!\n");
    return;
}

// use the data
do_something_with_data(data, size);
free(data);

In the above example, we encode to a growable memory buffer. The writer can instead write to a pre-allocated or stack-allocated buffer (with up-front sizes for compound types), avoiding the need for memory allocation. The writer can also be provided with a flush function (such as a file or socket write function) to call when the buffer is full or when writing is done.

If any error occurs, the writer is placed in an error state. The writer will flag an error if too much data is written, if the wrong number of elements are written, if an allocation failure occurs, if the data could not be flushed, etc. No additional error handling is needed in the above code; any subsequent writes are ignored when the writer is in an error state, so you don't need to check every write for errors.

The above example uses mpack_build_map() to automatically determine the number of key-value pairs contained. If you know up-front the number of elements needed, you can pass it to mpack_start_map() instead. In that case the corresponding mpack_finish_map() will assert in debug mode that the expected number of elements were actually written, which is something that other MessagePack C/C++ libraries may not do.

Comparison With Other Parsers

MPack is rich in features while maintaining very high performance and a small code footprint. Here's a short feature table comparing it to other C parsers:

MPack
(v1.1)
msgpack-c
(v3.3.0)
CMP
(v19)
CWPack
(v1.3.1)
No libc requirement
Growable memory writer ✓*
File I/O helpers ✓*
Stateful error handling
Incremental parser
Tree stream parser
Compound size tracking
Automatic compound size

A larger feature comparison table is available here which includes descriptions of the various entries in the table.

This benchmarking suite compares the performance of MPack to other implementations of schemaless serialization formats. MPack outperforms all JSON and MessagePack libraries (except CWPack), and in some tests MPack is several times faster than RapidJSON for equivalent data.

Why Not Just Use JSON?

Conceptually, MessagePack stores data similarly to JSON: they are both composed of simple values such as numbers and strings, stored hierarchically in maps and arrays. So why not just use JSON instead? The main reason is that JSON is designed to be human-readable, so it is not as efficient as a binary serialization format:

  • Compound types such as strings, maps and arrays are delimited, so appropriate storage cannot be allocated upfront. The whole object must be parsed to determine its size.

  • Strings are not stored in their native encoding. Special characters such as quotes and backslashes must be escaped when written and converted back when read.

  • Numbers are particularly inefficient (especially when parsing back floats), making JSON inappropriate as a base format for structured data that contains lots of numbers.

  • 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 platform-specific and locale-specific bugs, as well as a greater potential for security vulnerabilites. 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?

Testing MPack

The MPack build process does not build MPack into a library; it is used to build and run the unit tests. You do not need to build MPack or the unit testing suite to use MPack.

See test/README.md for information on how to test MPack.

mpack's People

Contributors

chris-heijdens avatar dirkson avatar ethanjli avatar etodanik avatar ludocode avatar masflam avatar nikitadanilov avatar romansofyin avatar schmidtw avatar shamazmazum avatar timgates42 avatar volks73 avatar wangzhione avatar

Stargazers

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

Watchers

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

mpack's Issues

enum mpack_type_t only exposes only biggest int and uint type

Currently i'm implementing strict protocol checking on the messagepack transport. Only the mpack_type_t exposes int or uint types (8,16,32 -> 64 upcasted). But messagepack has much more types. For our current protocol we have to check if the value of a key is strict uint8_t but this is currently not possible with mpack. My suggestion would be to have a small API breakage and add all uint* and int* sizes. And still put the value in the uint64_t, int64_t in the union. What do you think?

Compile warnings with -Wmissing-prototypes

I'm trying to compile with strict compiler flags and it seems to emit warnings (which are valid):

i'm using the v0.6 amalgamated package

/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:387:31: error: no previous prototype for ‘mpack_realloc’ [-Werror=missing-prototypes]
     MPACK_ALWAYS_INLINE void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) {
                               ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:569:26: error: no previous prototype for ‘mpack_tag_nil’ [-Werror=missing-prototypes]
 MPACK_INLINE mpack_tag_t mpack_tag_nil(void) {
                          ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:577:26: error: no previous prototype for ‘mpack_tag_int’ [-Werror=missing-prototypes]
 MPACK_INLINE mpack_tag_t mpack_tag_int(int64_t value) {
                          ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:586:26: error: no previous prototype for ‘mpack_tag_uint’ [-Werror=missing-prototypes]
 MPACK_INLINE mpack_tag_t mpack_tag_uint(uint64_t value) {
                          ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:595:26: error: no previous prototype for ‘mpack_tag_bool’ [-Werror=missing-prototypes]
 MPACK_INLINE mpack_tag_t mpack_tag_bool(bool value) {
                          ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:604:26: error: no previous prototype for ‘mpack_tag_float’ [-Werror=missing-prototypes]
 MPACK_INLINE mpack_tag_t mpack_tag_float(float value) {
                          ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:613:26: error: no previous prototype for ‘mpack_tag_double’ [-Werror=missing-prototypes]
 MPACK_INLINE mpack_tag_t mpack_tag_double(double value) {
                          ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:648:19: error: no previous prototype for ‘mpack_tag_equal’ [-Werror=missing-prototypes]
 MPACK_INLINE bool mpack_tag_equal(mpack_tag_t left, mpack_tag_t right) {
                   ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:662:29: error: no previous prototype for ‘mpack_load_native_u8’ [-Werror=missing-prototypes]
 MPACK_ALWAYS_INLINE uint8_t mpack_load_native_u8(const char* p) {
                             ^
/home/jjacobs/repos/dinet/libdi/include/mpack/mpack.h:666:30: error: no previous prototype for ‘mpack_load_native_u16’ [-Werror=missing-prototypes]
 MPACK_ALWAYS_INLINE uint16_t mpack_load_native_u16(const char* p) {

etcetera...

mpack_write_cstr should assert in debug mode on NULL pointer

I see when a application passes a NULL const char * to mpack_write_cstr the mpack_strlen is directly mapped to stdio strlen (default) and will segfault. Maybe for debug mode an assert should be placed before entering the function?

size_t len = mpack_strlen(cstr);

I think the same would be valid for mpack_write_native_big here (where const char *p == NULL):

static void mpack_write_native_big(mpack_writer_t* writer, const char* p, size_t count) {

This will safe a lot of stack frames during debugging when we assert early in mpack :-)

Missing mpack_expect_ext_* functions

It appears that the Expect API is missing mpack_expect_ext_* functions to complement the binary data functions in the Expect API. In the Expect API documentation, under the Binary Data / Extension Functions section, only the mpack_expect_bin_* functions are listed. It is possible the equivalent extension functions already exist and are just not in the latest documentation (as linked from the README); however, I did a cursory review and search of the source code in master and develop branches and did not see any Expect API functions for the extension type. In other words, I quickly searched for mpack_expect_ext in the mpack-expect.c file and found nothing.

The mpack_read_tag function in combination with the mpack_done_ext function from the Core Reader API can be used to read an extension type, but for API completeness and parity with the binary data functions in the Expect API, maybe the following functions can be added?

uint32_t mpack_expect_ext(mpack_reader_t *reader, int8_t *type)
uint32_t mpack_expect_ext_max(mpack_reader_t *reader, int8_t *type, uint32_t maxsize)
void mpack_expect_ext_size(mpack_reader_t *reader, int8_t *type, uint32_t count)
mpack_expect_ext_buf(mpack_reader_t *reader, int8_t *type, char *buf, size_t size)
mpack_expect_ext_alloc(mpack_reader_t *reader, int8_t *type, size_t maxsize, size_t *size)

Code analysis

I just performed code analysis using PVS-Studio, and the application detected up to 24 warnings with various levels.

Here's a beautiful and convenient HTML report for you:
mpack_analysis.zip

mpack_reader_ API doesn't mirror mpack_writer_

Shouldn't the reader API mirror the writer? It's rather confusing to have used mpack_write_i64 or mpack_write_i32 and then on the receiving side have mpack_read_tag indicate that both are of type mpack_type_uint.

C preprocessor code-generator

Hi Nicholas,

I wanted to let you know how to abuse the C preprocessor to generate C-code to serialize C-structs into MessagePack. This uses the "X-Macros" concept using the CPP from https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros.

output:

name [string_t]: (null)
x [uint8_t]: 0
y [uint8_t]: 0
z [uint8_t]: 0
name [string_t]: Sirius
x [uint8_t]: 255
y [uint8_t]: 127
z [uint8_t]: 63
MessagePack: [84 a4 6e 61 6d 65 a6 53 69 72 69 75 73 a1 78 cc ff a1 79 7f a1 7a 3f ]
/*
http://msgpack-json-editor.com/
https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>

#include "mpack-config.h"
#include <mpack.h>

/*
 Generic
 */
#define STRUCT_MEMBER(member, type, dummy) type member;

#define SERIALIZE_KEY(writer, key) mpack_write_cstr(writer, #key);
#define SERIALIZE_VALUE(writer, value, type) SERIALIZE_##type(writer, value)
#define SERIALIZE_uint8_t(writer, value)  mpack_write_u8(writer, value)
#define SERIALIZE_string_t(writer, value) mpack_write_cstr(writer, value);

#define SERIALIZE_MEMBER(member, type, obj, writer) \
    SERIALIZE_KEY(writer, member); \
    SERIALIZE_VALUE(writer, obj->member, type);

#define DESERIALIZE_MEMBER(member, type, obj, buffer) \
  memcpy(&(obj->member), buffer, sizeof(obj->member)); \
  buffer += sizeof(obj->member);

#define FORMAT_(type) FORMAT_##type
#define FORMAT_int    "%d"
#define FORMAT_double "%g"
#define FORMAT_uint8_t "%" PRIu8
#define FORMAT_string_t "%s"

/* FORMAT_(type) will be replaced with FORMAT_int or FORMAT_double */
#define PRINT_MEMBER(member, type, obj) \
  printf("%s [%s]: " FORMAT_(type) "\n", #member, #type, obj->member);

typedef const char * string_t;

/*
 starStruct
 */

#define EXPAND_STAR(_, ...) \
  _(name, string_t, __VA_ARGS__) \
  _(x, uint8_t, __VA_ARGS__) \
  _(y, uint8_t, __VA_ARGS__) \
  _(z, uint8_t, __VA_ARGS__)

typedef struct {
  EXPAND_STAR(STRUCT_MEMBER, )
  } starStruct;

void serialize_star(const starStruct *const star, mpack_writer_t *writer) {
  mpack_start_map(writer, 4);
  EXPAND_STAR(SERIALIZE_MEMBER, star, writer)
  mpack_finish_map(writer);
  }

void deserialize_star(starStruct *const star, const unsigned char *buffer) {
  EXPAND_STAR(DESERIALIZE_MEMBER, star, buffer)
  }

void print_star(const starStruct *const star) {
  EXPAND_STAR(PRINT_MEMBER, star)
  }

int
main(void)
{
    char* data;
    size_t size;
    static mpack_writer_t writer;

    starStruct s;
    memset(&s, 0, sizeof(s));
    print_star(&s);

    s.name = "Sirius";
    s.x = 255;
    s.y = 127;
    s.z = 63;

    print_star(&s);

    // encode to memory buffer
    mpack_writer_init_growable(&writer, &data, &size);

    // write start to msgpack
    serialize_star(&s, &writer);

    // finish writing
    if (mpack_writer_destroy(&writer) != mpack_ok) {
        fprintf(stderr, "An error occurred encoding the data!\n");
        return -1;
    }

    printf("MessagePack: [");
    for(unsigned int j = 0; j < mpack_writer_buffer_used(&writer); j++)
        printf("%02x ", ((uint8_t *)data)[j]);
    printf("]\n");

    // use the data
    free(data);

    return 0;
}

This is just a example how to use the preprocessor to "map" single-depth C-structs onto MessagePack without writing any script at all. It is still a very crude way but for Embedded Firmware purposes it SHOULD work on every compiler. And deserialize is not yet covered using mpack.

Kind regards,
Jerry

Tests shadow previous local variable(s)

We are compiling with -Wshadow on GCC 4.8 (also reproducable with clang 3.5):

[ 49%] Building C object 3rdparty/mpack/test/CMakeFiles/mpack_test.dir/test-node.c.o
In file included from 3rdparty/mpack/test/test-node.c:22:0:
3rdparty/mpack/test/test-node.c: In function ‘test_node_read_possible’:
3rdparty/mpack/test/test-node.h:72:18: error: declaration of ‘tree’ shadows a previous local [-Werror=shadow]
     mpack_tree_t tree; \
                  ^
3rdparty/mpack/test/test-node.c:488:5: note: in expansion of macro ‘TEST_SIMPLE_TREE_READ_ERROR’
     TEST_SIMPLE_TREE_READ_ERROR("\xcc", (MPACK_UNUSED(node), true), mpack_error_invalid); // truncated u8
     ^
3rdparty/mpack/test/test-node.c:481:18: error: shadowed declaration is here [-Werror=shadow]
     mpack_tree_t tree;
                  ^
In file included from 3rdparty/mpack/test/test-node.c:22:0:
3rdparty/mpack/test/test-node.h:72:18: error: declaration of ‘tree’ shadows a previous local [-Werror=shadow]
     mpack_tree_t tree; \
                  ^
3rdparty/mpack/test/test-node.c:489:5: note: in expansion of macro ‘TEST_SIMPLE_TREE_READ_ERROR’
     TEST_SIMPLE_TREE_READ_ERROR("\xcd", (MPACK_UNUSED(node), true), mpack_error_invalid); // truncated u16
     ^
3rdparty/mpack/test/test-node.c:481:18: error: shadowed declaration is here [-Werror=shadow]
     mpack_tree_t tree;
                  ^
In file included from 3rdparty/mpack/test/test-node.c:22:0:
3rdparty/mpack/test/test-node.h:72:18: error: declaration of ‘tree’ shadows a previous local [-Werror=shadow]
     mpack_tree_t tree; \
                  ^
3rdparty/mpack/test/test-node.c:490:5: note: in expansion of macro ‘TEST_SIMPLE_TREE_READ_ERROR’
     TEST_SIMPLE_TREE_READ_ERROR("\xce", (MPACK_UNUSED(node), true), mpack_error_invalid); // truncated u32
     ^
3rdparty/mpack/test/test-node.c:481:18: error: shadowed declaration is here [-Werror=shadow]
     mpack_tree_t tree;
                  ^
In file included from 3rdparty/mpack/test/test-node.c:22:0:
3rdparty/mpack/test/test-node.h:72:18: error: declaration of ‘tree’ shadows a previous local [-Werror=shadow]
     mpack_tree_t tree; \
                  ^
3rdparty/mpack/test/test-node.c:491:5: note: in expansion of macro ‘TEST_SIMPLE_TREE_READ_ERROR’
     TEST_SIMPLE_TREE_READ_ERROR("\xcf", (MPACK_UNUSED(node), true), mpack_error_invalid); // truncated u64
     ^
3rdparty/mpack/test/test-node.c:481:18: error: shadowed declaration is here [-Werror=shadow]
     mpack_tree_t tree;
                  ^
cc1: all warnings being treated as errors
make[3]: *** [3rdparty/mpack/test/CMakeFiles/mpack_test.dir/test-node.c.o] Error 1
make[2]: *** [3rdparty/mpack/test/CMakeFiles/mpack_test.dir/all] Error 2
make[1]: *** [all] Error 2
make: *** [debug] Error 2

Build warnings

Hi,

I've integrated MPack v0.8 today, but I ran into some warnings during compilation.

mpack/test/test-file.c:25:13: error: no previous extern declaration for non-static variable 'lipsum' [-Werror,-Wmissing-variable-declarations]
const char* lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec justo purus. Nunc finibus dolor id lorem sagittis, euismod efficitur arcu aliquam. Nullam a ante eget mi porttitor dignissim vitae at libero. Maecenas in justo massa. Mauris ultricies leo nisl, at ullamcorper erat maximus sit amet. Quisque pharetra sed ligula nec tristique. Mauris consectetur sapien lacus, et pharetra turpis rhoncus a. Sed in eleifend eros. Donec in libero lacus. Sed et finibus ipsum. Etiam eros leo, mollis eget molestie quis, rhoncus ac magna. Donec dolor risus, bibendum et scelerisque at, faucibus in mi. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum convallis accumsan mollis.";
            ^
mpack/test/test-file.c:26:13: error: no previous extern declaration for non-static variable 'quick_brown_fox' [-Werror,-Wmissing-variable-declarations]
const char* quick_brown_fox = "The quick brown fox jumps over a lazy dog.";
            ^
2 errors generated.

Adding static to these two variables solves the problem. :)
Perhaps it is good to add -Wmissing-variable-declarations in travis.

Thanks,

Rik

On-the-fly deserialization/parsing

I'm new to serialization formats and I decided to start with MessagePack. I'd like to use it in an embedded project with limited resources of memory.

I'm going to deserialize/parse a long message, but I'd like to make it "on-the-fly", node by node.

I understood I have to use mpack_tree_parse() that parses all the message and creates a dynamically typed tree in memory. But I don't have the memory space for the full tree of the message.

Is it possible to parse the message node by node on-the-fly?

Error with mpack_writer_destroy when no data has been written

I have been working with the mpack library, and I have come across an error. It appears to be related to using the mpack_writer_destroy function when no other mpack_write_* functions have been used. In other words, an error occurs destroying a writer with no data. I understand this is an edge case because why would one create and then ultimately destroy a writer without using it? However, it feels like mpack should exit cleanly/safely as opposed to causing an abort under these conditions.

I have created a simplified example that reproduces the problem on my machine. I was not sure how to convert it to a test to include with the mpack test suite. Instead, I have included the example as an attachment, along with a screen capture of the error dialog that appears (Error-Dialog.png). Below is the source code for the main function that reproduces the problem, which is located in issue.c of the attached archive:

#include "mpack.h"

int 
main(int argc, char* argv[])
{
    char* data = NULL;
    size_t size = 0;
    mpack_writer_t* writer = malloc(sizeof(mpack_writer_t));
    if (!writer) {
        fprintf(stderr, "A memory-related error occurred!\n");
        return 1;
    }
    mpack_writer_init_growable(writer, &data, &size);
    if (mpack_writer_error(writer) != mpack_ok) {
        fprintf(stderr, "An initialization-related error occurred!\n");
        return 1;
    }
    //mpack_write_int(writer, 56); <-- Uncomment this line, i.e. write some data, to avoid the issue
    if (mpack_writer_destroy(writer) != mpack_ok) {
        fprintf(stderr, "A destruction-related error occurred!\n");
        return 1;
    }
    fprintf(stdout, "Data:");
    for (size_t i = 0; i < size; i++) {
        fprintf(stdout, " %X", data[i]);
    }
    fprintf(stdout, "\n");
    free(data);
    free(writer);
    return 0;
}

The error does not appear to occur on macOS High Sierra. It only occurs on Windows 10, where I am using the MSVC compiler included with Microsoft C++ Build Tools 2017. I have not tried it on Linux. I have created a CMakeLists.txt file to help with building the example and included it in the attached archive file. I am using mpack v0.8.2 with the default configuration (mpack-config.h) in the example (also included in the attached archive). The following instructions can be used to build the example demonstrating the issue:

  1. Download the attached archive.
  2. Extract the archive.
  3. Start the x64 Native Build Tools command prompt that should have been included with Microsoft C++ Build Tools 2017.
  4. Navigate to the extracted archive folder.
  5. Run the following commands to build and run the example:
mkdir build
cd build
cmake ..
cmake --build .
Debug\run_issue.exe

I believe I have narrowed down the problem to the mpack_growable_writer_teardown function and its use of the mpack_realloc function, but I am not sure on how to resolve the issue, or if this is even an error. Maybe it is just a configuration I need to change? It is very possible I have missed something.

issue.zip

error-dialog

Exposing native store functions into public API

Hi Nicholas,

I'm currently integrating mpack 0.7 and really like the mpack_store|load* functions to be used also elsewhere in my code. Only the mpack_store_native_*_at functions are note in the public header. This because I was smacked against the warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] when setting optimalisation to -Os for my firmware.

Is there any chance you can give them a place in the public reader API (header) ?

Undefined configuration and compiler definitions cause warnings

Hi Nicholas,

I was compiling Mpack for a non-Linux platform today (non-GNU) and ran into a big list of warnings (46 to be exact). Since we want to compile our code with -Werror we really want to fix it. But we need your opinion on this.

See some of our warnings below this. We are using the amalgamation package, so line-numbers are crazy, but you will get the point. The compiler moans about the check within #if while the variable is not defined.

In the example of the first warning:

#if MPACK_STDLIB

should become:

#if defined(MPACK_STDLIB) && MPACK_STDLIB

but this creates a mess in the code. From my point of view, you want to use:

#ifdef MPACK_STDLIB

or

#if defined(MPACK_STDLIB)

but this changes the behavior when configuring Mpack, only setting config to 0 wouldn't suffice. You need to disable the config option completely.

We are using GCC 4.9.3 Linaro arm-none-eabi cross-tools.

I can fix this and make a PR for you. But (since it is your lib) I need your input on this.

Thanks,

Rik

/3rdparty/mpack/src/mpack/mpack.h:78:5: warning: "MPACK_STDLIB" is not defined [-Wundef]
 #if MPACK_STDLIB
     ^
/3rdparty/mpack/src/mpack/mpack.h:82:5: warning: "MPACK_STDIO" is not defined [-Wundef]
 #if MPACK_STDIO
     ^
/3rdparty/mpack/src/mpack/mpack.h:85:5: warning: "MPACK_SETJMP" is not defined [-Wundef]
 #if MPACK_SETJMP
     ^
/3rdparty/mpack/src/mpack/mpack.h:113:7: warning: "_MSC_VER" is not defined [-Wundef]
 #elif _MSC_VER
       ^
/3rdparty/mpack/src/mpack/mpack.h:192:5: warning: "MPACK_STDLIB" is not defined [-Wundef]
 #if MPACK_STDLIB
     ^
/3rdparty/mpack/src/mpack/mpack.h:224:5: warning: "MPACK_READ_TRACKING" is not defined [-Wundef]
 #if MPACK_READ_TRACKING && (!defined(MPACK_READER) || !MPACK_READER)
     ^
/3rdparty/mpack/src/mpack/mpack.h:227:5: warning: "MPACK_WRITE_TRACKING" is not defined [-Wundef]
 #if MPACK_WRITE_TRACKING && (!defined(MPACK_WRITER) || !MPACK_WRITER)
     ^
/3rdparty/mpack/src/mpack/mpack.h:231:9: warning: "MPACK_STDIO" is not defined [-Wundef]
     #if MPACK_STDIO
         ^
/3rdparty/mpack/src/mpack/mpack.h:234:9: warning: "MPACK_READ_TRACKING" is not defined [-Wundef]
     #if MPACK_READ_TRACKING
         ^
/3rdparty/mpack/src/mpack/mpack.h:237:9: warning: "MPACK_WRITE_TRACKING" is not defined [-Wundef]
     #if MPACK_WRITE_TRACKING
         ^
/3rdparty/mpack/src/mpack/mpack.h:559:5: warning: "MPACK_READ_TRACKING" is not defined [-Wundef]
 #if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
     ^
/3rdparty/mpack/src/mpack/mpack.h:559:28: warning: "MPACK_WRITE_TRACKING" is not defined [-Wundef]
 #if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
                            ^
/3rdparty/mpack/src/mpack/mpack.h:780:5: warning: "MPACK_WRITE_TRACKING" is not defined [-Wundef]
 #if MPACK_WRITE_TRACKING
     ^
/3rdparty/mpack/src/mpack/mpack.h:827:9: warning: "MPACK_SETJMP" is not defined [-Wundef]
     #if MPACK_SETJMP
         ^
/3rdparty/mpack/src/mpack/mpack.h:833:9: warning: "MPACK_WRITE_TRACKING" is not defined [-Wundef]
     #if MPACK_WRITE_TRACKING
         ^
/3rdparty/mpack/src/mpack/mpack.h:882:5: warning: "MPACK_STDIO" is not defined [-Wundef]
 #if MPACK_STDIO
     ^
/3rdparty/mpack/src/mpack/mpack.h:906:5: warning: "MPACK_SETJMP" is not defined [-Wundef]
 #if MPACK_SETJMP
     ^
/3rdparty/mpack/src/mpack/mpack.h:1203:5: warning: "MPACK_WRITE_TRACKING" is not defined [-Wundef]
 #if MPACK_WRITE_TRACKING
     ^
/3rdparty/mpack/src/mpack/mpack.h:1298:5: warning: "MPACK_READ_TRACKING" is not defined [-Wundef]
 #if MPACK_READ_TRACKING
     ^
/3rdparty/mpack/src/mpack/mpack.h:1351:9: warning: "MPACK_SETJMP" is not defined [-Wundef]
     #if MPACK_SETJMP

Question: Should I use the Expect API for storing filesystem metadata?

I've been looking for a serialization format for the filesystem I'm developing. MPack seems to have the features I'm looking for (small binary format, zero-copy, decent documentation, good speed), but I'm not quite sure on what the difference is between the Expect API and the Node API. It seems like I would want to use the Expect API as I'm dealing with filesystem meta-data which has a fixed schema, and the imperative parser is supposed to be zero-copy I believe. Is my understanding correct?

Make it possible to compile MPack on 8-bit embedded targets?

Currently, compiling mpack for some 8-bit embedded targets (such as Arduino AVR microcontrollers) fails because doubles are just 32-bit floats and not proper 64-bit floating-point numbers:

MPACK_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t), "double is wrong size??");

If possible, it would be great to make MPack compilable for such targets, even if reading/writing doubles must be unsupported on such targets - for example by making a flag in mpack-defaults.h to omit the mpack_load_double, mpack_store_double, and mpack_encode_double functions, and to make mpack_tree_parse_node_contents and mpack_parse_tag return false instead of attempting to read doubles on when type 0xcb (double) is encountered. I've started to try out this idea in a fork I made, and I'll report back on whether I find obvious differences in a few basic use cases between a few 8-bit AVR microcontrollers and a few 32-bit ARM microcontrollers. Caveat is that I've never used mpack before, so maybe my understanding has some crucial gaps about why this wouldn't work or about what is the correct way to disable double reading/writing functionality.

Perhaps more graceful degradation of functionality on AVR-like targets would be to convert 32-bit doubles to and from the 64-bit representations stored in the unions in mpack_load_double, mpack_store_double. I'm not sure if it's possible to implement this in a portable way - the closest I've found is somebody's function to read a 32-bit float from a buffer containing a 64-bit floating point representation at https://www.microchip.com/forums/FindPost/258940, and the ArduinoJson library's msgpack deserializer implementation (readDouble and doubleToFloat).

Scan-build warnings

Hi Nicholas,

Nice to see Mpack getting better every release. Also the improved code-coverage has a great value for us.

I'm facing some issues with clang-analyzer (scan-build). But all seem to come from the tests. Perhaps you can fix them? Probably it is a good idea to bail out in your test when a null-pointer otherwise get deferenced later on.

Here the screenshots:

mpack1
mpack2
mpack3
mpack4
mpack5
mpack6

Lowlevel pruning with reader

Currently I extract a key : to feed to a other piece of the API decoder and want to calculate the size in bytes of the map and the begin address. Here a example:

{ 
   "somekey" : 1,
   "anotherkey" : "value",
   "interestedkey" : {
       "subkey" : "awesomevalue",
       "subkey1" : true
    },
    "foo" : "bar"
}

I itterate over the parent map and search for "interestedkey", then I pass the reader context to a function which calculates const char *buffer, size_t size for a new reader.

Currently I do in the extraction function:

static void di_rpc_deserialize_msg_params(mpack_reader_t *reader, struct di_rpc_msg *msg)
{
        const size_t keys = mpack_expect_map(reader);

        if (mpack_reader_error(reader) != mpack_ok) {
                msg->params.begin = NULL;
                msg->params.end   = NULL;
                return;
        }

        msg->params.begin = &reader->buffer[reader->pos - 1];

        for (size_t n = 0; n < keys; n++) {
                mpack_discard(reader);
                mpack_discard(reader);
        }

        msg->params.end = &reader->buffer[reader->pos];

        mpack_done_map(reader);
}

Only it feels very inefficient to itterate over all key-values and discard them. Is it valid to just rewind the reader context pos one byte (so it points to the map byte) and then discard from this point?

Like this:


static void di_rpc_deserialize_msg_params(mpack_reader_t *reader, struct di_rpc_msg *msg)
{
        mpack_expect_map(reader);

        if (mpack_reader_error(reader) != mpack_ok) {
                msg->params.buffer = NULL;
                msg->params.size   = 0;
                return;
        }

        reader->pos -= 1;
        msg->params.buffer = &reader->buffer[reader->pos];
        mpack_discard(reader);
        msg->params.size = (size_t)((const char *)&reader->buffer[reader->pos] - msg->params.buffer);
        mpack_done_map(reader);
}

Maybe lowlevel pruning would be nice to have in the reader API? This is especially useful in systems where you want to grab a chunk of mpack data and pass to another layer.

Integration of optional C11 write and read

As we talked before, I would like to contribute an optional extension to mpack to have primitive values (messagepack) easily serialized and deserialized with the C11 _Generic selection. The compiler can then select the according function at compile time instead of the programmer itself.
This could add to following functions to the API (defines):

mpack_write(mpack_writer_t writer, <generic selected type, uint8_t, uint16_t etcetera>);
mpack_read(mpack_writer_t reader, <generic selected type, uint8_t, uint16_t etcetera>);

Let me know what you think then I will start integration of current code as pull request. If we can not easily detect this C11 feature we could make it explict enable/disable this feature with a mpack-config.h define e.g: MPACK_C11_EXTENSION.

Static analysis malloc call with size 0

Our code with continues integration has scan-build (clang-analyzer) and found with v0.6 the following bug:

Unix API Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131) /3rdparty/mpack/src/mpack/mpack.c 3 Call to 'malloc' has an allocation size of 0 bytes

2358    #ifdef MPACK_MALLOC
2359    void* mpack_expect_array_alloc_impl(mpack_reader_t* reader, size_t element_size, uint32_t max_count, size_t* out_count) {
2360        size_t count = *out_count = mpack_expect_array(reader);
2361        if (mpack_reader_error(reader))
1
Taking false branch 
→
2362            return NULL;
2363        if (count > max_count) {
2
←
Taking false branch 
→
2364            mpack_reader_flag_error(reader, mpack_error_type);
2365            return NULL;
2366        }
2367        void* p = MPACK_MALLOC(element_size * count);
3
←
Within the expansion of the macro 'MPACK_MALLOC':
a
Call to 'malloc' has an allocation size of 0 bytes

2368        if (p == NULL)
2369            mpack_reader_flag_error(reader, mpack_error_memory);
2370        return p;
2371    }
2372    #endif

mpack_write_native could be public when tracking mode is enabled

Currently I'm implementing a serializer where a messagepack msg is crafted in two pieces.

  • I write first an map into a buffer, e.g error object
  • I write message header into a second buffer

Then I want to write the error object buffer into the message header second buffer:

error object (json presentation):

{ 
   "descr" : "This is a really bad error",
   "code" : 1337
}

header + error object buffer:

{
   "version" : 1
   "reply" : "foobar"
   "error" :  < data of error object buffer > 
}

I first tried using mpack_write_bytes and this hit a breakpoint because for debug builds I have tracking enabled. So basicly I need a "untracked" byte writer where bounds are checked when I'm still building with tracking enabled.

Feel free to ask if something is not clear.

Compilation with -Wconversion

Hi Nicholas,

In our project we are looking into -Wconversion. But we found some warnings (and in our case errors with -Werror). I see that you explicitly deactivated -Wno-sign-conversion, which surpresses exactly the warnings I found.

Is there a reason to just ignore these warnings? Perhaps casting makes it more portable between build-environments.

include/mpack/mpack.h: In function ‘mpack_tag_array’:
include/mpack/mpack.h:1006:15: error: conversion to ‘uint32_t’ from ‘int32_t’ may change the sign of the result [-Werror=sign-conversion]
     ret.v.n = count;
               ^
include/mpack/mpack.h: In function ‘mpack_tag_map’:
include/mpack/mpack.h:1015:15: error: conversion to ‘uint32_t’ from ‘int32_t’ may change the sign of the result [-Werror=sign-conversion]
     ret.v.n = count;
               ^
include/mpack/mpack.h: In function ‘mpack_tag_str’:
include/mpack/mpack.h:1024:15: error: conversion to ‘uint32_t’ from ‘int32_t’ may change the sign of the result [-Werror=sign-conversion]
     ret.v.l = length;
               ^
include/mpack/mpack.h: In function ‘mpack_tag_bin’:
include/mpack/mpack.h:1033:15: error: conversion to ‘uint32_t’ from ‘int32_t’ may change the sign of the result [-Werror=sign-conversion]
     ret.v.l = length;
               ^
include/mpack/mpack.h: In function ‘mpack_tag_ext’:
include/mpack/mpack.h:1043:15: error: conversion to ‘uint32_t’ from ‘int32_t’ may change the sign of the result [-Werror=sign-conversion]
     ret.v.l = length;
               ^
cc1: all warnings being treated as errors

Kind regards,

Rik

Compile error with specific config

When mpack.h included from .cpp,
defining #define MPACK_WRITER 0 in config causing compile error.

src\mpack.h:3253:31: error: variable or field 'mpack_write' declared void
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int8_t value) {
                               ^~~~~~~~~~~~~~
src\mpack.h:3253:31: error: 'mpack_writer_t' was not declared in this scope
src\mpack.h:3253:31: note: suggested alternative: 'mpack_error_t'
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int8_t value) {
                               ^~~~~~~~~~~~~~
                               mpack_error_t
src\mpack.h:3253:47: error: 'writer' was not declared in this scope
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int8_t value) {
                                               ^~~~~~
src\mpack.h:3253:47: note: suggested alternative: 'fwrite'
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int8_t value) {
                                               ^~~~~~
                                               fwrite
src\mpack.h:3253:62: error: expected primary-expression before 'value'
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int8_t value) {
                                                              ^~~~~
src\mpack.h:3257:31: error: variable or field 'mpack_write' declared void
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int16_t value) {
                               ^~~~~~~~~~~~~~
src\mpack.h:3257:31: error: 'mpack_writer_t' was not declared in this scope
src\mpack.h:3257:31: note: suggested alternative: 'mpack_error_t'
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int16_t value) {
                               ^~~~~~~~~~~~~~
                               mpack_error_t
src\mpack.h:3257:47: error: 'writer' was not declared in this scope
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int16_t value) {
                                               ^~~~~~
src\mpack.h:3257:47: note: suggested alternative: 'fwrite'
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int16_t value) {
                                               ^~~~~~
                                               fwrite
src\mpack.h:3257:63: error: expected primary-expression before 'value'
 MPACK_INLINE void mpack_write(mpack_writer_t* writer, int16_t value) {
                                                               ^~~~~
...
...

possible fix:

in mpack.h(v1.0 amalgamated) line 3239
from

#if defined(__cplusplus) || defined(MPACK_DOXYGEN)

to

#if (MPACK_WRITER && defined(__cplusplus)) || defined(MPACK_DOXYGEN)

Remove setjmp/longjmp

Anybody using the setjmp/longjmp support in MPack? I'm going to remove it in 0.6 and replace it with an error callback and context.

The reason I'm removing it is because it's not safe to use in the straightforward way you would expect. The docs encourage putting a reader/writer/tree on the stack, but the contents of non-volatile stack variables that have changed after setjmp are undefined after the longjmp. To use it safely you'd have to declare the reader as volatile, or allocate it off the stack (and not change the pointer to it obviously.) Libraries like libpng that provide longjmp support wrap their structures so that they are allocated off the stack which is why they can offer this support safely.

I'll be retaining the non-local jump safety across MPack though, so from any error handler you'll still be able to longjmp or throw through MPack. An error callback will also make it easier to create C++ bindings which will have the option of throwing on error.

Integration release v0.7 missing test_assert

I'm integrating the new v0.7 release into our CMake project which used previously v0.6 and when compiling it is missing the test_assert macro/function. I tried to search the repository but it is nowhere defined? I think there is also something wrong with header dependencies using the test-system.h

Scanning dependencies of target mpack_test
[  8%] Building C object test/CMakeFiles/mpack_test.dir/__/src/mpack/mpack.c.o
[ 16%] Building C object test/CMakeFiles/mpack_test.dir/test.c.o
[ 25%] Building C object test/CMakeFiles/mpack_test.dir/test-buffer.c.o
[ 33%] Building C object test/CMakeFiles/mpack_test.dir/test-common.c.o
[ 41%] Building C object test/CMakeFiles/mpack_test.dir/test-expect.c.o
[ 50%] Building C object test/CMakeFiles/mpack_test.dir/test-file.c.o
[ 58%] Building C object test/CMakeFiles/mpack_test.dir/test-malloc.c.o
/home/jjacobs/repos/dinet/libdi/3rdparty/mpack/test/test-malloc.c: In function ‘test_free’:
/home/jjacobs/repos/dinet/libdi/3rdparty/mpack/test/test-malloc.c:52:5: error: implicit declaration of function ‘test_assert’ [-Werror=implicit-function-declaration]
     test_assert(p != NULL, "attempting to free NULL");
     ^
cc1: all warnings being treated as errors
make[2]: *** [test/CMakeFiles/mpack_test.dir/test-malloc.c.o] Error 1
make[1]: *** [test/CMakeFiles/mpack_test.dir/all] Error 2
make: *** [all] Error 2

mpack compilation error on CentOS 6

mpack is a great library and we started the first integration here:

fluent/fluent-bit@3886c22

However, we got some build problems on a certain compiler version (gcc on centos 6):

/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c: In function ‘mpack_str_check_no_null’:
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:847: error: ‘for’ loop initial declarations are only allowed in C99 mode
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:847: note: use option -std=c99 or -std=gnu99 to compile your code
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c: In function ‘mpack_expect_enum’:
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:4017: error: ‘for’ loop initial declarations are only allowed in C99 mode
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c: In function ‘mpack_expect_enum_optional’:
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:4050: error: ‘for’ loop initial declarations are only allowed in C99 mode
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c: In function ‘mpack_node_map_int_impl’:
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:5827: error: ‘for’ loop initial declarations are only allowed in C99 mode
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c: In function ‘mpack_node_map_uint_impl’:
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:5858: error: ‘for’ loop initial declarations are only allowed in C99 mode
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c: In function ‘mpack_node_map_str_impl’:
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:5892: error: ‘for’ loop initial declarations are only allowed in C99 mode
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c: In function ‘mpack_node_enum_optional’:
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.c:5994: error: ‘for’ loop initial declarations are only allowed in C99 mode

this is not a new report, indeed there are previous issues reported on mpack repo:

we can get rid of the problem defining CFLAGS with -std=c99, anyways I see other warnings remain:

/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:1218: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:1240: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:2274: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:2292: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:3237: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:3388: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:4314: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:4332: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:5734: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:5753: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/root/fluent-bit/lib/mpack-amalgamation-1.0/src/mpack/mpack.h:7145: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’

what would be the suggested flags to get rid of warnings ? or can you set the variables on top of the functions ?, looks like a small change that will help to build it on old systems.

scan-build warning with mpack_test

[ 58%] Building C object 3rdparty/mpack/test/CMakeFiles/mpack_test.dir/__/src/mpack/mpack.c.o
../3rdparty/mpack/src/mpack/mpack.c:181:21: warning: Call to 'malloc' has an allocation size of 0 bytes
void* new_ptr = malloc(new_size);
^~~~~~~~~~~~~~~~
1 warning generated.

It is also in the travis output.
https://travis-ci.org/ludocode/mpack/jobs/77627638#L161

The odd thing is, it doesn't generate no report on this.

Safe deserialisation with cstr key

Currently i'm using the lowlevel reader API to deserialize key-values and make decisions based on that. I have a memory contrained system and reusing kv char buffers (for mpack_type_str). The current limitation of the mpack_read API is there is no mpack_read_cstr function which does prober NULL termination and bound checking. I'm not able to use the expect API because I have already read the tag from the stream. The expect API only works when no tag is current in the reader state.

I think the solution would be to move the read part of the mpack_expect_cstr into mpack reader API.

Here is a snippet:


void di_rpc_deserialize_msg(mpack_reader_t *reader, struct di_rpc_msg *msg)
{
        char k[64];
        char v[64];
        ....

        /* Loop over all keys */
        keys = mpack_expect_map(reader);
        for (size_t n = 0; n < keys; n++) {
                /* Reset read kv */
                k[0] = 0;
                v[0] = 0;

                /* Key */
                mpack_expect_cstr(reader, k, sizeof(k));

                /* Value */
                t = mpack_read_tag(reader);
                if (t.type == mpack_type_str) {
                        /* It would be much better to have mpack_read_cstr here ... */
                        mpack_read_bytes(reader, v, sizeof(v));
                        v[t.v.l] = 0;

Memory leak on mpack_writer_destroy

ASAN reports 1 byte direct leak in mpack_writer_destroy

==8973==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1 byte(s) in 1 object(s) allocated from:
    #0 0x55af2c7e5549 in realloc (/home/mark/CLionProjects/z/cmake-build-debug/z+0xeb549)
    #1 0x55af2c82efd8 in mpack_realloc (/home/mark/CLionProjects/z/cmake-build-debug/z+0x134fd8)
    #2 0x55af2c8326f7 in mpack_growable_writer_teardown (/home/mark/CLionProjects/z/cmake-build-debug/z+0x1386f7)
    #3 0x55af2c832e5a in mpack_writer_destroy (/home/mark/CLionProjects/z/cmake-build-debug/z+0x138e5a)
    #4 0x55af2c82397d in z_net_on_read /home/mark/CLionProjects/z/src/net/net_server.c:164:12
    #5 0x7f65fcb54f92  (/usr/lib/libuv.so.1+0x17f92)

Snippet of code where this error occurs: https://paste.wut.ee/ukasoqaric.c

Non-negative integers are always written as unsigned

In a project I'm writing in sometimes negative and sometimes non-negative integers. The current implementation always writes a non-negative integer as an unsigned integer. The consequence of this is cumbersome decoding logic: try to decode the value as an uint and if that fails decode it as an int.

As far as I can see is the spec not explicit about changing the type of a signed into an unsigned integer. However, the spec states indeed that the smallest representation should be used whenever that's possible. When I interpret the spec, I consider signed and unsigned integers as separate non-interchangeable types. So for me an non-negative integer can't be 'optimized' into an unsigned integer that maybe is smaller to represent.

I look forward to your reaction. Maybe it can be an header #define in mpack-config.h?

Considered adding LZ4 compression ?

there's a strong implement in C# with LZ4 compression (MessagePack-CSharp) performance and powerful.
I noticed that MPack is the best in C ++ implementation. Also very performance and powerful.
Is the author interested in adding support for LZ4 compression?

New release merge from develop

Hi @ludocode,

Are there any plans to have a official release tagged and amalgamated? Last release was 3 months ago and some things are more awesome then before :+).
I'm not sure but maybe you need to bump the minor version number because the api has been extended.

I'm not sure you want to get some new features or fixes into the new release, that's why i'm asking. I tracked your public activity and saw you where very busy with the msgpack-tools.

Kind regards,
Jerry

C89/99

Hey there!

Currently your code fails to compile when c_std=gnu89. However, the only C99 feature that you appear to use is the ability to declare variables inside for loops. Would you be interested in a PR that moves the variable declarations out of the for() loops, and thus makes your code compaticle with gnu89?

Cheers!

Edit: It was only a little work, so I just went ahead and submitted the PR: #68

Possible encoding problems with differing double/int64 endianness

Hi Nicholas,

I'm working on an ARM platform(little-endian) with your library. I was using doubles and floats, but this probably also counts for other types in mpack. The items in the the packet are endian swapped and not sent big-endian as described in the spec.
https://github.com/msgpack/msgpack/blob/master/spec.md#formats-float

When I send them to a server (i.e. x86), the values are also wrong. Using a union for mapping to uint64 and double is valid on big-endian systems, but invalid on little-endian.

static inline void mpack_write_native_double(mpack_writer_t* writer, double value) {
union {
double d;
uint64_t i;
} u;
u.d = value;
mpack_write_native_u64(writer, u.i);
}

Perhaps it is a good idea to check for little-endian and swap the bytes when necessary?

Reader documentation?

There is no documentation/example on using the reader/expect API. Only the Reader Node API.

Could this be added please?

Function interfaces of buffers

I would like to suggest all functions/buffers should have the following prototypes:
writeable buffers: void *buffer or void *data
read only buffers: const void *buffer or const void *data interface instead of char *. This maps directly to POSIX (like) APIs which directly use buffers. Because the size is given the void * size is known.

Maybe keep in mind constant pointers and pointers to constants:
http://www.codeguru.com/cpp/cpp/cpp_mfc/general/article.php/c6967/Constant-Pointers-and-Pointers-to-Constants.htm

Let me know what you think.

Clang 3.7 static analyzer (scan-build): MPACK_REALLOC has an allocation size of 0 bytes

#define MPACK_VERSION_MAJOR 0  /**< The major version number of MPack. */
#define MPACK_VERSION_MINOR 8  /**< The minor version number of MPack. */
#define MPACK_VERSION_PATCH 1  /**< The patch version number of MPack. */
[ 40%] Building C object CMakeFiles/mpack/src/mpack/mpack.c.o
In file included from mpack/src/mpack/mpack.c:35:
mpack/src/mpack/mpack.h:678:20: warning: Call to 'realloc' has an allocation size of 0 bytes
            return MPACK_REALLOC(old_ptr, new_size);
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mpack-config.h:67:23: note: expanded from macro 'MPACK_REALLOC'
#define MPACK_REALLOC realloc

Our mpack-config.h looks like this if it is needed:


/**
 * 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.
 */

#ifndef MPACK_CONFIG_H
#define MPACK_CONFIG_H 1


/*
 * Features
 */

/** Enables compilation of the base Tag Reader. */
#define MPACK_READER 1

/** Enables compilation of the static Expect API. */
#define MPACK_EXPECT 1

/** Enables compilation of the dynamic Node API. */
#define MPACK_NODE 1

/** Enables compilation of the Writer. */
#define MPACK_WRITER 1


/*
 * Dependencies
 */

/**
 * Enables the use of C stdlib. This allows the library to use malloc
 * for debugging and in allocation helpers.
 */
#define MPACK_STDLIB 1

/**
 * Enables the use of C stdio. This adds helpers for easily
 * reading/writing C files and makes debugging easier.
 */
#define MPACK_STDIO 1

/**
 * \def MPACK_MALLOC
 *
 * Defines the memory allocation function used by mpack. This is used by
 * helpers for automatically allocating data the correct size, and for
 * debugging functions. If this macro is undefined, the allocation helpers
 * will not be compiled.
 *
 * A memory allocator is required for the Node API.
 */
/**
 * \def MPACK_REALLOC
 *
 * Defines the realloc function used by mpack. It is used by growable buffers
 * to resize more quickly.
 *
 * This is optional, even when MPACK_MALLOC is used. If MPACK_MALLOC is
 * set and MPACK_REALLOC is not, MPACK_MALLOC is used with a simple copy
 * to grow buffers.
 */
#if defined(MPACK_STDLIB) && !defined(MPACK_MALLOC)
#define MPACK_MALLOC malloc
#define MPACK_REALLOC realloc
#endif

/**
 * \def MPACK_FREE
 *
 * Defines the memory free function used by mpack. This is used by helpers
 * for automatically allocating data the correct size. If this macro is
 * undefined, the allocation helpers will not be compiled.
 *
 * A memory allocator is required for the Node API.
 */
#if defined(MPACK_STDLIB) && !defined(MPACK_FREE)
#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
 */

/**
 * \def MPACK_DEBUG
 *
 * Enables debug features. You may want to wrap this around your
 * own debug preprocs. By default, they are enabled if DEBUG or _DEBUG
 * are defined.
 *
 * Note that MPACK_DEBUG cannot be defined differently for different
 * source files because it affects layout of structs defined in header
 * 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)
//#define MPACK_DEBUG 1
//#else
#define MPACK_DEBUG 1
//#endif

/**
 * Set this to 1 to implement a custom mpack_assert_fail() function. This
 * function must not return, and must have the following signature:
 *
 *     void mpack_assert_fail(const char* message)
 *
 * 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.
 */
#define MPACK_CUSTOM_ASSERT 0

#if MPACK_DEBUG && MPACK_READER && defined(MPACK_MALLOC)
/**
 * \def MPACK_READ_TRACKING
 *
 * Enables compound type size tracking for readers. This ensures that the
 * correct number of elements or bytes are read from a compound type.
 *
 * This is enabled by default in debug builds (provided a malloc() is
 * available.)
 */
#define MPACK_READ_TRACKING 1
#endif

#if MPACK_DEBUG && MPACK_WRITER && defined(MPACK_MALLOC)
/**
 * \def MPACK_WRITE_TRACKING
 *
 * Enables compound type size tracking for writers. This ensures that the
 * correct number of elements or bytes are written in a compound type.
 *
 * Note that without write tracking enabled, it is possible for buggy code
 * to emit invalid MessagePack without flagging an error by writing the wrong
 * number of elements or bytes in a compound type. With tracking enabled,
 * MPACK will catch such errors and break on the offending line of code.
 *
 * This is enabled by default in debug builds (provided a malloc() is
 * available.)
 */
#define MPACK_WRITE_TRACKING 1
#endif


/*
 * Miscellaneous
 */

/**
 * Stack space to use when initializing a reader or writer with a
 * stack-allocated buffer.
 */
#define MPACK_STACK_SIZE 4096

/**
 * Buffer size to use for allocated buffers (such as for a file writer.)
 */
#define MPACK_BUFFER_SIZE 65536

/**
 * Number of nodes in each allocated node page.
 *
 * Nodes are 16 bytes when compiled for a 32-bit architecture and
 * 24 bytes when compiled for a 64-bit architecture.
 *
 * Using as many nodes fit in one memory page seems to provide the
 * best performance, and has very little waste when parsing small
 * messages.
 */
#define MPACK_NODE_PAGE_SIZE (4096 / sizeof(mpack_node_t))

/**
 * 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.
 */
#define MPACK_NODE_INITIAL_DEPTH 8

/**
 * The maximum depth for the node parser if MPACK_MALLOC is not available.
 * The parsing stack is placed on the call stack.
 */
#define MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC 32


#endif

question : open existing tree and modify values

I'm trying to open an existing msgpack buffer and edit a specific field in the map , what is the best way to do this?

looks like mpack_tree_init & mpack_writer_init are two distinct concept

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

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

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.