Open-source project
ludocode/mpack avatar
ludocode/mpack

MPack: a MessagePack encoder and decoder small enough to paste into your project

MPack - A C encoder/decoder for the MessagePack serialization format / msgpack.org[C]

637 stars102 forksCMIT

At a glance

What is it?
MPack is a C implementation of the MessagePack serialization format that arrives as two source files, mpack.h and mpack.c, with a tree parser, a writer that can target a fixed buffer or a growable one, and compile-time switches for trimming the feature set. Its selling point is that it is small enough to embed rather than link.
Who is it for?
MPack suits a C or C++ project that speaks MessagePack on a small embedded target, inside a Linux kernel module, or on an 8-bit microcontroller, and that would rather vendor two files than take on a dependency.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 76 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two files instead of a dependency

The introduction lists five properties, and the fourth of them is the one that changes how you adopt the library. The code is small enough to be embedded directly into your codebase: you download the amalgamation package from the releases page and add mpack.h and mpack.c to your project. The other four claims are simple and easy to check. It is simple and easy to use, secure against untrusted data, lightweight and suitable for embedded, extensively documented at ludocode.github.io/mpack/, and extremely fast according to a linked benchmark suite. The portability list is broader than most C libraries manage: all modern compilers, all desktop and smartphone operating systems, WebAssembly, inside the Linux kernel via a separate mpack-linux-kernel project, and even 8-bit microcontrollers such as Arduino. The amalgamated distribution is what makes the Linux kernel case possible, since a kernel module cannot carry a build system or a package manager. The default branch is develop rather than master, and the licence is MIT, with an AUTHORS.md and a CHANGELOG.md at the repository root.

The tree API trades memory for a simple interface

The Node API parses a chunk of MessagePack data into an immutable tree of dynamically typed nodes, and a series of helper functions extract data of specific types from each node. The README's example reads the data from the msgpack homepage:

C
// 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;
}

The error handling model is unusual enough to be worth understanding. The README notes 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 or zero values are returned and the tree is placed in an error state, so an error check is only needed before using the data. That is a deliberate design, and it is what makes the tree API pleasant to use, but it is also a trap if you forget to check mpack_tree_destroy, because the failure surfaces later as a wrong value rather than as a parse error.

The writer either owns its buffer or does not

The Write API encodes structured data to MessagePack, and the example shows the growable path:

C
// 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);

The alternative is a writer that targets a pre-allocated or stack-allocated buffer, with sizes supplied up front for compound types, which avoids memory allocation entirely, and the writer can also be given a flush function such as a file or socket write to call when the buffer fills or writing finishes. The error model here is stateful in the same way: if any error occurs the writer is placed in an error state, and subsequent writes are ignored, so you do not need to check every write. There is a second, stricter option for compound types. mpack_build_map lets the library count the key-value pairs automatically, while mpack_start_map with a known count makes the matching mpack_finish_map assert in debug mode that the expected number of elements were written, which the README notes other MessagePack C and C++ libraries may not do.

Compile-time trimming and the Expect API

Two mechanisms let you trade features for footprint, and they suit different situations. The first is compile-time configuration of which features, components and debug checks are compiled, and what dependencies are available, so the library can be cut down for a constrained target. The second is API choice at runtime. The tree API allocates nodes automatically, and 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 Expect API is the one worth understanding before committing, because it inverts the usual contract: instead of asking a generic tree for a value and checking whether it is the right type, you declare the schema and the parser validates as it goes. That removes the dynamic typing and the tree entirely, and with it the nil-and-zero-on-error behaviour that makes the tree API convenient.

What the comparison table actually claims

The README includes a short feature table comparing MPack against msgpack-c, CMP and CWPack, and it is a document worth reading closely rather than skimming. The rows are: no libc requirement, where MPack, CMP and CWPack are marked and msgpack-c is not; growable memory writer, where MPack, msgpack-c and CWPack are marked, with CWPack marked with an asterisk; file I/O helpers, marked for MPack, msgpack-c and CWPack with an asterisk; stateful error handling, marked for MPack and CMP; incremental parser, marked for MPack, CMP and CWPack; tree stream parser, marked for MPack and msgpack-c; compound size tracking, marked for MPack only; and automatic compound size, also marked for MPack only. So the differentiators against the field are stateful error handling, the tree parser combined with the incremental parser, and compound size tracking with automatic compound size. A larger feature comparison table with descriptions of each entry is at docs/features.md. The performance claim is a separate matter and points at a schemaless-benchmarks suite comparing implementations of schemaless formats, where MPack outperforms all JSON and MessagePack libraries except CWPack, and in some tests is several times faster than RapidJSON for equivalent data.

Why not just use JSON

The README spends a section answering the question every newcomer asks, and the argument is about efficiency rather than expressiveness. Conceptually, MessagePack stores data similarly to JSON, since both are composed of simple values such as numbers and strings stored hierarchically in maps and arrays, but JSON is designed to be human-readable, so it is not as efficient as a binary serialization format. Two consequences follow. Compound types such as strings, maps and arrays are delimited, so appropriate storage cannot be allocated upfront and the whole object must be parsed to determine its size. And strings are not stored in their native encoding. The rest of that section is cut off in the version of the README available here, so the full argument about string encoding is not something this description can state, but the shape of it is that the binary format skips the parse-to-size step and the escaping layer, which is exactly the work a binary format exists to avoid.

Where MPack is the wrong choice

The limits follow from the same properties that make it attractive. The tree API is dynamically typed, which means a schema mistake in your producer surfaces as a nil node and a false or zero value in your consumer unless you check the error state, and that is a class of bug a strongly typed stack such as a code-generated decoder would not have. The Expect API fixes the validation but only against a schema you write, and it is a more laborious API to use for ad hoc data. Second, the compile-time trimming and the two-file vendoring model mean you own the code after you copy it, and with the newest release being v1.1.1 from 2023-01-09, you are vendoring code that is not on a release cadence. Third, if you need MessagePack with a maintained upstream, a schema registry or cross-language guarantees about integer widths and extension types, the reference C implementation from the MessagePack project is the safer base, and the difference in approach is that MPack optimises for a small embeddable footprint while the reference implementation optimises for coverage and conformance.

Editorial conclusion

MPack suits a C or C++ project that speaks MessagePack on a small embedded target, inside a Linux kernel module, or on an 8-bit microcontroller, and that would rather vendor two files than take on a dependency. It is the wrong tool for a project that needs schema-driven validation of complex nested structures, where the Expect API is more work than the tree API, and it is the wrong tool for a team that wants a maintained upstream reference implementation, since the newest release is v1.1.1 from 2023-01-09 while the last push was on 2026-08-08. Verify first which features you actually need, since the feature set can be customized at compile time, and read the feature table in docs/features.md before you assume a capability the default build does not give you.

Frequently asked questions

How do I add MPack to my project?

The README says the code is small enough to be embedded directly: download the amalgamation package from the releases page and add mpack.h and mpack.c to your project. There is no package manager step and no build dependency to manage.

Does MPack handle errors automatically?

Yes, in a specific way. The README says no additional error handling is needed in the examples: a missing or corrupt file, missing map keys or unexpected node types produce special nil nodes and false or zero values, and the tree or writer is placed in an error state, so an error check is needed only before using the data.

What is the Expect API for?

For parsing data of a predefined schema. The README says that for maximum performance and minimal memory usage the Expect API can be used, while the tree API allocates nodes automatically and can instead be given a fixed node pool in memory-constrained environments.

Which platforms does MPack support?

The README states it supports all modern compilers, all desktop and smartphone operating systems, WebAssembly, use inside the Linux kernel through the mpack-linux-kernel project, and 8-bit microcontrollers such as Arduino.

What does MPack offer that other C MessagePack parsers do not?

The comparison table marks MPack as the only one of MPack, msgpack-c, CMP and CWPack with stateful error handling, compound size tracking and automatic compound size, and it combines a tree stream parser with an incremental parser. A larger table with descriptions of each entry is at docs/features.md.

Official sources

  1. Issues
  2. License: MIT
  3. ludocode/mpack on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ludocode-mpack.svg)](https://hysenlabs.com/projects/ludocode-mpack)

Was this useful?