MessagePack

It's like JSON.
but fast and small.

MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it's faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.

Redis scripting has support for MessagePack because it is a fast and compact serialization format with a simple to implement specification. I liked it so much that I implemented a MessagePack C extension for Lua just to include it into Redis.

Salvatore Sanfilippo, creator of Redis

Fluentd uses MessagePack for all internal data representation. It's crazy fast because of zero-copy optimization of msgpack-ruby. Now MessagePack is an essential component of Fluentd to achieve high performance and flexibility at the same time.

Sadayuki Furuhashi, creator of Fluentd

Treasure Data built a multi-tenant database optimized for analytical queries using MessagePack. The schemaless database is growing by billions of records every month. We also use MessagePack as a glue between components. Actually we just wanted a fast replacement of JSON, and MessagePack is simply useful.

Kazuki Ohta, CTO

MessagePack has been simply invaluable to us. We use MessagePack + Memcache to cache many of our feeds on Pinterest. These feeds are compressed and very quick to unpack thanks to MessagePack while Memcache gives us fast atomic pushes.

Marty Weiner, Software Engineer

Also use MessagePack? Waiting for your testimonial!

API

 

List your implementation here!

loteixeira/as3-msgpack https://github.com/loteixeira/as3-msgpack

MessagePack for Actionscript3 (Flash, Flex and AIR).

as3-msgpack was designed to work with the interfaces IDataInput and IDataOutput, thus the API might be easily connected with the native classes that handle binary data (such as ByteArray, Socket, FileStream and URLStream).
Moreover, as3-msgpack is capable of decoding data from binary streams.
Get started: http://loteixeira.github.io/lib/2013/08/19/as3-msgpack/

Basic usage (encoding/decoding):

// create messagepack object
var msgpack:MsgPack = new MsgPack();

// encode an array
var bytes:ByteArray = msgpack.write([1, 2, 3, 4, 5]);

// rewind the buffer
bytes.position = 0;

// print the decoded object
trace(msgpack.read(bytes));

For downloads, source code and further information, check the project repository: https://github.com/loteixeira/as3-msgpack.

HEADS-project/arduino_msgpack https://github.com/HEADS-project/arduino_msgpack

arduino_msgpack

This Arduino library provides a light weight serializer and parser for messagepack.

Install

Download the zip, and import it with your Arduino IDE: Sketch>Include Library>Add .zip library

Usage

See the either the .h file, or the examples (led_controller and test_uno_writer).

In short:

  • functions like msgpck_what_next(Stream * s); watch the next type of data without reading it (without advancing the buffer of Stream s).
  • functions like msgpck_read_bool(Stream * s, bool *b) read a value from Stream s.
  • functions like msgpck_write_bool(Stream * s, bool b) write a value on Stream s.

Notes:

  • Stream are used as much as possible in order not to add to much overhead with buffers. Therefore you should be able to store the minimum number of value at a given time.
  • Map and Array related functions concern only their headers. Ex: If you want to write an array containing two elements you should write the array header, then write the two elements.
Limitations

Currently the library does not support:

  • 8 bytes float (Only 4 bytes floats are supported by default on every Arduino and floats are anyway not recommended on Arduino)
  • 2^32 char long (or longer) strings
  • 2^32 byte long (or longer) bins
  • extention types.

camgunz/cmp https://github.com/camgunz/cmp

CMP

Build Status Coverage Status

CMP is a C implementation of the MessagePack serialization format. It currently implements version 5 of the MessagePack Spec.

CMP's goal is to be lightweight and straightforward, forcing nothing on the programmer.

License

While I'm a big believer in the GPL, I license CMP under the MIT license.

Example Usage

The following examples use a file as the backend, and are modeled after the examples included with the msgpack-c project.

#include <stdio.h>
#include <stdlib.h>

#include "cmp.h"

static bool read_bytes(void *data, size_t sz, FILE *fh) {
    return fread(data, sizeof(uint8_t), sz, fh) == (sz * sizeof(uint8_t));
}

static bool file_reader(cmp_ctx_t *ctx, void *data, size_t limit) {
    return read_bytes(data, limit, (FILE *)ctx->buf);
}

static bool file_skipper(cmp_ctx_t *ctx, size_t count) {
    return fseek((FILE *)ctx->buf, count, SEEK_CUR);
}

static size_t file_writer(cmp_ctx_t *ctx, const void *data, size_t count) {
    return fwrite(data, sizeof(uint8_t), count, (FILE *)ctx->buf);
}

static void error_and_exit(const char *msg) {
    fprintf(stderr, "%s\n\n", msg);
    exit(EXIT_FAILURE);
}

int main(void) {
    FILE *fh = NULL;
    cmp_ctx_t cmp = {0};
    uint32_t array_size = 0;
    uint32_t str_size = 0;
    char hello[6] = {0};
    char message_pack[12] = {0};

    fh = fopen("cmp_data.dat", "w+b");

    if (fh == NULL) {
        error_and_exit("Error opening data.dat");
    }

    cmp_init(&cmp, fh, file_reader, file_skipper, file_writer);

    if (!cmp_write_array(&cmp, 2)) {
        error_and_exit(cmp_strerror(&cmp));
    }

    if (!cmp_write_str(&cmp, "Hello", 5)) {
        error_and_exit(cmp_strerror(&cmp));
    }

    if (!cmp_write_str(&cmp, "MessagePack", 11)) {
        error_and_exit(cmp_strerror(&cmp));
    }

    rewind(fh);

    if (!cmp_read_array(&cmp, &array_size)) {
        error_and_exit(cmp_strerror(&cmp));
    }

    /* You can read the str byte size and then read str bytes... */

    if (!cmp_read_str_size(&cmp, &str_size)) {
        error_and_exit(cmp_strerror(&cmp));
    }

    if (str_size > (sizeof(hello) - 1)) {
        error_and_exit("Packed 'hello' length too long\n");
    }

    if (!read_bytes(hello, str_size, fh)) {
        error_and_exit(cmp_strerror(&cmp));
    }

    /*
     * ...or you can set the maximum number of bytes to read and do it all in
     * one call
     */

    str_size = sizeof(message_pack);
    if (!cmp_read_str(&cmp, message_pack, &str_size)) {
        error_and_exit(cmp_strerror(&cmp));
    }

    printf("Array Length: %u.\n", array_size);
    printf("[\"%s\", \"%s\"]\n", hello, message_pack);

    fclose(fh);

    return EXIT_SUCCESS;
}
Advanced Usage

See the examples folder.

Fast, Lightweight, Flexible, and Robust

CMP uses no internal buffers; conversions, encoding and decoding are done on the fly.

CMP's source and header file together are ~4k LOC.

CMP makes no heap allocations.

CMP uses standardized types rather than declaring its own, and it depends only on stdbool.h, stdint.h and string.h.

CMP is written using C89 (ANSI C), aside, of course, from its use of fixed-width integer types and bool.

On the other hand, CMP's test suite requires C99.

CMP only requires the programmer supply a read function, a write function, and an optional skip function. In this way, the programmer can use CMP on memory, files, sockets, etc.

CMP is portable. It uses fixed-width integer types, and checks the endianness of the machine at runtime before swapping bytes (MessagePack is big-endian).

CMP provides a fairly comprehensive error reporting mechanism modeled after errno and strerror.

CMP is thread aware; while contexts cannot be shared between threads, each thread may use its own context freely.

CMP is tested using the MessagePack test suite as well as a large set of custom test cases. Its small test program is compiled with clang using -Wall -Werror -Wextra ... along with several other flags, and generates no compilation errors in either clang or GCC.

CMP's source is written as readably as possible, using explicit, descriptive variable names and a consistent, clear style.

CMP's source is written to be as secure as possible. Its testing suite checks for invalid values, and data is always treated as suspect before it passes validation.

CMP's API is designed to be clear, convenient and unsurprising. Strings are null-terminated, binary data is not, error codes are clear, and so on.

CMP provides optional backwards compatibility for use with other MessagePack implementations that only implement version 4 of the spec.

Building

There is no build system for CMP. The programmer can drop cmp.c and cmp.h in their source tree and modify as necessary. No special compiler settings are required to build it, and it generates no compilation errors in either clang or gcc.

Versioning

CMP's versions are single integers. I don't use semantic versioning because I don't guarantee that any version is completely compatible with any other. In general, semantic versioning provides a false sense of security. You should be evaluating compatibility yourself, not relying on some stranger's versioning convention.

Stability

I only guarantee stability for versions released on the releases page. While rare, both master and develop branches may have errors or mismatched versions.

Backwards Compatibility

Version 4 of the MessagePack spec has no BIN type, and provides no STR8 marker. In order to remain backwards compatible with version 4 of MessagePack, do the following:

Avoid these functions:

  • cmp_write_bin
  • cmp_write_bin_marker
  • cmp_write_str8_marker
  • cmp_write_str8
  • cmp_write_bin8_marker
  • cmp_write_bin8
  • cmp_write_bin16_marker
  • cmp_write_bin16
  • cmp_write_bin32_marker
  • cmp_write_bin32

Use these functions in lieu of their v5 counterparts:

  • cmp_write_str_marker_v4 instead of cmp_write_str_marker
  • cmp_write_str_v4 instead of cmp_write_str
  • cmp_write_object_v4 instead of cmp_write_object
Disabling Floating Point Operations

Thanks to tdragon it's possible to disable floating point operations in CMP by defining CMP_NO_FLOAT. No floating point functionality will be included. Fair warning: this changes the ABI.

Setting Endianness at Compile Time

CMP will honor WORDS_BIGENDIAN. If defined to 0 it will convert data to/from little-endian format when writing/reading. If defined to 1 it won't. If not defined, CMP will check at runtime.

ludocode/mpack https://github.com/ludocode/mpack

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.

msgpack/msgpack-cli https://github.com/msgpack/msgpack-cli

MessagePack for CLI
CI Status
Configuration Status
Release Build status release
Debug (.NET Core 2.0) Build status debug (.NET Core 2.0)
Debug (.NET Core 2.0) Build status debug (.NET Core 1.0)
Debug (.NET Framework 4.x) Build status debug (.NET Framework 4.x)
Debug (Code DOM)] Build status debug (Code DOM)
Debug (miscs)] Build status debug (miscs)
Debug (.NET Framework 3.5) Build status debug (.NET Framework 3.5)
Debug (.NET Framework 3.5 Code DOM) Build status debug (.NET Framework 3.5 Code DOM)
What is it?

This is MessagePack serialization/deserialization for CLI (Common Language Infrastructure) implementations such as .NET Framework, Silverlight, Mono (including Moonlight.) This library can be used from ALL CLS compliant languages such as C#, F#, Visual Basic, Iron Python, Iron Ruby, PowerShell, C++/CLI or so.

Usage

You can serialize/deserialize objects as following:

  1. Create serializer via MessagePackSerializer.Get generic method. This method creates dependent types serializers as well.
  2. Invoke serializer as following:
  • Pack method with destination Stream and target object for serialization.
  • Unpack method with source Stream.
// Creates serializer.
var serializer = MessagePackSerializer.Get<T>();
// Pack obj to stream.
serializer.Pack(stream, obj);
// Unpack from stream.
var unpackedObject = serializer.Unpack(stream);
' Creates serializer.
Dim serializer = MessagePackSerializer.Get(Of T)()
' Pack obj to stream.
serializer.Pack(stream, obj)
' Unpack from stream.
Dim unpackedObject = serializer.Unpack(stream)

For production environment, you should instantiate own SerializationContext and manage its lifetime. It is good idea to treat it as singleton because SerializationContext is thread-safe.

Features
  • Fast and interoperable binary format serialization with simple API.
  • Generating pre-compiled assembly for rapid start up.
  • Flexible MessagePackObject which represents MessagePack type system naturally.

Note: AOT support is limited yet. Use serializer pre-generation with mpu -s utility or API.
If you do not pre-generated serializers, MsgPack for CLI uses reflection in AOT environments, it is slower and it sometimes causes AOT related error (ExecutionEngineException for runtime JIT compilation). You also have to call MessagePackSerializer.PrepareType<T> and companions in advance to avoid AOT related error. See wiki for details.

Documentation

See wiki

Installation
  • Binary files distributed via the NuGet package MsgPack.Cli.
  • You can extract binary (DLL) file as following:
    1. Download *.zip file from GitHub Release page.
    2. Extract it.
    3. Under the bin directory, binaries are there!
    • For mono, you can use net461 or net35 drops as you run with.
    • For Unity, unity3d drop is suitable.
How to build
For .NET Framework
  1. Install Visual Studio 2017 (Community edition is OK) and 2015 (for MsgPack.Windows.sln).

    • You must install .NET Framework 3.5, 4.x, .NET Core, and Xamarin dev tools to build all builds successfully. If you do not want to install options, edit <TargetFrameworks> element in *.csproj files to exclude platforms you want to exclude.
  2. Install latest .NET Core SDK.

  3. Run with Visual Studio Developer Command Prompt:

    msbuild MsgPack.sln /t:Restore msbuild MsgPack.sln

Or (for Unity 3D drops):

msbuild MsgPack.compats.sln /t:Restore
msbuild MsgPack.compats.sln

Or (for Windows Runtime/Phone drops and Silverlight 5 drops):

msbuild MsgPack.Windows.sln /t:Restore
msbuild MsgPack.Windows.sln

Or (for Xamarin unit testing, you must have Xamarin Business or upper license and Mac machine on the LAN to build on Windows):

msbuild MsgPack.Xamarin.sln /t:Restore
msbuild MsgPack.Xamarin.sln

Or open one of above solution files in your IDE and run build command in it.

For Mono
  1. Install latest Mono and .NET Core SDK.
  2. Now, you can build MsgPack.sln and MsgPack.Xamarin.sln with above instructions and msbuild in latest Mono. Note that xbuild does not work because it does not support latest csproj format.
Own Unity 3D Build

First of all, there are binary drops on github release page, you should use it to save your time.
Because we will not guarantee source code organization compatibilities, we might add/remove non-public types or members, which should break source code build.
If you want to import sources, you must include just only described on MsgPack.Unity3D.csproj.
If you want to use ".NET 2.0 Subset" settings, you must use just only described on MsgPack.Unity3D.CorLibOnly.csproj file, and define CORLIB_ONLY compiler constants.

Xamarin Android testing

If you run on Windows, it is recommended to use HXM instead of Hyper-V based emulator.
You can disable Hyper-V from priviledged (administrator) powershell as follows:

Disable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Hypervisor

If you want to use Hyper-V again (such as for Docker for Windows etc.), you can do it by following in priviledged (administrator) powershell:

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Hypervisor
Xamarin Android Trouble shooting tips
  • Q: Javac shows compilation error.
    • A: Rebuild the test project and try it run again.
Xamarin iOS testing

You must create provisoning profiles in your MacOS devices.
See Xamarin documents about provisining for details.

There are bundle IDs of current iOS tests:

  • org.msgpack.msgpack-cli-xamarin-ios-test
  • org.msgpack.msgpack-cli-xamarin-ios-test-packer
  • org.msgpack.msgpack-cli-xamarin-ios-test-unpacker
  • org.msgpack.msgpack-cli-xamarin-ios-test-unpacking
  • org.msgpack.msgpack-cli-xamarin-ios-test-timestamp
  • org.msgpack.msgpack-cli-xamarin-ios-test-arrayserialization
  • org.msgpack.msgpack-cli-xamarin-ios-test-mapserialization

Note that some reflection based serializer tests failed with AOT related limitation.

Xamarin iOS Trouble shooting tips

See Xamarin's official trouble shooting docs first.

  • Q: An error occurred while running unit test project.
    • A: Rebuild the project and rerun it. Or, login your Mac again, ant retry it.
  • Q: It is hard to read English.
    • A: You can read localized Xamarin docs with putting {region}-{lang} as the first component of URL path such as https://developer.xamarin.com/ja-jp/guides/....
Maintenance
MsgPack.Windows.sln

This solution contains Silverlight5 and (old) UWP project for backward compability. They are required Visual Studio 2015 to build and test.
You can download Visual Studio 2015 community edition from here.

You do not have to install Visual Studio 2015 as long as you don't edit/test/build Silverlight and/or old UWP project.

See also

ymofen/SimpleMsgPack.Net https://github.com/ymofen/SimpleMsgPack.Net

SimpleMsgPack.Net

MessagePack implementation for C# / msgpack.org[C#]

Binary files distributed via the NuGet package SimpleMsgPack.

It's like JSON but small and fast.

unit Owner: D10.Mofen
contact:
       qq:185511468, 
    email:[email protected]
	homepage:www.diocp.org
if you find any bug, please contact me!
Works with

.NET Framework 4.x

Code Example
    MsgPack msgpack = new MsgPack();
    msgpack.ForcePathObject("p.name").AsString = "张三";
    msgpack.ForcePathObject("p.age").AsInteger = 25;
    msgpack.ForcePathObject("p.datas").AsArray.Add(90);
    msgpack.ForcePathObject("p.datas").AsArray.Add(80);
    msgpack.ForcePathObject("p.datas").AsArray.Add("李四");
    msgpack.ForcePathObject("p.datas").AsArray.Add(3.1415926);

    // pack file
    msgpack.ForcePathObject("p.filedata").LoadFileAsBytes("C:\\a.png");

    // pack msgPack binary
    byte[] packData = msgpack.Encode2Bytes();

    MsgPack unpack_msgpack = new MsgPack();
	
    // unpack msgpack
    unpack_msgpack.DecodeFromBytes(packData);

    System.Console.WriteLine("name:{0}, age:{1}",
          unpack_msgpack.ForcePathObject("p.name").AsString,
          unpack_msgpack.ForcePathObject("p.age").AsInteger);

    Console.WriteLine("==================================");
    System.Console.WriteLine("use index property, Length{0}:{1}",
          unpack_msgpack.ForcePathObject("p.datas").AsArray.Length,
          unpack_msgpack.ForcePathObject("p.datas").AsArray[0].AsString
          );

    Console.WriteLine("==================================");
    Console.WriteLine("use foreach statement:");
    foreach (MsgPack item in unpack_msgpack.ForcePathObject("p.datas"))
    {
        Console.WriteLine(item.AsString);
    }

    // unpack filedata 
    unpack_msgpack.ForcePathObject("p.filedata").SaveBytesToFile("C:\\b.png");
    Console.Read();

caesay/MPack https://github.com/caesay/MPack

MPack

This library is a lightweight implementation of the MessagePack binary serialization format. MessagePack is a 1-to-1 binary representation of JSON, and the official specification can be found here: https://github.com/msgpack....

Build status (https://ci.appveyor.com/project/caesay/mpack)

Notes
  • This library is designed to be super light weight.
  • Its easiest to understand how this library works if you think in terms of json. The type MPackMap represents a dictionary, and the type MPackArray represents an array.
  • Create MPack instances with the static method MPack.From(object);. You can pass any simple type (such as string, integer, etc), or any Array composed of a simple type. MPack also has implicit conversions from most of the basic types built in.
  • Transform an MPack object back into a CLR type with the static method MPack.To<T>(); or MPack.To(type);. MPack also has explicit converions going back to most basic types, you can do string str = (string)mpack; for instance.
  • MPack now supports native asynchrounous reading and cancellation tokens. It will not block a thread to wait on a stream.
NuGet

MPack is available as a NuGet package!

PM> Install-Package MPack
Usage

Create a object model that can be represented as MsgPack. Here we are creating a dictionary, but really it can be anything:

MPackMap dictionary = new MPackMap
{
    {
        "array1", MPack.From(new[]
        {
            "array1_value1",  // implicitly converted string
            MPack.From("array1_value2"),
            MPack.FromString("array1_value3"),
        })
    },
    {"bool1", MPack.From(true)}, //boolean
    {"double1", MPack.From(50.5)}, //single-precision float
    {"double2", MPack.From(15.2)},
    {"int1", 50505}, // implicitly converted integer
    {"int2", MPack.From(50)} // integer
};

Serialize the data to a byte array or to a stream to be saved, transmitted, etc:

byte[] encodedBytes = dictionary.EncodeToBytes();
// -- or --
dictionary.EncodeToStream(stream);

Parse the binary data back into a MPack object model (you can also cast back to an MPackMap or MPackArray after reading if you want dictionary/array methods):

var reconstructed = MPack.ParseFromBytes(encodedBytes);
// -- or --
var reconstructed = MPack.ParseFromStream(stream);

Turn MPack objects back into types that we understand with the generic To<>() method. Since we know the types of everything here we can just call To<bool>() to reconstruct our bool, but if you don't know you can access the instance enum MPack.ValueType to know what kind of value it is:

bool bool1 = reconstructed["bool1"].To<bool>();
var array1 = reconstructed["array1"] as MPackArray;
var array1_value1 = array1[0];
double double1 = reconstructed["double1"].To<double>();
//etc...
Credits

The following people/projects have made this possible:

  1. Me: [caelantsayler]at[gmail]dot[com]
  2. All of the people that make MessagePack happen: https://github.com/msgpack

mlsomers/LsMsgPack http://www.infotopie.nl/open-source/msgpack-explorer

LsMsgPack

MsgPack debugging and validation tool also usable as Fiddler plugin

More info about this application (and screenshots) can be found at: http://www.infotopie.nl/open-source/msgpack-explorer

Library Usage Example

Although the original was optimised for debugging and analysing, a lightweight version of the lib is included which does not keep track of all offsets and other overhead needed for debugging. It can be used in your code.

Add LsMsgPackL.dll as a reference.

public class MyClass
{
    public string Name { get; set; }
    public int Quantity { get; set; }
    public List<object> Anything { get; set; }
}

public void Test()
{
    MyClass message = new MyClass()
    {
        Name = "Test message",
        Quantity = 100,
        Anything = new List<object>(new object[] { "first", 2, false, null, 4.2d, "last" })
    };
    
    // Serialize
    byte[] buffer = MsgPackSerializer.Serialize(message);
    
    // Deserialize
    MyClass returnMsg = MsgPackSerializer.Deserialize<MyClass>(buffer);
}

I have never run any benchmarks so I have no idea how it will perform against other implementations.

Fiddler Integration

In order to use this tool as a Fiddler plugin, copy the following files to the Fiddler Inspectors directory (usually C:\Program Files\Fiddler2\Inspectors):

  • MsgPackExplorer.exe
  • LsMsgPackFiddlerInspector.dll
  • LsMsgPack.dll

Restart fiddler and you should see a MsgPack option in the Inspectors list.

Source documentation
Modules LsMsgPack.dll

This module contains the "parser" and generator of MsgPack Packages. It breaks down the binary file into a hirarchical structure, keeping track of offsets and errors. And it can also be used to generate MsgPack files.

MsgPackExplorer.exe

The main winforms executable, containing a MsgPackExplorer UserControl (so it can easily be integrated into other tools such as Fiddler).

LsMsgPackFiddlerInspector.dll

A tiny wrapper enabling the use of MsgPack Explorer as a Fiddler Inspector.

LsMsgPackUnitTests.dll

Some unit tests on the core LsMsgPack.dll. No full coverage yet, but at least it's a start.

LsMsgPackL.dll & LightUnitTests.dll

A light version of the "parser". The parsing and generating methods are almost identical to the LsMsgPack lib, but with allot of overhead removed that comes with keeping track of offsets, original types and other debugging info. I'm planning to use this version in my projects that use the MsgPack format. The LightUnitTests are the same as LsMsgPackUnitTests with some tests omitted.

neuecc/MessagePack-CSharp https://github.com/neuecc/MessagePack-CSharp

MessagePack for C# (.NET, .NET Core, Unity, Xamarin)

NuGet NuGet Releases

Join the chat at https://gitter.im/MessagePack-CSharp/Lobby Build Status

The extremely fast MessagePack serializer for C#. It is 10x faster than MsgPack-Cli and outperforms other C# serializers. MessagePack for C# also ships with built-in support for LZ4 compression - an extremely fast compression algorithm. Performance is important, particularly in applications like games, distributed computing, microservices, or data caches.

Perf comparison graph

MessagePack has a compact binary size and a full set of general purpose expressive data types. Please have a look at the comparison with JSON, protobuf, ZeroFormatter section and learn why MessagePack C# is the fastest.

Table of Contents
Installation

This library is distributed via NuGet. Special Unity support is available, too.

We target .NET Standard 2.0 with special optimizations for .NET Core 2.1+, making it compatible with most reasonably recent .NET runtimes such as Core 2.0 and later, Framework 4.6.1 and later, Mono 5.4 and later and Unity 2018.3 and later. The library code is pure C# (with Just-In-Time IL code generation on some platforms).

NuGet packages

To install with NuGet, just install the MessagePack package:

Install-Package MessagePack

Install the optional C# analyzers package to get warnings about coding mistakes and automatic fix suggestions to save you time:

Install-Package MessagePackAnalyzer

There are also a range of official and third party Extension Packages available (learn more in our extensions section):

Install-Package MessagePack.ReactiveProperty
Install-Package MessagePack.UnityShims
Install-Package MessagePack.AspNetCoreMvcFormatter
Unity

For Unity projects, the releases page provides downloadable .unitypackage files. When using in Unity IL2CPP or Xamarin AOT environments, please carefully read the pre-code generation section.

Migration notes from v1.x

If you were using MessagePack for C# v1.x, check out the "How to update to our new v2.x version" document.

Quick Start

Define the struct or class to be serialized and annotate it with a [MessagePackObject] attribute. Annotate members whose values should be serialized (fields as well as properties) with [Key] attributes.

[MessagePackObject]
public class MyClass
{
    // Key attributes take a serialization index (or string name)
    // The values must be unique and versioning has to be considered as well.
    // Keys are described in later sections in more detail.
    [Key(0)]
    public int Age { get; set; }

    [Key(1)]
    public string FirstName { get; set; }

    [Key(2)]
    public string LastName { get; set; }

    // All fields or properties that should not be serialized must be annotated with [IgnoreMember].
    [IgnoreMember]
    public string FullName { get { return FirstName + LastName; } }
}

Call MessagePackSerializer.Serialize<T>/Deserialize<T> to serialize/deserialize your object instance. You can use the ConvertToJson method to get a human readable representation of any MessagePack binary blob.

class Program
{
    static void Main(string[] args)
    {
        var mc = new MyClass
        {
            Age = 99,
            FirstName = "hoge",
            LastName = "huga",
        };

        // Call Serialize/Deserialize, that's all.
        byte[] bytes = MessagePackSerializer.Serialize(mc);
        MyClass mc2 = MessagePackSerializer.Deserialize<MyClass>(bytes);

        // You can dump MessagePack binary blobs to human readable json.
        // Using indexed keys (as opposed to string keys) will serialize to MessagePack arrays,
        // hence property names are not available.
        // [99,"hoge","huga"]
        var json = MessagePackSerializer.ConvertToJson(bytes);
        Console.WriteLine(json);
    }
}

By default, a MessagePackObject annotation is required. This can be made optional; see the Object Serialization section and the Formatter Resolver section for details.

Analyzer

The MessagePackAnalyzer package aids with:

  1. Automating definitions for your serializable objects.
  2. Produces compiler warnings upon incorrect attribute use, member accessibility, and more.

analyzergif

If you want to allow a specific custom type (for example, when registering a custom type), put MessagePackAnalyzer.json at the project root and change the Build Action to AdditionalFiles.

image

An example MessagePackAnalyzer.json:

[ "MyNamespace.FooClass", "MyNameSpace.BarStruct" ]
Built-in supported types

These types can serialize by default:

  • Primitives (int, string, etc...), Enums, Nullable<>, Lazy<>
  • TimeSpan, DateTime, DateTimeOffset
  • Guid, Uri, Version, StringBuilder
  • BigInteger, Complex, Half
  • Array[], Array[,], Array[,,], Array[,,,], ArraySegment<>, BitArray
  • KeyValuePair<,>, Tuple<,...>, ValueTuple<,...>
  • ArrayList, Hashtable
  • List<>, LinkedList<>, Queue<>, Stack<>, HashSet<>, ReadOnlyCollection<>, SortedList<,>
  • IList<>, ICollection<>, IEnumerable<>, IReadOnlyCollection<>, IReadOnlyList<>
  • Dictionary<,>, IDictionary<,>, SortedDictionary<,>, ILookup<,>, IGrouping<,>, ReadOnlyDictionary<,>, IReadOnlyDictionary<,>
  • ObservableCollection<>, ReadOnlyObservableCollection<>
  • ISet<>,
  • ConcurrentBag<>, ConcurrentQueue<>, ConcurrentStack<>, ConcurrentDictionary<,>
  • Immutable collections (ImmutableList<>, etc)
  • Custom implementations of ICollection<> or IDictionary<,> with a parameterless constructor
  • Custom implementations of IList or IDictionary with a parameterless constructor

You can add support for custom types, and there are some official/third-party extension packages for:

  • ReactiveProperty
  • for Unity (Vector3, Quaternion, etc...)
  • F# (Record, FsList, Discriminated Unions, etc...)

Please see the extensions section.

MessagePack.Nil is the built-in type representing null/void in MessagePack for C#.

Object Serialization

MessagePack for C# can serialize your own public class or struct types. By default, serializable types must be annotated with the [MessagePackObject] attribute and members with the [Key] attribute. Keys can be either indexes (int) or arbitrary strings. If all keys are indexes, arrays are used for serialization, which offers advantages in performance and binary size. Otherwise, MessagePack maps (dictionaries) will be used.

If you use [MessagePackObject(keyAsPropertyName: true)], then members do not require explicit Key attributes, but string keys will be used.

[MessagePackObject]
public class Sample1
{
    [Key(0)]
    public int Foo { get; set; }
    [Key(1)]
    public int Bar { get; set; }
}

[MessagePackObject]
public class Sample2
{
    [Key("foo")]
    public int Foo { get; set; }
    [Key("bar")]
    public int Bar { get; set; }
}

[MessagePackObject(keyAsPropertyName: true)]
public class Sample3
{
    // No need for a Key attribute
    public int Foo { get; set; }

    // If want to ignore a public member, you can use the  IgnoreMember attribute
    [IgnoreMember]
    public int Bar { get; set; }
}

// [10,20]
Console.WriteLine(MessagePackSerializer.SerializeToJson(new Sample1 { Foo = 10, Bar = 20 }));

// {"foo":10,"bar":20}
Console.WriteLine(MessagePackSerializer.SerializeToJson(new Sample2 { Foo = 10, Bar = 20 }));

// {"Foo":10}
Console.WriteLine(MessagePackSerializer.SerializeToJson(new Sample3 { Foo = 10, Bar = 20 }));

All public instance members (fields as well as properties) will be serialized. If you want to ignore certain public members, annotate the member with a [IgnoreMember] attribute.

Please note that any serializable struct or class must have public accessibility; private and internal structs and classes cannot be serialized! The default of requiring MessagePackObject annotations is meant to enforce explicitness and therefore may help write more robust code.

Should you use an indexed (int) key or a string key? We recommend using indexed keys for faster serialization and a more compact binary representation than string keys. However, the additional information in the strings of string keys can be quite useful when debugging.

When classes change or are extended, be careful about versioning. MessagePackSerializer will initialize members to their default value if a key does not exist in the serialized binary blob, meaning members using reference types can be initialized to null. If you use indexed (int) keys, the keys should start at 0 and should be sequential. If a later version stops using certain members, you should keep the obsolete members (C# provides an Obsolete attribute to annotate such members) until all other clients had a chance to update and remove their uses of these members as well. Also, when the values of indexed keys "jump" a lot, leaving gaps in the sequence, it will negatively affect the binary size, as null placeholders will be inserted into the resulting arrays. However, you shouldn't reuse indexes of removed members to avoid compatibility issues between clients or when trying to deserialize legacy blobs.

Example of index gaps and resulting placeholders:

[MessagePackObject]
public class IntKeySample
{
    [Key(3)]
    public int A { get; set; }
    [Key(10)]
    public int B { get; set; }
}

// [null,null,null,0,null,null,null,null,null,null,0]
Console.WriteLine(MessagePackSerializer.SerializeToJson(new IntKeySample()));

If you do not want to explicitly annotate with the MessagePackObject/Key attributes and instead want to use MessagePack for C# more like e.g. Json.NET, you can make use of the contractless resolver.

public class ContractlessSample
{
    public int MyProperty1 { get; set; }
    public int MyProperty2 { get; set; }
}

var data = new ContractlessSample { MyProperty1 = 99, MyProperty2 = 9999 };
var bin = MessagePackSerializer.Serialize(
  data,
  MessagePack.Resolvers.ContractlessStandardResolver.Options);

// {"MyProperty1":99,"MyProperty2":9999}
Console.WriteLine(MessagePackSerializer.ConvertToJson(bin));

// You can also set ContractlessStandardResolver as the default.
// (Global state; Not recommended when writing library code)
MessagePackSerializer.DefaultOptions = MessagePack.Resolvers.ContractlessStandardResolver.Options;

// Now serializable...
var bin2 = MessagePackSerializer.Serialize(data);

If you want to serialize private members as well, you can use one of the *AllowPrivate resolvers.

[MessagePackObject]
public class PrivateSample
{
    [Key(0)]
    int x;

    public void SetX(int v)
    {
        x = v;
    }

    public int GetX()
    {
        return x;
    }
}

var data = new PrivateSample();
data.SetX(9999);

// You can choose either StandardResolverAllowPrivate
// or ContractlessStandardResolverAllowPrivate
var bin = MessagePackSerializer.Serialize(
  data,
  MessagePack.Resolvers.DynamicObjectResolverAllowPrivate.Options);

If you want to use MessagePack for C# more like a BinaryFormatter with a typeless serialization API, use the typeless resolver and helpers. Please consult the Typeless section.

Resolvers are the way to add specialized support for custom types to MessagePack for C#. Please refer to the Extension point section.

DataContract compatibility

You can use [DataContract] annotations instead of [MessagePackObject] ones. If type is annotated with DataContract, you can use [DataMember] annotations instead of [Key] ones and [IgnoreDataMember] instead of [IgnoreMember].

Then [DataMember(Order = int)] will behave the same as [Key(int)], [DataMember(Name = string)] the same as [Key(string)], and [DataMember] the same as [Key(nameof(member name)].

Using DataContract, e.g. in shared libraries, makes your classes/structs independent from MessagePack for C# serialization. However, it is not supported by the analyzers nor in code generation by the mpc tool. Also, features like UnionAttribute, MessagePackFormatter, SerializationConstructor, etc can not be used. Due to this, we recommend that you use the specific MessagePack for C# annotations when possible.

Serializing readonly/immutable object members (SerializationConstructor)

MessagePack for C# supports serialization of readonly/immutable objects/members. For example, this struct can be serialized and deserialized.

[MessagePackObject]
public struct Point
{
    [Key(0)]
    public readonly int X;
    [Key(1)]
    public readonly int Y;

    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

var data = new Point(99, 9999);
var bin = MessagePackSerializer.Serialize(data);

// Okay to deserialize immutable object
var point = MessagePackSerializer.Deserialize<Point>(bin);

MessagePackSerializer will choose the constructor with the best matched argument list, using argument indexes index for index keys, or parameter names for string keys. If it cannot determine an appropriate constructor, a MessagePackDynamicObjectResolverException: can't find matched constructor parameter exception will be thrown. You can specify which constructor to use manually with a [SerializationConstructor] annotation.

[MessagePackObject]
public struct Point
{
    [Key(0)]
    public readonly int X;
    [Key(1)]
    public readonly int Y;

    [SerializationConstructor]
    public Point(int x)
    {
        this.X = x;
        this.Y = -1;
    }

    // If not marked attribute, used this(most matched argument)
    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}
C# 9 record types

C# 9.0 record with primary constructor is similar immutable object, also supports serialize/deserialize.

// use key as property name
[MessagePackObject(true)]public record Point(int X, int Y);

// use property: to set KeyAttribute
[MessagePackObject] public record Point([property:Key(0)] int X, [property: Key(1)] int Y);

// Or use explicit properties
[MessagePackObject]
public record Person
{
    [Key(0)]
    public string FirstName { get; init; }

    [Key(1)]
    public string LastName { get; init; }
}
C# 9 init property setter limitations

When using init property setters in generic classes, a CLR bug prevents our most efficient code generation from invoking the property setter. As a result, you should avoid using init on property setters in generic classes when using the public-only DynamicObjectResolver/StandardResolver.

When using the DynamicObjectResolverAllowPrivate/StandardResolverAllowPrivate resolver the bug does not apply and you may use init without restriction.

Serialization Callback

Objects implementing the IMessagePackSerializationCallbackReceiver interface will received OnBeforeSerialize and OnAfterDeserialize calls during serialization/deserialization.

[MessagePackObject]
public class SampleCallback : IMessagePackSerializationCallbackReceiver
{
    [Key(0)]
    public int Key { get; set; }

    public void OnBeforeSerialize()
    {
        Console.WriteLine("OnBefore");
    }

    public void OnAfterDeserialize()
    {
        Console.WriteLine("OnAfter");
    }
}
Union

MessagePack for C# supports serializing interface-typed and abstract class-typed objects. It behaves like XmlInclude or ProtoInclude. In MessagePack for C# these are called Union. Only interfaces and abstracts classes are allowed to be annotated with Union attributes. Unique union keys are required.

// Annotate inheritance types
[MessagePack.Union(0, typeof(FooClass))]
[MessagePack.Union(1, typeof(BarClass))]
public interface IUnionSample
{
}

[MessagePackObject]
public class FooClass : IUnionSample
{
    [Key(0)]
    public int XYZ { get; set; }
}

[MessagePackObject]
public class BarClass : IUnionSample
{
    [Key(0)]
    public string OPQ { get; set; }
}

// ---

IUnionSample data = new FooClass() { XYZ = 999 };

// Serialize interface-typed object.
var bin = MessagePackSerializer.Serialize(data);

// Deserialize again.
var reData = MessagePackSerializer.Deserialize<IUnionSample>(bin);

// Use with e.g. type-switching in C# 7.0
switch (reData)
{
    case FooClass x:
        Console.WriteLine(x.XYZ);
        break;
    case BarClass x:
        Console.WriteLine(x.OPQ);
        break;
    default:
        break;
}

Unions are internally serialized to two-element arrays.

IUnionSample data = new BarClass { OPQ = "FooBar" };

var bin = MessagePackSerializer.Serialize(data);

// Union is serialized to two-length array, [key, object]
// [1,["FooBar"]]
Console.WriteLine(MessagePackSerializer.ConvertToJson(bin));

Using Union with abstract classes works the same way.

[Union(0, typeof(SubUnionType1))]
[Union(1, typeof(SubUnionType2))]
[MessagePackObject]
public abstract class ParentUnionType
{
    [Key(0)]
    public int MyProperty { get; set; }
}

[MessagePackObject]
public class SubUnionType1 : ParentUnionType
{
    [Key(1)]
    public int MyProperty1 { get; set; }
}

[MessagePackObject]
public class SubUnionType2 : ParentUnionType
{
    [Key(1)]
    public int MyProperty2 { get; set; }
}

Please be mindful that you cannot reuse the same keys in derived types that are already present in the parent type, as internally a single flat array or map will be used and thus cannot have duplicate indexes/keys.

Dynamic (Untyped) Deserialization

When calling MessagePackSerializer.Deserialize<object> or MessagePackSerializer.Deserialize<dynamic>, any values present in the blob will be converted to primitive values, i.e. bool, char, sbyte, byte, short, int, long, ushort, uint, ulong, float, double, DateTime, string, byte[], object[], IDictionary<object, object>.

// Sample blob.
var model = new DynamicModel { Name = "foobar", Items = new[] { 1, 10, 100, 1000 } };
var blob = MessagePackSerializer.Serialize(model, ContractlessStandardResolver.Options);

// Dynamic ("untyped")
var dynamicModel = MessagePackSerializer.Deserialize<dynamic>(blob, ContractlessStandardResolver.Options);

// You can access the data using array/dictionary indexers, as shown above
Console.WriteLine(dynamicModel["Name"]); // foobar
Console.WriteLine(dynamicModel["Items"][2]); // 100

Exploring object trees using the dictionary indexer syntax is the fastest option for untyped deserialization, but it is tedious to read and write. Where performance is not as important as code readability, consider deserializing with ExpandoObject.

Object Type Serialization

StandardResolver and ContractlessStandardResolver can serialize object/anonymous typed objects.

var objects = new object[] { 1, "aaa", new ObjectFieldType { Anything = 9999 } };
var bin = MessagePackSerializer.Serialize(objects);

// [1,"aaa",[9999]]
Console.WriteLine(MessagePackSerializer.ConvertToJson(bin));

// Support anonymous Type Serialize
var anonType = new { Foo = 100, Bar = "foobar" };
var bin2 = MessagePackSerializer.Serialize(anonType, MessagePack.Resolvers.ContractlessStandardResolver.Options);

// {"Foo":100,"Bar":"foobar"}
Console.WriteLine(MessagePackSerializer.ConvertToJson(bin2));

Unity supports is limited.

When deserializing, the behavior will be the same as Dynamic (Untyped) Deserialization.

Typeless

The typeless API is similar to BinaryFormatter, as it will embed type information into the blobs, so no types need to be specified explicitly when calling the API.

object mc = new Sandbox.MyClass()
{
    Age = 10,
    FirstName = "hoge",
    LastName = "huga"
};

// Serialize with the typeless API
var blob = MessagePackSerializer.Typeless.Serialize(mc);

// Blob has embedded type-assembly information.
// ["Sandbox.MyClass, Sandbox",10,"hoge","huga"]
Console.WriteLine(MessagePackSerializer.ConvertToJson(bin));

// You can deserialize to MyClass again with the typeless API
// Note that no type has to be specified explicitly in the Deserialize call
// as type information is embedded in the binary blob
var objModel = MessagePackSerializer.Typeless.Deserialize(bin) as MyClass;

Type information is represented by the MessagePack ext format, type code 100.

MessagePackSerializer.Typeless is a shortcut of Serialize/Deserialize<object>(TypelessContractlessStandardResolver.Instance). If you want to configure it as the default resolver, you can use MessagePackSerializer.Typeless.RegisterDefaultResolver.

TypelessFormatter can used standalone or combined with other resolvers.

// Replaced `object` uses the typeless resolver
var resolver = MessagePack.Resolvers.CompositeResolver.Create(
    new[] { MessagePack.Formatters.TypelessFormatter.Instance },
    new[] { MessagePack.Resolvers.StandardResolver.Instance });

public class Foo
{
    // use Typeless(this field only)
    [MessagePackFormatter(typeof(TypelessFormatter))]
    public object Bar;
}

If a type's name is changed later, you can no longer deserialize old blobs. But you can specify a fallback name in such cases, providing a TypelessFormatter.BindToType function of your own.

MessagePack.Formatters.TypelessFormatter.BindToType = typeName =>
{
    if (typeName.StartsWith("SomeNamespace"))
    {
        typeName = typeName.Replace("SomeNamespace", "AnotherNamespace");
    }

    return Type.GetType(typeName, false);
};
Security

Deserializing data from an untrusted source can introduce security vulnerabilities in your application. Depending on the settings used during deserialization, untrusted data may be able to execute arbitrary code or cause a denial of service attack. Untrusted data might come from over the network from an untrusted source (e.g. any and every networked client) or can be tampered with by an intermediary when transmitted over an unauthenticated connection, or from a local storage that might have been tampered with, or many other sources. MessagePack for C# does not provide any means to authenticate data or make it tamper-resistant. Please use an appropriate method of authenticating data before deserialization - such as a MAC .

Please be very mindful of these attack scenarios; many projects and companies, and serialization library users in general, have been bitten by untrusted user data deserialization in the past.

When deserializing untrusted data, put MessagePack into a more secure mode by configuring your MessagePackSerializerOptions.Security property:

var options = MessagePackSerializerOptions.Standard
    .WithSecurity(MessagePackSecurity.UntrustedData);

// Pass the options explicitly for the greatest control.
T object = MessagePackSerializer.Deserialize<T>(data, options);

// Or set the security level as the default.
MessagePackSerializer.DefaultOptions = options;

You should also avoid the Typeless serializer/formatters/resolvers for untrusted data as that opens the door for the untrusted data to potentially deserialize unanticipated types that can compromise security.

The UntrustedData mode merely hardens against some common attacks, but is no fully secure solution in itself.

Performance

Benchmarks comparing MessagePack For C# to other serializers were run on Windows 10 Pro x64 Intel Core i7-6700K 4.00GHz, 32GB RAM. Benchmark code is available here - and their version info. ZeroFormatter and FlatBuffers have infinitely fast deserializers, so ignore their deserialization performance.

image

MessagePack for C# uses many techniques to improve performance.

  • The serializer uses IBufferWriter<byte> rather than System.IO.Stream to reduce memory overhead.
  • Buffers are rented from pools to reduce allocations, keeping throughput high through reduced GC pressure.
  • Don't create intermediate utility instances (*Writer/*Reader, *Context, etc...)
  • Utilize dynamic code generation and JIT to avoid boxing value types. Use AOT generation on platforms that prohibit JITs.
  • Cached generated formatters on static generic fields (don't use dictionary-cache because dictionary lookup is overhead). See Resolvers
  • Heavily tuned dynamic IL code generation and JIT to avoid boxing value types. See DynamicObjectTypeBuilder. Use AOT generation on platforms that prohibit JIT.
  • Call the Primitive API directly when IL code generation determines target types to be primitive.
  • Reduce branching of variable length formats when IL code generation knows the target type (integer/string) ranges
  • Don't use the IEnumerable<T> abstraction to iterate over collections when possible, see: CollectionFormatterBase and derived collection formatters
  • Use pre-generated lookup tables to reduce checks of mgpack type constraints, see: MessagePackBinary
  • Uses optimized type key dictionary for non-generic methods, see: ThreadsafeTypeKeyHashTable
  • Avoid string key decoding for lookup maps (string key and use automata based name lookup with inlined IL code generation, see: AutomataDictionary
  • To encode string keys, use pre-generated member name bytes and fixed sized byte array copies in IL, see: UnsafeMemory.cs

Before creating this library, I implemented a fast fast serializer with ZeroFormatter#Performance. This is a further evolved implementation. MessagePack for C# is always fast and optimized for all types (primitive, small struct, large object, any collections).

Deserialization Performance for different options

Performance varies depending on the options used. This is a micro benchmark with BenchmarkDotNet. The target object has 9 members (MyProperty1 ~ MyProperty9), values are zero.

Method Mean Error Scaled Gen 0 Allocated
M IntKey 72.67 ns NA 1.00 0.0132 56 B
M StringKey 217.95 ns NA 3.00 0.0131 56 B
M Typeless_IntKey 176.71 ns NA 2.43 0.0131 56 B
M Typeless_StringKey 378.64 ns NA 5.21 0.0129 56 B
MsgPackCliMap 1,355.26 ns NA 18.65 0.1431 608 B
MsgPackCliArray 455.28 ns NA 6.26 0.0415 176 B
ProtobufNet 265.85 ns NA 3.66 0.0319 136 B
Hyperion 366.47 ns NA 5.04 0.0949 400 B
JsonNetString 2,783.39 ns NA 38.30 0.6790 2864 B
JsonNetStreamReader 3,297.90 ns NA 45.38 1.4267 6000 B
JilString 553.65 ns NA 7.62 0.0362 152 B
JilStreamReader 1,408.46 ns NA 19.38 0.8450 3552 B

ÌntKey, StringKey, Typeless_IntKey, Typeless_StringKey are MessagePack for C# options. All MessagePack for C# options achieve zero memory allocations in the deserialization process. JsonNetString/JilString is deserialized from strings. JsonNetStreamReader/JilStreamReader is deserialized from UTF-8 byte arrays using StreamReader. Deserialization is normally read from Stream. Thus, it will be restored from byte arrays (or Stream) instead of strings.

MessagePack for C# IntKey is the fastest. StringKey is slower than IntKey because matching the character string of property names is required. IntKey works by reading the array length, then for (array length) { binary decode }. StringKey works by reading map length, for (map length) { decode key, lookup key, binary decode }, so it requires an additional two steps (decoding of keys and lookups of keys).

String key is often a useful, contractless, simple replacement of JSON, interoperability with other languages, and more robust versioning. MessagePack for C# is also optimized for string keys as much a possible. First of all, it does not decode UTF-8 byte arrays to full string for matching with the member name; instead it will look up the byte arrays as it is (to avoid decoding costs and extra memory allocations).

And It will try to match each long type (per 8 character, if it is not enough, pad with 0) using automata and inline it when generating IL code.

image

This also avoids calculating the hash code of byte arrays, and the comparison can be made several times faster using the long type.

This is the sample of decompiled generated deserializer code, decompiled using ILSpy.

image

If the number of nodes is large, searches will use an embedded binary search.

Extra note, this is serialization benchmark result.

Method Mean Error Scaled Gen 0 Allocated
IntKey 84.11 ns NA 1.00 0.0094 40 B
StringKey 126.75 ns NA 1.51 0.0341 144 B
Typeless_IntKey 183.31 ns NA 2.18 0.0265 112 B
Typeless_StringKey 193.95 ns NA 2.31 0.0513 216 B
MsgPackCliMap 967.68 ns NA 11.51 0.1297 552 B
MsgPackCliArray 284.20 ns NA 3.38 0.1006 424 B
ProtobufNet 176.43 ns NA 2.10 0.0665 280 B
Hyperion 280.14 ns NA 3.33 0.1674 704 B
ZeroFormatter 149.95 ns NA 1.78 0.1009 424 B
JsonNetString 1,432.55 ns NA 17.03 0.4616 1944 B
JsonNetStreamWriter 1,775.72 ns NA 21.11 1.5526 6522 B
JilString 547.51 ns NA 6.51 0.3481 1464 B
JilStreamWriter 778.78 ns NA 9.26 1.4448 6066 B

Of course, IntKey is fastest but StringKey also performs reasonably well.

LZ4 Compression

MessagePack is a fast and compact format but it is not compression. LZ4 is an extremely fast compression algorithm, and using it MessagePack for C# can achieve extremely fast performance as well as extremely compact binary sizes!

MessagePack for C# has built-in LZ4 support. You can activate it using a modified options object and passing it into an API like this:

var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
MessagePackSerializer.Serialize(obj, lz4Options);

MessagePackCompression has two modes, Lz4Block and Lz4BlockArray. Neither is a simple binary LZ4 compression, but a special compression integrated into the serialization pipeline, using MessagePack ext code (Lz4BlockArray (98) or Lz4Block (99)). Therefore, it is not readily compatible with compression offered in other languages.

Lz4Block compresses an entire MessagePack sequence as a single LZ4 block. This is the simple compression that achieves best compression ratio, at the cost of copying the entire sequence when necessary to get contiguous memory.

Lz4BlockArray compresses an entire MessagePack sequence as a array of LZ4 blocks. Compressed/decompressed blocks are chunked and thus do not enter the GC's Large-Object-Heap, but the compression ratio is slightly worse.

We recommend to use Lz4BlockArray as the default when using compression. For compatibility with MessagePack v1.x, use Lz4Block.

Regardless of which LZ4 option is set at the deserialization, both methods can be deserialized. For example, when the Lz4BlockArray option was used, binary data using either Lz4Block and Lz4BlockArray can be deserialized. Neither can be decompressed and hence deserialized when the compression option is set to None.

Attributions

LZ4 compression support is using Milosz Krajewski's lz4net code with some modifications.

Comparison with protobuf, JSON, ZeroFormatter

protobuf-net is major, widely used binary-format library on .NET. I love protobuf-net and respect their great work. But when you use protobuf-net as a general purpose serialization format, you may encounter an annoying issue.

[ProtoContract]
public class Parent
{
    [ProtoMember(1)]
    public int Primitive { get; set; }
    [ProtoMember(2)]
    public Child Prop { get; set; }
    [ProtoMember(3)]
    public int[] Array { get; set; }
}

[ProtoContract]
public class Child
{
    [ProtoMember(1)]
    public int Number { get; set; }
}

using (var ms = new MemoryStream())
{
    // serialize null.
    ProtoBuf.Serializer.Serialize<Parent>(ms, null);

    ms.Position = 0;
    var result = ProtoBuf.Serializer.Deserialize<Parent>(ms);

    Console.WriteLine(result != null); // True, not null. but all property are zero formatted.
    Console.WriteLine(result.Primitive); // 0
    Console.WriteLine(result.Prop); // null
    Console.WriteLine(result.Array); // null
}

using (var ms = new MemoryStream())
{
    // serialize empty array.
    ProtoBuf.Serializer.Serialize<Parent>(ms, new Parent { Array = System.Array.Empty<int>() });

    ms.Position = 0;
    var result = ProtoBuf.Serializer.Deserialize<Parent>(ms);

    Console.WriteLine(result.Array == null); // True, null!
}

protobuf(-net) cannot handle null and empty collection correctly, because protobuf has no null representation (see this SO answer from a protobuf-net author).

MessagePack's type system can correctly serialize the entire C# type system. This is a strong reason to recommend MessagePack over protobuf.

Protocol Buffers have good IDL and gRPC support. If you want to use IDL, I recommend Google.Protobuf over MessagePack.

JSON is good general-purpose format. It is simple, human-readable and thoroughly-enough specified. Utf8Json - which I created as well - adopts same architecture as MessagePack for C# and avoids encoding/decoding costs as much as possible just like this library does. If you want to know more about binary vs text formats, see Utf8Json/which serializer should be used.

ZeroFormatter is similar as FlatBuffers but specialized to C#, and special in that regard. Deserialization is infinitely fast but the produced binary size is larger. And ZeroFormatter's caching algorithm requires additional memory.

For many common uses, MessagePack for C# would be a better fit.

Hints to achieve maximum performance when using MessagePack for C#

MessagePack for C# prioritizes maximum performance by default. However, there are also some options that sacrifice performance for convenience.

Use indexed keys instead of string keys (Contractless)

The Deserialization Performance for different options section shows the results of indexed keys (IntKey) vs string keys (StringKey) performance. Indexed keys serialize the object graph as a MessagePack array. String keys serializes the object graph as a MessagePack map.

For example this type is serialized to

[MessagePackObject]
public class Person
{
    [Key(0)] or [Key("name")]
    public string Name { get; set;}
    [Key(1)] or [Key("age")]
    public int Age { get; set;}
}

new Person { Name = "foobar", Age = 999 }
  • IntKey: ["foobar", 999]
  • StringKey: {"name:"foobar","age":999}.

IntKey is always fast in both serialization and deserialization because it does not have to handle and lookup key names, and always has the smaller binary size.

StringKey is often a useful, contractless, simple replacement for JSON, interoperability with other languages with MessagePack support, and less error prone versioning. But to achieve maximum performance, use IntKey.

Create own custom composite resolver

CompositeResolver.Create is an easy way to create composite resolvers. But formatter lookups have some overhead. If you create a custom resolver (or use StaticCompositeResolver.Instance), you can avoid this overhead.

public class MyApplicationResolver : IFormatterResolver
{
    public static readonly IFormatterResolver Instance = new MyApplicationResolver();

    // configure your custom resolvers.
    private static readonly IFormatterResolver[] Resolvers = new IFormatterResolver[]
    {
    };

    private MyApplicationResolver() { }

    public IMessagePackFormatter<T> GetFormatter<T>()
    {
        return Cache<T>.Formatter;
    }

    private static class Cache<T>
    {
        public static IMessagePackFormatter<T> Formatter;

        static Cache()
        {
            // configure your custom formatters.
            if (typeof(T) == typeof(XXX))
            {
                Formatter = new ICustomFormatter();
                return;
            }

            foreach (var resolver in Resolvers)
            {
                var f = resolver.GetFormatter<T>();
                if (f != null)
                {
                    Formatter = f;
                    return;
                }
            }
        }
    }
}

NOTE: If you are creating a library, recommend using the above custom resolver instead of CompositeResolver.Create. Also, libraries must not use StaticCompositeResolver - as it is global state - to avoid compatibility issues.

Use native resolvers

By default, MessagePack for C# serializes GUID as string. This is much slower than the native .NET format GUID. The same applies to Decimal. If your application makes heavy use of GUID or Decimal and you don't have to worry about interoperability with other languages, you can replace them with the native serializers NativeGuidResolver and NativeDecimalResolver respectively.

Also, DateTime is serialized using the MessagePack timestamp format. By using the NativeDateTimeResolver, it is possible to maintain Kind and perform faster serialization.

Be careful when copying buffers

MessagePackSerializer.Serialize returns byte[] in default. The final byte[] is copied from an internal buffer pool. That is an extra cost. You can use IBufferWriter<T> or the Stream API to write to buffers directly. If you want to use a buffer pool outside of the serializer, you should implement custom IBufferWriter<byte> or use an existing one such as Sequence<T> from the Nerdbank.Streams package.

During deserialization, MessagePackSerializer.Deserialize(ReadOnlyMemory<byte> buffer) is better than the Deserialize(Stream stream) overload. This is because the Stream API version starts by reading the data, generating a ReadOnlySequence<byte>, and only then starts the deserialization.

Choosing compression

Compression is generally effective when there is duplicate data. In MessagePack, arrays containing objects using string keys (Contractless) can be compressed efficiently because compression can be applied to many duplicate property names. Indexed keys compression is not as effectively compressed as string keys, but indexed keys are smaller in the first place.

This is some example benchmark performance data;

Serializer Mean DataSize
IntKey 2.941 us 469.00 B
IntKey(Lz4) 3.449 us 451.00 B
StringKey 4.340 us 1023.00 B
StringKey(Lz4) 5.469 us 868.00 B

IntKey(Lz4) is not as effectively compressed, but performance is still somewhat degraded. On the other hand, StringKey can be expected to have a sufficient effect on the binary size. However, this is just an example. Compression can be quite effective depending on the data, too, or have little effect other than slowing down your program. There are also cases in which well-compressible data exists in the values (such as long strings, e.g. containing HTML data with many repeated HTML tags). It is important to verify the actual effects of compression on a case by case basis.

Extensions

MessagePack for C# has extension points that enable you to provide optimal serialization support for custom types. There are official extension support packages.

Install-Package MessagePack.ReactiveProperty
Install-Package MessagePack.UnityShims
Install-Package MessagePack.AspNetCoreMvcFormatter

The MessagePack.ReactiveProperty package adds support for types of the ReactiveProperty library. It adds ReactiveProperty<>, IReactiveProperty<>, IReadOnlyReactiveProperty<>, ReactiveCollection<>, Unit serialization support. It is useful for save viewmodel state.

The MessagePack.UnityShims package provides shims for Unity's standard structs (Vector2, Vector3, Vector4, Quaternion, Color, Bounds, Rect, AnimationCurve, Keyframe, Matrix4x4, Gradient, Color32, RectOffset, LayerMask, Vector2Int, Vector3Int, RangeInt, RectInt, BoundsInt) and corresponding formatters. It can enable proper communication between servers and Unity clients.

After installation, extension packages must be enabled, by creating composite resolvers. Here is an example showing how to enable all extensions.

// Set extensions to default resolver.
var resolver = MessagePack.Resolvers.CompositeResolver.Create(
    // enable extension packages first
    ReactivePropertyResolver.Instance,
    MessagePack.Unity.Extension.UnityBlitResolver.Instance,
    MessagePack.Unity.UnityResolver.Instance,

    // finally use standard (default) resolver
    StandardResolver.Instance
);
var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);

// Pass options every time or set as default
MessagePackSerializer.DefaultOptions = options;

For configuration details, see: Extension Point section.

The MessagePack.AspNetCoreMvcFormatter is add-on for ASP.NET Core MVC's serialization to boost up performance. This is configuration sample.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddMvcOptions(option =>
    {
        option.OutputFormatters.Clear();
        option.OutputFormatters.Add(new MessagePackOutputFormatter(ContractlessStandardResolver.Options));
        option.InputFormatters.Clear();
        option.InputFormatters.Add(new MessagePackInputFormatter(ContractlessStandardResolver.Options));
    });
}

Other authors are creating extension packages, too.

  • MagicOnion - gRPC based HTTP/2 RPC Streaming Framework
  • MasterMemory - Embedded Readonly In-Memory Document Database

You can make your own extension serializers or integrate with frameworks. Let's create and share!

Experimental Features

MessagePack for C# has experimental features which provides you with very performant formatters. There is an official package.

Install-Package MessagePack.Experimental

For detailed information, see: Experimental.md

API
High-Level API (MessagePackSerializer)

The MessagePackSerializer class is the entry point of MessagePack for C#. Static methods make up the main API of MessagePack for C#.

API Description
Serialize<T> Serializes an object graph to a MessagePack binary blob. Async variant for Stream available. Non-generic overloads available.
Deserialize<T> Deserializes a MessagePack binary to an object graph. Async variant for Stream available. Non-generic overloads available.
SerializeToJson Serialize a MessagePack-compatible object graph to JSON instead of MessagePack. Useful for debugging.
ConvertToJson Convert MessagePack binary to JSON. Useful for debugging.
ConvertFromJson Convert JSON to a MessagePack binary.

The MessagePackSerializer.Typeless class offers most of the same APIs as above, but removes all type arguments from the API, forcing serialization to include the full type name of the root object. It uses the TypelessContractlessStandardResolver. Consider the result to be a .NET-specific MessagePack binary that isn't readily compatible with MessagePack deserializers in other runtimes.

MessagePack for C# fundamentally serializes using IBufferWriter<byte> and deserializes using ReadOnlySequence<byte> or Memory<byte>. Method overloads are provided to conveniently use it with common buffer types and the .NET Stream class, but some of these convenience overloads require copying buffers once and therefore have a certain overhead.

The high-level API uses a memory pool internally to avoid unnecessary memory allocation. If result size is under 64K, it allocates GC memory only for the return bytes.

Each serialize/deserialize method takes an optional MessagePackSerializerOptions parameter which can be used to specify a custom IFormatterResolver to use or to activate LZ4 compression support.

Multiple MessagePack structures on a single Stream

To deserialize a Stream that contains multiple consecutive MessagePack data structures, you can use the MessagePackStreamReader class to efficiently identify the ReadOnlySequence<byte> for each data structure and deserialize it. For example:

static async Task<List<T>> DeserializeListFromStreamAsync<T>(Stream stream, CancellationToken cancellationToken)
{
    var dataStructures = new List<T>();
    using (var streamReader = new MessagePackStreamReader(stream))
    {
        while (await streamReader.ReadAsync(cancellationToken) is ReadOnlySequence<byte> msgpack)
        {
            dataStructures.Add(MessagePackSerializer.Deserialize<T>(msgpack, cancellationToken: cancellationToken));
        }
    }

    return dataStructures;
}
Low-Level API (IMessagePackFormatter<T>)

The IMessagePackFormatter<T> interface is responsible for serializing a unique type. For example Int32Formatter : IMessagePackFormatter<Int32> represents Int32 MessagePack serializer.

public interface IMessagePackFormatter<T>
{
    void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
    T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
}

Many built-in formatters exists under MessagePack.Formatters. Your custom types are usually automatically supported with the built-in type resolvers that generate new IMessagePackFormatter<T> types on-the-fly using dynamic code generation. See our AOT code generation support for platforms that do not support this.

However, some types - especially those provided by third party libraries or the runtime itself - cannot be appropriately annotated, and contractless serialization would produce inefficient or even wrong results. To take more control over the serialization of such custom types, write your own IMessagePackFormatter<T> implementation. Here is an example of such a custom formatter implementation. Note its use of the primitive API that is described in the next section.

/// <summary>Serializes a <see cref="FileInfo" /> by its full path as a string.</summary>
public class FileInfoFormatter<T> : IMessagePackFormatter<FileInfo>
{
    public void Serialize(
      ref MessagePackWriter writer, FileInfo value, MessagePackSerializerOptions options)
    {
        if (value == null)
        {
            writer.WriteNil();
            return;
        }

        writer.WriteString(value.FullName);
    }

    public FileInfo Deserialize(
      ref MessagePackReader reader, MessagePackSerializerOptions options)
    {
        if (reader.TryReadNil())
        {
            return null;
        }

        options.Security.DepthStep(ref reader);

        var path = reader.ReadString();

        reader.Depth--;
        return new FileInfo(path);
    }
}

The DepthStep and Depth-- statements provide a level of security while deserializing untrusted data that might otherwise be able to execute a denial of service attack by sending MessagePack data that would deserialize into a very deep object graph leading to a StackOverflowException that would crash the process. This pair of statements should surround the bulk of any IMessagePackFormatter<T>.Deserialize method.

Important: A message pack formatter must read or write exactly one data structure. In the above example we just read/write a string. If you have more than one element to write out, you must precede it with a map or array header. You must read the entire map/array when deserializing. For example:

public class MySpecialObjectFormatter<T> : IMessagePackFormatter<MySpecialObject>
{
    public void Serialize(
      ref MessagePackWriter writer, MySpecialObject value, MessagePackSerializerOptions options)
    {
        if (value == null)
        {
            writer.WriteNil();
            return;
        }

        writer.WriteArrayHeader(2);
        writer.WriteString(value.FullName);
        writer.WriteString(value.Age);
    }

    public MySpecialObject Deserialize(
      ref MessagePackReader reader, MessagePackSerializerOptions options)
    {
        if (reader.TryReadNil())
        {
            return null;
        }

        options.Security.DepthStep(ref reader);

        string fullName = null;
        int age = 0;

        // Loop over *all* array elements independently of how many we expect,
        // since if we're serializing an older/newer version of this object it might
        // vary in number of elements that were serialized, but the contract of the formatter
        // is that exactly one data structure must be read, regardless.
        // Alternatively, we could check that the size of the array/map is what we expect
        // and throw if it is not.
        int count = reader.ReadArrayHeader();
        for (int i = 0; i < count; i++)
        {
            case 0:
                fullName = reader.ReadString();
                break;
            case 1:
                age = reader.ReadInt32();
                break;
            default:
                reader.Skip();
                break;
        }

        reader.Depth--;
        return new MySpecialObject(fullName, age);
    }
}

Your custom formatters must be discoverable via some IFormatterResolver. Learn more in our resolvers section.

You can see many other samples from builtin formatters.

Primitive API (MessagePackWriter, MessagePackReader)

The MessagePackWriter and MessagePackReader structs make up the lowest-level API. They read and write the primitives types defined in the MessagePack specification.

MessagePackReader

A MessagePackReader can efficiently read from ReadOnlyMemory<byte> or ReadOnlySequence<byte> without any allocations, except to allocate a new string as required by the ReadString() method. All other methods return either value structs or ReadOnlySequence<byte> slices for extensions/arrays. Reading directly from ReadOnlySequence<byte> means the reader can directly consume some modern high performance APIs such as PipeReader.

Method Description
Skip Advances the reader's position past the current value. If the value is complex (e.g. map, array) the entire structure is skipped.
Read* Read and return a value whose type is named by the method name from the current reader position. Throws if the expected type does not match the actual type. When reading numbers, the type need not match the binary-specified type exactly. The numeric value will be coerced into the desired type or throw if the integer type is too small for a large value.
TryReadNil Advances beyond the current value if the current value is nil and returns true; otherwise leaves the reader's position unchanged and returns false.
ReadBytes Returns a slice of the input sequence representing the contents of a byte[], and advances the reader.
ReadStringSequence Returns a slice of the input sequence representing the contents of a string without decoding it, and advances the reader.
Clone Creates a new MessagePackReader with the specified input sequence and the same settings as the original reader.
CreatePeekReader Creates a new reader with the same position as this one, allowing the caller to "read ahead" without impacting the original reader's position.
NextCode Reads the low-level MessagePack byte that describes the type of the next value. Does not advance the reader. See MessagePack format of first byte. Its static class has ToMessagePackType and ToFormatName utility methods. MessagePackRange means Min-Max fix range of MessagePack format.
NextMessagePackType Describes the NextCode value as a higher level category. Does not advance the reader. See MessagePack spec of source types.
(others) Other methods and properties as described by the .xml doc comment file and Intellisense.

The MessagePackReader is capable of automatically interpreting both the old and new MessagePack spec.

MessagePackWriter

A MessagePackWriter writes to a given instance of IBufferWriter<byte>. Several common implementations of this exist, allowing zero allocations and minimal buffer copies while writing directly to several I/O APIs including PipeWriter.

The MessagePackWriter writes the new MessagePack spec by default, but can write MessagePack compatible with the old spec by setting the OldSpec property to true.

Method Description
Clone Creates a new MessagePackWriter with the specified underlying IBufferWriter<byte> and the same settings as the original writer.
Flush Writes any buffered bytes to the underlying IBufferWriter<byte>.
WriteNil Writes the MessagePack equivalent of .NET's null value.
Write Writes any MessagePack primitive value in the most compact form possible. Has overloads for every primitive type defined by the MessagePack spec.
Write*IntType* Writes an integer value in exactly the MessagePack type specified, even if a more compact format exists.
WriteMapHeader Introduces a map by specifying the number of key=value pairs it contains.
WriteArrayHeader Introduces an array by specifying the number of elements it contains.
WriteExtensionFormat Writes the full content of an extension value including length, type code and content.
WriteExtensionFormatHeader Writes just the header (length and type code) of an extension value.
WriteRaw Copies the specified bytes directly to the underlying IBufferWriter<byte> without any validation.
(others) Other methods and properties as described by the .xml doc comment file and Intellisense.

DateTime is serialized to MessagePack Timestamp format, it serialize/deserialize UTC and loses Kind info and requires that MessagePackWriter.OldSpec == false. If you use the NativeDateTimeResolver, DateTime values will be serialized using .NET's native Int64 representation, which preserves Kind info but may not be interoperable with non-.NET platforms.

Main Extension Point (IFormatterResolver)

An IFormatterResolver is storage of typed serializers. The MessagePackSerializer API accepts a MessagePackSerializerOptions object which specifies the IFormatterResolver to use, allowing customization of the serialization of complex types.

Resolver Name Description
BuiltinResolver Builtin primitive and standard classes resolver. It includes primitive(int, bool, string...) and there nullable, array and list. and some extra builtin types(Guid, Uri, BigInteger, etc...).
StandardResolver Composited resolver. It resolves in the following order builtin -> attribute -> dynamic enum -> dynamic generic -> dynamic union -> dynamic object -> dynamic object fallback. This is the default of MessagePackSerializer.
ContractlessStandardResolver Composited StandardResolver(except dynamic object fallback) -> DynamicContractlessObjectResolver -> DynamicObjectTypeFallbackResolver. It enables contractless serialization.
StandardResolverAllowPrivate Same as StandardResolver but allow serialize/deserialize private members.
ContractlessStandardResolverAllowPrivate Same as ContractlessStandardResolver but allow serialize/deserialize private members.
PrimitiveObjectResolver MessagePack primitive object resolver. It is used fallback in object type and supports bool, char, sbyte, byte, short, int, long, ushort, uint, ulong, float, double, DateTime, string, byte[], ICollection, IDictionary.
DynamicObjectTypeFallbackResolver Serialize is used type in from object type, deserialize is used PrimitiveObjectResolver.
AttributeFormatterResolver Get formatter from [MessagePackFormatter] attribute.
CompositeResolver Composes several resolvers and/or formatters together in an ordered list, allowing reuse and overriding of behaviors of existing resolvers and formatters.
NativeDateTimeResolver Serialize by .NET native DateTime binary format. It keeps DateTime.Kind that loses by standard(MessagePack timestamp) format.
NativeGuidResolver Serialize by .NET native Guid binary representation. It is faster than standard(string) representation.
NativeDecimalResolver Serialize by .NET native decimal binary representation. It is faster than standard(string) representation.
DynamicEnumResolver Resolver of enum and there nullable, serialize there underlying type. It uses dynamic code generation to avoid boxing and boostup performance serialize there name.
DynamicEnumAsStringResolver Resolver of enum and there nullable. It uses reflection call for resolve nullable at first time.
DynamicGenericResolver Resolver of generic type(Tuple<>, List<>, Dictionary<,>, Array, etc). It uses reflection call for resolve generic argument at first time.
DynamicUnionResolver Resolver of interface marked by UnionAttribute. It uses dynamic code generation to create dynamic formatter.
DynamicObjectResolver Resolver of class and struct made by MessagePackObjectAttribute. It uses dynamic code generation to create dynamic formatter.
DynamicContractlessObjectResolver Resolver of all classes and structs. It does not needs MessagePackObjectAttribute and serialized key as string(same as marked [MessagePackObject(true)]).
DynamicObjectResolverAllowPrivate Same as DynamicObjectResolver but allow serialize/deserialize private members.
DynamicContractlessObjectResolverAllowPrivate Same as DynamicContractlessObjectResolver but allow serialize/deserialize private members.
TypelessObjectResolver Used for object, embed .NET type in binary by ext(100) format so no need to pass type in deserialization.
TypelessContractlessStandardResolver Composited resolver. It resolves in the following order nativedatetime -> builtin -> attribute -> dynamic enum -> dynamic generic -> dynamic union -> dynamic object -> dynamiccontractless -> typeless. This is the default of MessagePackSerializer.Typeless

Each instance of MessagePackSerializer accepts only a single resolver. Most object graphs will need more than one for serialization, so composing a single resolver made up of several is often required, and can be done with the CompositeResolver as shown below:

// Do this once and store it for reuse.
var resolver = MessagePack.Resolvers.CompositeResolver.Create(
    // resolver custom types first
    ReactivePropertyResolver.Instance,
    MessagePack.Unity.Extension.UnityBlitResolver.Instance,
    MessagePack.Unity.UnityResolver.Instance,

    // finally use standard resolver
    StandardResolver.Instance
);
var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);

// Each time you serialize/deserialize, specify the options:
byte[] msgpackBytes = MessagePackSerializer.Serialize(myObject, options);
T myObject2 = MessagePackSerializer.Deserialize<MyObject>(msgpackBytes, options);

A resolver can be set as default with MessagePackSerializer.DefaultOptions = options, but WARNING: When developing an application where you control all MessagePack-related code it may be safe to rely on this mutable static to control behavior. For all other libraries or multi-purpose applications that use MessagePackSerializer you should explicitly specify the MessagePackSerializerOptions to use with each method invocation to guarantee your code behaves as you expect even when sharing an AppDomain or process with other MessagePack users that may change this static property.

Here is sample of use DynamicEnumAsStringResolver with DynamicContractlessObjectResolver (It is Json.NET-like lightweight setting.)

// composite same as StandardResolver
var resolver = MessagePack.Resolvers.CompositeResolver.Create(
    MessagePack.Resolvers.BuiltinResolver.Instance,
    MessagePack.Resolvers.AttributeFormatterResolver.Instance,

    // replace enum resolver
    MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance,

    MessagePack.Resolvers.DynamicGenericResolver.Instance,
    MessagePack.Resolvers.DynamicUnionResolver.Instance,
    MessagePack.Resolvers.DynamicObjectResolver.Instance,

    MessagePack.Resolvers.PrimitiveObjectResolver.Instance,

    // final fallback(last priority)
    MessagePack.Resolvers.DynamicContractlessObjectResolver.Instance
);

If you want to make an extension package, you should write both a formatter and resolver for easier consumption. Here is sample of a resolver:

public class SampleCustomResolver : IFormatterResolver
{
    // Resolver should be singleton.
    public static readonly IFormatterResolver Instance = new SampleCustomResolver();

    private SampleCustomResolver()
    {
    }

    // GetFormatter<T>'s get cost should be minimized so use type cache.
    public IMessagePackFormatter<T> GetFormatter<T>()
    {
        return FormatterCache<T>.Formatter;
    }

    private static class FormatterCache<T>
    {
        public static readonly IMessagePackFormatter<T> Formatter;

        // generic's static constructor should be minimized for reduce type generation size!
        // use outer helper method.
        static FormatterCache()
        {
            Formatter = (IMessagePackFormatter<T>)SampleCustomResolverGetFormatterHelper.GetFormatter(typeof(T));
        }
    }
}

internal static class SampleCustomResolverGetFormatterHelper
{
    // If type is concrete type, use type-formatter map
    static readonly Dictionary<Type, object> formatterMap = new Dictionary<Type, object>()
    {
        {typeof(FileInfo), new FileInfoFormatter()}
        // add more your own custom serializers.
    };

    internal static object GetFormatter(Type t)
    {
        object formatter;
        if (formatterMap.TryGetValue(t, out formatter))
        {
            return formatter;
        }

        // If target type is generics, use MakeGenericType.
        if (t.IsGeneric && t.GetGenericTypeDefinition() == typeof(ValueTuple<,>))
        {
            return Activator.CreateInstance(typeof(ValueTupleFormatter<,>).MakeGenericType(t.GenericTypeArguments));
        }

        // If type can not get, must return null for fallback mechanism.
        return null;
    }
}
MessagePackFormatterAttribute

MessagePackFormatterAttribute is a lightweight extension point of class, struct, interface, enum and property/field. This is like Json.NET's JsonConverterAttribute. For example, serialize private field, serialize x10 formatter.

[MessagePackFormatter(typeof(CustomObjectFormatter))]
public class CustomObject
{
    string internalId;

    public CustomObject()
    {
        this.internalId = Guid.NewGuid().ToString();
    }

    // serialize/deserialize internal field.
    class CustomObjectFormatter : IMessagePackFormatter<CustomObject>
    {
        public void Serialize(ref MessagePackWriter writer, CustomObject value, MessagePackSerializerOptions options)
        {
            options.Resolver.GetFormatterWithVerify<string>().Serialize(ref writer, value.internalId, options);
        }

        public CustomObject Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
        {
            var id = options.Resolver.GetFormatterWithVerify<string>().Deserialize(ref reader, options);
            return new CustomObject { internalId = id };
        }
    }
}

// per field, member

public class Int_x10Formatter : IMessagePackFormatter<int>
{
    public int Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
    {
        return reader.ReadInt32() * 10;
    }

    public void Serialize(ref MessagePackWriter writer, int value, MessagePackSerializerOptions options)
    {
        writer.WriteInt32(value * 10);
    }
}

[MessagePackObject]
public class MyClass
{
    // You can attach custom formatter per member.
    [Key(0)]
    [MessagePackFormatter(typeof(Int_x10Formatter))]
    public int MyProperty1 { get; set; }
}

Formatter is retrieved by AttributeFormatterResolver, it is included in StandardResolver.

IgnoreFormatter

IgnoreFormatter<T> is lightweight extension point of class and struct. If there exists types that can't be serialized, you can register IgnoreFormatter<T> that serializes those to nil/null.

// CompositeResolver can set custom formatter.
var resolver = MessagePack.Resolvers.CompositeResolver.Create(
    new IMessagePackFormatter[]
    {
        // for example, register reflection infos (can not serialize)
        new IgnoreFormatter<MethodBase>(),
        new IgnoreFormatter<MethodInfo>(),
        new IgnoreFormatter<PropertyInfo>(),
        new IgnoreFormatter<FieldInfo>()
    },
    new IFormatterResolver[]
    {
        ContractlessStandardResolver.Instance
    });
Reserved Extension Types

MessagePack for C# already used some MessagePack extension type codes, be careful to use same ext code.

Code Type Use by
-1 DateTime MessagePack-spec reserved for timestamp
30 Vector2[] for Unity, UnsafeBlitFormatter
31 Vector3[] for Unity, UnsafeBlitFormatter
32 Vector4[] for Unity, UnsafeBlitFormatter
33 Quaternion[] for Unity, UnsafeBlitFormatter
34 Color[] for Unity, UnsafeBlitFormatter
35 Bounds[] for Unity, UnsafeBlitFormatter
36 Rect[] for Unity, UnsafeBlitFormatter
37 Int[] for Unity, UnsafeBlitFormatter
38 Float[] for Unity, UnsafeBlitFormatter
39 Double[] for Unity, UnsafeBlitFormatter
98 All MessagePackCompression.Lz4BlockArray
99 All MessagePackCompression.Lz4Block
100 object TypelessFormatter
Unity support

Unity lowest supported version is 2018.3, API Compatibility Level supports both .NET 4.x and .NET Standard 2.0.

You can install the unitypackage from the releases page. If your build targets .NET Framework 4.x and runs on mono, you can use it as is. But if your build targets IL2CPP, you can not use Dynamic***Resolver, so it is required to use pre-code generation. Please see pre-code generation section.

MessagePack for C# includes some additional System.*.dll libraries that originally provides in NuGet. They are located under Plugins. If other packages use these libraries (e.g. Unity Collections package using System.Runtime.CompilerServices.Unsafe.dll), to avoid conflicts, please delete the DLL under Plugins.

Currently CompositeResolver.Create does not work on IL2CPP, so it is recommended to use StaticCompositeResolver.Instance.Register instead.

In Unity, MessagePackSerializer can serialize Vector2, Vector3, Vector4, Quaternion, Color, Bounds, Rect, AnimationCurve, Keyframe, Matrix4x4, Gradient, Color32, RectOffset, LayerMask, Vector2Int, Vector3Int, RangeInt, RectInt, BoundsInt and their nullable, array and list types with the built-in extension UnityResolver. It is included in StandardResolver by default.

MessagePack for C# has an additional unsafe extension. UnsafeBlitResolver is special resolver for extremely fast but unsafe serialization/deserialization of struct arrays.

image

x20 faster Vector3[] serialization than native JsonUtility. If use UnsafeBlitResolver, serialization uses a special format (ext:typecode 30~39) for Vector2[], Vector3[], Quaternion[], Color[], Bounds[], Rect[]. If use UnityBlitWithPrimitiveArrayResolver, it supports int[], float[], double[] too. This special feature is useful for serializing Mesh (many Vector3[]) or many transform positions.

If you want to use unsafe resolver, register UnityBlitResolver or UnityBlitWithPrimitiveArrayResolver.

Here is sample of configuration.

StaticCompositeResolver.Instance.Register(
    MessagePack.Unity.UnityResolver.Instance,
    MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance,
    MessagePack.Resolvers.StandardResolver.Instance
);

var options = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance);
MessagePackSerializer.DefaultOptions = options;

The MessagePack.UnityShims NuGet package is for .NET server-side serialization support to communicate with Unity. It includes shims for Vector3 etc and the Safe/Unsafe serialization extension.

If you want to share a class between Unity and a server, you can use SharedProject or Reference as Link or a glob reference (with LinkBase), etc. Anyway, you need to share at source-code level. This is a sample project structure using a glob reference (recommended).

  • ServerProject(.NET 4.6/.NET Core/.NET Standard)
    • [<Compile Include="..\UnityProject\Assets\Scripts\Shared\**\*.cs" LinkBase="Shared" />]
    • [MessagePack]
    • [MessagePack.UnityShims]
  • UnityProject
    • [Concrete SharedCodes]
    • [MessagePack](not dll/NuGet, use MessagePack.Unity.unitypackage's sourcecode)
AOT Code Generation (support for Unity/Xamarin)

By default, MessagePack for C# serializes custom objects by generating IL on the fly at runtime to create custom, highly tuned formatters for each type. This code generation has a minor upfront performance cost. Because strict-AOT environments such as Xamarin and Unity IL2CPP forbid runtime code generation, MessagePack provides a way for you to run a code generator ahead of time as well.

Note: When using Unity, dynamic code generation only works when targeting .NET Framework 4.x + mono runtime. For all other Unity targets, AOT is required.

If you want to avoid the upfront dynamic generation cost or you need to run on Xamarin or Unity, you need AOT code generation. mpc (MessagePackCompiler) is the code generator of MessagePack for C#. mpc uses Roslyn to analyze source code.

First of all, mpc requires .NET Core 3 Runtime. The easiest way to acquire and run mpc is as a dotnet tool.

dotnet tool install --global MessagePack.Generator

Installing it as a local tool allows you to include the tools and versions that you use in your source control system. Run these commands in the root of your repo:

dotnet new tool-manifest
dotnet tool install MessagePack.Generator

Check in your .config\dotnet-tools.json file. On another machine you can "restore" your tool using the dotnet tool restore command.

Once you have the tool installed, simply invoke using dotnet mpc within your repo:

dotnet mpc --help

Alternatively, you can download mpc from the releases page, that includes platform native binaries (that don't require a separate dotnet runtime).

Usage: mpc [options...]

Options:
  -i, -input <String>                                Input path to MSBuild project file or the directory containing Unity source files. (Required)
  -o, -output <String>                               Output file path(.cs) or directory(multiple generate file). (Required)
  -c, -conditionalSymbol <String>                    Conditional compiler symbols, split with ','. (Default: null)
  -r, -resolverName <String>                         Set resolver name. (Default: GeneratedResolver)
  -n, -namespace <String>                            Set namespace root name. (Default: MessagePack)
  -m, -useMapMode <Boolean>                          Force use map mode serialization. (Default: False)
  -ms, -multipleIfDirectiveOutputSymbols <String>    Generate #if-- files by symbols, split with ','. (Default: null)

mpc targets C# code with [MessagePackObject] or [Union] annotations.

// Simple Sample:
dotnet mpc -i "..\src\Sandbox.Shared.csproj" -o "MessagePackGenerated.cs"

// Use force map simulate DynamicContractlessObjectResolver
dotnet mpc -i "..\src\Sandbox.Shared.csproj" -o "MessagePackGenerated.cs" -m

By default, mpc generates the resolver as MessagePack.Resolvers.GeneratedResolver and formatters asMessagePack.Formatters.*.

Here is the full sample code to register a generated resolver in Unity.

using MessagePack;
using MessagePack.Resolvers;
using UnityEngine;

public class Startup
{
    static bool serializerRegistered = false;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void Initialize()
    {
        if (!serializerRegistered)
        {
            StaticCompositeResolver.Instance.Register(
                 MessagePack.Resolvers.GeneratedResolver.Instance,
                 MessagePack.Resolvers.StandardResolver.Instance
            );

            var option = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance);

            MessagePackSerializer.DefaultOptions = option;
            serializerRegistered = true;
        }
    }

#if UNITY_EDITOR


    [UnityEditor.InitializeOnLoadMethod]
    static void EditorInitialize()
    {
        Initialize();
    }

#endif
}

In Unity, you can use MessagePack CodeGen windows at Windows -> MessagePack -> CodeGenerator.

Install the .NET Core runtime, install mpc (as a .NET Core Tool as described above), and execute dotnet mpc. Currently this tool is experimental so please tell me your opinion.

In Xamarin, you can install the the MessagePack.MSBuild.Tasks NuGet package into your projects to pre-compile fast serialization code and run in environments where JIT compilation is not allowed.

RPC

MessagePack advocated MessagePack RPC, but work on it has stopped and it is not widely used.

MagicOnion

I've created a gRPC based MessagePack HTTP/2 RPC streaming framework called MagicOnion. gRPC usually communicates with Protocol Buffers using IDL. But MagicOnion uses MessagePack for C# and does not need IDL. When communicating C# to C#, schemaless (or rather C# classes as schema) is better than using IDL.

StreamJsonRpc

The StreamJsonRpc library is based on JSON-RPC and includes a pluggable formatter architecture and as of v2.3 includes MessagePack support.

How to build

See our contributor's guide.

Author Info

Yoshifumi Kawai (a.k.a. neuecc) is a software developer in Japan. He is the Director/CTO at Grani, Inc. Grani is a mobile game developer company in Japan and well known for using C#. He is awarding Microsoft MVP for Visual C# since 2011. He is known as the creator of UniRx (Reactive Extensions for Unity)

jonathonl/goodform https://github.com/jonathonl/goodform

GoodForm

Form validation library. Includes MsgPack and JSON serializer/deserializer.

Building

GoodForm uses std::any, which requires c++17. When c++17 is not available, boost::any is expected and will be installed automatically when using cget.

cd goodform
cget install -f ./requirements.txt                      # Install dependencies locally.
mkdir build && cd build                                 # Create out of source build directory.
cmake -DCMAKE_TOOLCHAIN_FILE=../cget/cget/cget.cmake .. # Configure project with dependency paths.
make
MsgPack Usage
std::stringstream ss;
goodform::any var, var2;
var = goodform::object
  {
    {"compact", true},
    {"schema", 0}
  };

goodform::msgpack::serialize(var, ss);
goodform::msgpack::deserialize(ss, var2);

goodform::form form(var2);

struct
{
  bool compact;
  std::int32_t schema;
} mpack;

mpack.compact = form.at("compact").boolean().val();
mpack.schema = form.at("schema").int32().val();

if (form.is_good())
{
  std::cout << "{ \"compact\": " << std::boolalpha << mpack.compact << ", \"schema\": " << mpack.schema << " }" << std::endl;
}
JSON Usage
goodform::any var;
std::stringstream ss;
ss << "{" << std::endl
  << "\"first_name\":\"John\", // This is a comment" << std::endl
  << "\"last_name\":\"Smith\", " << std::endl
  << "\"age\": 23," << std::endl
  << "\"gpa\": 4.0," << std::endl
  << "\"email\":\"[email protected]\"," << std::endl
  << "\"password_hash\":\"5f4dcc3b5aa765d61d8327deb882cf99\"," << std::endl
  << "\"interests\": [\"sailing\",\"swimming\",\"yoga\"]" << std::endl
  << "}" << std::endl;

goodform::json::deserialize(ss, var);

goodform::form form(var);

struct
{
  std::string first_name;
  std::string last_name;
  std::uint8_t age;
  float gpa;
  std::string email;
  std::string password_hash;
  bool subscribe_to_email_marketing;
  std::list<std::string> interests;
} form_data;


form_data.first_name = form.at("first_name").string().match(std::regex("^[a-zA-Z ]{1,64}$")).val();
form_data.last_name = form.at("last_name").string().match(std::regex("^[a-zA-Z ]{1,64}$")).val();
form_data.age = form.at("age").uint8().val();
form_data.gpa = form.at("gpa").float32().gte(0).lte(4.0).val();
form_data.email = form.at("email").string().match(std::regex("^.{3,256}$")).val();
form_data.password_hash = form.at("password_hash").string().match(std::regex("^[a-fA-F0-9]{32}$")).val();
form_data.subscribe_to_email_marketing = form.at("subscribe_to_email_marketing", true).boolean().val(); // Optional field defaults to true.

form.at("interests").array().for_each([&form_data](goodform::sub_form& sf, std::size_t i)
{
  form_data.interests.push_back(sf.string().val());
});

if (form.is_good())
{
  // Use validated form_data.
}
else
{
  // Handle error.
}

ar90n/msgpack11 https://github.com/ar90n/msgpack11

Build Status

What is msgpack11 ?

msgpack11 is a tiny MsgPack library for C++11, providing MsgPack parsing and serialization.
This library is inspired by json11.
The API of msgpack11 is designed to be similar with json11.

Installation
  • Using CMake

      git clone [email protected]:ar90n/msgpack11.git
      mkdir build
      cd build
      cmake ../msgpack11
      make && make install
    
  • Using Buck

      git clone [email protected]:ar90n/msgpack11.git
      cd msgpack11
      buck build :msgpack11
    
Example
MsgPack my_msgpack = MsgPack::object {
    { "key1", "value1" },
    { "key2", false },
    { "key3", MsgPack::array { 1, 2, 3 } },
};

//access to elements
std::cout << my_msgpack["key1"].string_value();

//serialize
std::string msgpack_bytes = my_msgpack.dump();

//deserialize
std::string err;
MsgPack des_msgpack = MsgPack::parse(msgpack_bytes, err);

There are more specific examples in example.cpp. Please see it.

Benchmark

Derived from schemaless-benchmarks

Library Binary size time[ms] @ Smallest time[ms] @ Small time[ms] @ Medium time[ms] @ Large time[ms] @ Largest
msgpack-c-pack(v2.1.4) 6649 0.55 2.38 43.22 711.75 8748.20
msgpack-c-unpack(v2.1.4) 21804 1.34 6.00 83.09 714.64 11192.32
msgpack11-pack(v0.0.9) 99844 20.80 130.04 1063.24 10466.65 136640.99
msgpack11-unpack(v0.0.9) 99460 13.31 92.54 786.73 7345.43 99119.56

CPU : 2.6 GHz Intel Core i7
Memory : 16 GB 2133 MHz LPDDR3
Git revision : 6f6b4302b68b3c88312eb24367418b7fce81298c

Feature
  • Support serialization and deserialization.
Acknowledgement
License

This software is released under the MIT License, see LICENSE.txt.

msgpack/msgpack-c https://github.com/msgpack/msgpack-c

msgpack for C/C++

It's like JSON but smaller and faster.

Overview

MessagePack is an efficient binary serialization format, which lets you exchange data among multiple languages like JSON, except that it's faster and smaller. Small integers are encoded into a single byte and short strings require only one extra byte in addition to the strings themselves.

C Library

See c_master

C++ Library

See cpp_master

Documentation

You can get additional information including the tutorial on the wiki.

Contributing

msgpack-c is developed on GitHub at msgpack/msgpack-c. To report an issue or send a pull request, use the issue tracker.

Here's the list of great contributors.

License

msgpack-c is licensed under the Boost Software License, Version 1.0. See the LICENSE_1_0.txt file for details.

clwi/CWPack https://github.com/clwi/CWPack

CWPack

CWPack is a lightweight and yet complete implementation of the MessagePack serialization format version 5. It also supports the Timestamp extension type.

Excellent Performance

Together with MPack, CWPack is the fastest open-source messagepack implementation. Both totally outperform CMP and msgpack-c

Design

CWPack does no memory allocations and no file handling in its basic setup. All that is done outside of CWPack. Example extensions are included.

CWPack is working against memory buffers. User defined handlers are called when buffers are filled up (packing) or needs refill (unpack).

Containers (arrays, maps) are read/written in parts, first the item containing the size and then the contained items one by one. Exception to this is the cw_skip_items function which skip whole containers.

Example

Pack and unpack example from the MessagePack home page:

void example (void)
{
    cw_pack_context pc;
    char buffer[20];
    cw_pack_context_init (&pc, buffer, 20, 0);

    cw_pack_map_size (&pc, 2);
    cw_pack_str (&pc, "compact", 7);
    cw_pack_boolean (&pc, true);
    cw_pack_str (&pc, "schema", 6);
    cw_pack_unsigned (&pc, 0);

    if (pc.return_code != CWP_RC_OK)  ERROR;
    int length = pc.current - pc.start;
    if (length != 18) ERROR;

    cw_unpack_context uc;
    cw_unpack_context_init (&uc, pc.start, length, 0);

    if (cw_unpack_next_map_size(&uc) != 2) ERROR;
    if (cw_unpack_next_str_lengh(&uc) != 7) ERROR;
    if (strncmp("compact", uc.item.as.str.start, 7)) ERROR;
    if (cw_unpack_next_boolean(&uc) != true) ERROR;
    if (cw_unpack_next_str_lengh(&uc) != 6) ERROR;
    if (strncmp("schema", uc.item.as.str.start, 6)) ERROR;
    if (cw_unpack_next_signed32(&uc) != 0) ERROR;

    if (uc.return_code != CWP_RC_OK)  ERROR;
    cw_unpack_next(&uc);
    if (uc.return_code != CWP_RC_END_OF_INPUT)  ERROR;
}

In the examples folder there are more examples.

Backward compatibility

CWPack may be run in compatibility mode. It affects only packing; EXT & TIMESTAMP is considered illegal, BIN are transformed to STR and generation of STR8 is supressed.

Error handling

When an error is detected in a context, the context is stopped and all future calls to that context are immediatly returned without any actions. Thus it is possible to make some calls and delay error checking until all calls are done.

CWPack does not check for illegal values (e.g. in STR for illegal unicode characters).

Build

CWPack consists of a single src file and three header files. It is written in strict ansi C and the files are together ~ 1.4K lines. No separate build is neccesary, just include the files in your own build.

CWPack has no dependencies to other libraries.

Test

Included in the test folder are a module test and a performance test and shell scripts to run them.

Objective-C

CWPack also contains an Objective-C interface. The MessagePack home page example would look as:

CWPackContext *pc = [CWPackContext newWithContext:my_cw_pack_context];
[pc packObject:@{@"compact":@YES, @"schema":@0}];

CWUnpackContext *uc = [CWUnpackContext newWithContext:my_cw_unpack_context];
NSDictionary *dict = [uc unpackNextObject];

edma2/clojure-msgpack https://github.com/edma2/clojure-msgpack

clojure-msgpack

clojure-msgpack is a lightweight and simple library for converting between native Clojure data structures and MessagePack byte formats. clojure-msgpack only depends on Clojure itself; it has no third-party dependencies.

Installation

Clojars Project Build Status

Usage
Basic
  • pack: Serialize object as a sequence of java.lang.Bytes.
  • unpack Deserialize bytes as a Clojure object.
(require '[msgpack.core :as msg])
(require 'msgpack.clojure-extensions)

(msg/pack {:compact true :schema 0})
; => #<byte[] [[email protected]>

(msg/unpack (msg/pack {:compact true :schema 0}))
; => {:schema 0, :compact true}
Streaming

clojure-msgpack provides a streaming API for situations where it is more convenient or efficient to work with byte streams instead of fixed byte arrays (e.g. size of object is not known ahead of time).

The streaming counterpart to msgpack.core/pack is msgpack.core/pack-stream which returns nil and accepts either java.io.OutputStream or java.io.DataOutput as an additional argument.

msgpack.core/unpack is in "streaming mode" when the argument is of type java.io.DataInput or java.io.InputStream.

(use 'clojure.java.io)

(with-open [s (output-stream "test.dat")]
  (msg/pack-stream {:compact true :schema 0} s))

(with-open [s (input-stream "test.dat")] (msg/unpack s))
; => {:schema 0, :compact true}
Core types
Clojure MessagePack
nil Nil
java.lang.Boolean Boolean
java.lang.Byte Integer
java.lang.Short Integer
java.lang.Integer Integer
java.lang.Long Integer
java.lang.BigInteger Integer
clojure.lang.BigInt Integer
java.lang.Float Float
java.lang.Double Float
java.math.BigDecimal Float
java.lang.String String
clojure.lang.Sequential Array
clojure.lang.IPersistentMap Map
msgpack.core.Ext Extended

Serializing a value of unrecognized type will fail with IllegalArgumentException. See Application types if you want to register your own types.

Clojure types

Some native Clojure types don't have an obvious MessagePack counterpart. We can serialize them as Extended types. To enable automatic conversion of these types, load the clojure-extensions library.

Clojure MessagePack
clojure.lang.Keyword Extended (type = 3)
clojure.lang.Symbol Extended (type = 4)
java.lang.Character Extended (type = 5)
clojure.lang.Ratio Extended (type = 6)
clojure.lang.IPersistentSet Extended (type = 7)

With msgpack.clojure-extensions:

(require 'msgpack.clojure-extensions)
(msg/pack :hello)
; => #<byte[] [[email protected]>

Without msgpack.clojure-extensions:

(msg/pack :hello)
; => IllegalArgumentException No implementation of method: :pack-stream of
; protocol: #'msgpack.core/Packable found for class: clojure.lang.Keyword
; clojure.core/-cache-protocol-fn (core _deftype.clj:544)
Application types

You can also define your own Extended types with extend-msgpack.

(require '[msgpack.macros :refer [extend-msgpack]])

(defrecord Person [name])

(extend-msgpack
  Person
  100
  [p] (.getBytes (:name p))
  [bytes] (->Person (String. bytes)))

(msg/unpack (msg/pack [(->Person "bob") 5 "test"]))
; => (#user.Person{:name "bob"} 5 "test")
Options

All pack and unpack functions take an optional map of options:

  • :compatibility-mode Serialize/deserialize strings and bytes using the raw-type defined here: https://github.com/msgpack/msgpack/blob/master/spec-old.md

    Note: No error is thrown if an unpacked value is reserved under the old spec but defined under the new spec. We always deserialize something if we can regardless of compatibility-mode.

(msg/pack (byte-array (byte 9)) {:compatibility-mode true})
License

clojure-msgpack is MIT licensed. See the included LICENSE file for more details.

crystal-community/msgpack-crystal https://github.com/crystal-community/msgpack-crystal

MessagePack

Build Status

MessagePack implementation in Crystal.

Installation

Add this to your application's shard.yml:

dependencies:
  msgpack:
    github: crystal-community/msgpack-crystal
Usage
require "msgpack"

class Location
  include MessagePack::Serializable

  property lat : Float64
  property lng : Float64
end

class House
  include MessagePack::Serializable

  property address : String
  property location : Location?
end

house = House.from_msgpack({address: "Road12", location: {lat: 12.3, lng: 34.5}}.to_msgpack)
p house
# => <House:0x1b06de0 @address="Road12", @location=#<Location:0x1b06dc0 @lat=12.3, @lng=34.5>>

p house.to_msgpack
# => Bytes[130, 167, 97, 100, 100, 114, 101, 115, 115, 166, 82, 111, 97, 100, ...

house.address = "Something"
house = House.from_msgpack(house.to_msgpack)
p house
# => #<House:0x13f0d80 @address="Something", @location=#<Location:0x13f0d60 @lat=12.3, @lng=34.5>>

house = House.from_msgpack({"address" => "Crystal Road 1234"}.to_msgpack)
p house
# => <House:0x1b06d80 @address="Crystal Road 1234", @location=nil>
More Examples

examples

Msgpack-RPC

implemented by simple_rpc shard

Copyright

Copyright 2015 Benoist Claassen

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

steakknife/msgpack.cr https://github.com/steakknife/msgpack.cr

msgpack.cr

A low-level msgpack codec for Crystal

TODO
  • More specs
  • Mapping
Installation

Add this to your application's shard.yml:

dependencies:
  msgpack:
    github: steakknife/msgpack.cr
Usage
require "msgpack"

1.to_msgpack # => Slice[210, 0, 0, 0, 1]

# write 2_i32 to file foo.msgpack
File.open("foo.msgpack", "w") { |f| f.write(2.to_msgpack) }
Extending
Encoding

Any type can become encodable by including Msgpack::Encodable and defining to_msgpack(io : IO)

Decoding

Any type can become decodable by following the example

Development
Run tests
crystal spec
Alternate Implementations
Contributing
  1. Fork it ( https://github.com/steakknife/msgpack.cr/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request
Contributors
License

MIT

Copyright

2016 (c) Copyright Barry Allard

msgpack/msgpack-d https://github.com/msgpack/msgpack-d

Build Status

MessagePack for D

MessagePack is a binary-based JSON-like serialization library.

MessagePack for D is a pure D implementation of MessagePack.

Features
  • Small size and High performance
  • Zero copy serialization / deserialization
  • Streaming deserializer for non-contiguous IO situation
  • Supports D features (Ranges, Tuples, real type)

Note: The real type is only supported in D. Don't use the real type when communicating with other programming languages. Note that Unpacker will raise an exception if a loss of precision occurs.

Current Limitations
  • No circular references support
  • If you want to use the LDC compiler, you need at least version 0.15.2 beta2
Install

Use dub to add it as a dependency:

% dub install msgpack-d
Usage

Example code can be found in the example directory.

The documentation can be found here

pack / unpack

msgpack-d is very simple to use. Use pack for serialization, and unpack for deserialization:

import std.file;
import msgpack;

struct S { int x; float y; string z; }

void main()
{
    S input = S(10, 25.5, "message");

    // serialize data
    ubyte[] inData = pack(input);

    // write data to a file
    write("file.dat", inData);

    // read data from a file
    ubyte[] outData = cast(ubyte[])read("file.dat");

    // unserialize the data
    S target = outData.unpack!S();

    // verify data is the same
    assert(target.x == input.x);
    assert(target.y == input.y);
    assert(target.z == input.z);
}
Feature: Skip serialization/deserialization of a specific field.

Use the @nonPacked attribute:

struct User
{
    string name;
    @nonPacked int level;  // pack / unpack will ignore the 'level' field
}
Feature: Use your own serialization/deserialization routines for custom class and struct types.

msgpack-d provides the functions registerPackHandler / registerUnpackHandler to allow you to use custom routines during the serialization or deserialization of user-defined class and struct types. This feature is especially useful when serializing a derived class object when that object is statically typed as a base class object.

For example:

class Document { }
class XmlDocument : Document
{
    this() { }
    this(string name) { this.name = name; }
    string name;
}

void xmlPackHandler(ref Packer p, ref XmlDocument xml)
{
    p.pack(xml.name);
}

void xmlUnpackHandler(ref Unpacker u, ref XmlDocument xml)
{
    u.unpack(xml.name);
}

void main()
{
    /// Register the 'xmlPackHandler' and 'xmlUnpackHandler' routines for
    /// XmlDocument object instances.
    registerPackHandler!(XmlDocument, xmlPackHandler);
    registerUnpackHandler!(XmlDocument, xmlUnpackHandler);

    /// Now we can serialize/deserialize XmlDocument object instances via a
    /// base class reference.
    Document doc = new XmlDocument("test.xml");
    auto data = pack(doc);
    XmlDocument xml = unpack!XmlDocument(data);
    assert(xml.name == "test.xml");  // xml.name is "test.xml"
}

In addition, here is also a method using @serializedAs attribute:

import std.datetime: Clock, SysTime;
static struct SysTimePackProxy
{
    static void serialize(ref Packer p, ref in SysTime tim)
    {
        p.pack(tim.toISOExtString());
    }

    static void deserialize(ref Unpacker u, ref SysTime tim)
    {
        string tmp;
        u.unpack(tmp);
        tim = SysTime.fromISOExtString(tmp);
    }
}
static struct LogData
{
    string msg;
    string file;
    ulong  line;
    @serializedAs!SysTimePackProxy SysTime timestamp;

    this(string message, string file = __FILE__, ulong line = __LINE__)
    {
        this.msg = message;
        this.file = file;
        this.line = line;
        this.timestamp = Clock.currTime();
    }
}

void main()
{
    /// Now we can serialize/deserialize LogData
    LogData[] logs;
    logs ~= LogData("MessagePack is nice!");
    auto data = pack(logs);
    LogData[] datas = unpack!(LogData[])(data);
    assert(datas[0].timestamp.toString() == datas[0].timestamp.toString());
}
The PackerImpl / Unpacker / StreamingUnpacker types

These types are used by the pack and unpack functions.

See the documentation of PackerImpl, Unpacker and StreamingUnpacker for more details.

Links
Copyright
Copyright (c) 2010- Masahiro Nakagawa
License

Distributed under the Boost Software License, Version 1.0.

danellis/dart-msgpack https://github.com/danellis/dart-msgpack

dart-msgpack

This is a very early release of my MessagePack library for Dart. Currently, message classes must be written by hand. For example:

class NotificationFrame extends Message {
    String kind;
    Map<String, Object> data;

    NotificationFrame(this.kind, this.data);

    static NotificationFrame fromList(List f) => new NotificationFrame(f[0], f[1]);
    List toList() => [kind, data];
}

For each class you need to define the fromList and toList methods, which convert from and to a list of fields respectively.

For example usage, see the unit tests.

knopp/msgpack_dart https://github.com/knopp/msgpack_dart

msgpack_dart

MessagePack implementation for dart.

Clean, simple, fast and with sane API and implementation.

chinawsb/qmsgpack-delphi http://www.qdac.cc

QMsgPack-Messagepack for Delphi/C++ Builder

QMsgPack is a simple and powerful Delphi & C++ Builder implementation for messagepack protocol. QMsgPack is a part of QDAC 3.0,Source code hosted in Sourceforge(http://sourceforge.net/p/qdac3).

Feathers

· Full types support,include messagepack extension type

· Full open source,free for used in ANY PURPOSE

· Quick and simple interface

· RTTI support include

Install

QMsgPack is not a desgin time package.So just place QMsgPack files into search path and add to your project.

Support

· Topic in Website (http://www.qdac.cc/?cat=44) ,CHINESE only

· Mail to author ([email protected])

· Post in forum (http://tieba.baidu.com/f?kw=qdac)

· QQ Group No:250530692 (http://jq.qq.com/?_wv=1027&k=ZH6mZR)

Source check out

· HTTP (http://svn.code.sf.net/p/qdac3/code/)

· SVN (svn://svn.code.sf.net/p/qdac3/code/)

Example
var
  lvMsg, lvMsg2:TQMsgPack;
  lvBytes:TBytes;
  s:string;
begin
  lvMsg := TQMsgPack.Create;
  lvMsg.ForcePath('key.obj').AsString := '汉字,ascii';
    
  //
  lvBytes := lvMsg.Encode;

  lvMsg2 := TQMsgPack.Create;
  lvMsg2.Parse(lvBytes);
  //
  showMessage(lvMsg.ForcePath('key.obj').AsString);
  ....
  

mururu/msgpack-elixir https://github.com/mururu/msgpack-elixir

MessagePack for Elixir

Build Status

Installation

Add :message_pack as a dependency in your mix.exs file.

defp deps do
  [{:message_pack, "~> 0.2.0"}]
end
Usage
# pack
MessagePack.pack([1,2,3]) #=> { :ok, <<147,1,2,3>> }
MessagePack.pack!([1,2,3]) #=> <<147,1,2,3>>

# unpack
MessagePack.unpack(<<147,1,2,3>>) #=> { :ok, [1,2,3] }
MessagePack.unpack!(<<147,1,2,3>>) #=> [1,2,3]

# unpack_once
MessagePack.unpack_once(<<147,1,2,3,4>>) #=> {:ok, {[1, 2, 3], <<4>>}}
MessagePack.unpack_once!(<<147,1,2,3,4>>) #=> {[1, 2, 3], <<4>>}
Options
  • enable_string

Support string type. This options is false by default.

iex(1)> { :ok, bin } = MessagePack.pack(<<255>>)
{:ok, <<161, 255>>}
iex(3)> MessagePack.unpack(<<161, 255>>)
{:ok, <<255>>}
iex(4)> MessagePack.unpack(<<161, 255>>, enable_string: true)
{:error, {:invalid_string, <<255>>}}
  • ext

Support extention type.

See test/message_pack_ext_test.exs.

License

MIT

lexmag/msgpax https://hexdocs.pm/msgpax

Msgpax

CI Status Hex Version

Msgpax is a fast and comprehensive library for serializing and deserializing Elixir terms using the MessagePack format.

Documentation is available online.

Features
  • Packing and unpacking Elixir terms via Msgpax.pack/1 and Msgpax.unpack/1 (and their bang! variants).
  • Unpacking of partial slices of MessagePack-encoded terms via Msgpax.unpack_slice/1.
  • Support for "Binary" and "Extension" MessagePack types via Msgpax.Bin and Msgpax.Ext, respectively.
  • Protocol-based packing through the Msgpax.Packer protocol, that can be derived for user-defined structs.
  • A Plug parser (Msgpax.PlugParser) to parse requests with MessagePack-encoded bodies.
  • Support for MessagePack data fragment manipulation.

A detailed table that shows the relationship between Elixir types and MessagePack types can be found in the documentation for the Msgpax module.

Installation

Add :msgpax as a dependency in your mix.exs file:

def deps do
  [{:msgpax, "~> 2.0"}]
end

Then, run mix deps.get in your shell to fetch the new dependency.

License

Msgpax is released under the ISC license.

msgpack/msgpack-erlang https://github.com/msgpack/msgpack-erlang

MessagePack Erlang

Travis Drone.io hex.pm version

Prerequisites for runtime

Erlang/OTP, >= 17.0 Also based on the new msgpack spec 0b8f5a.

edit rebar.config to use in your application
{deps, [
  {msgpack, ".*",
    {git, "git://github.com/msgpack/msgpack-erlang.git", {branch, "master"}}}
]}.

Or as it is now published at hex.pm, just

{deps, [msgpack]}.

might work.

Simple deserialization
Ham = msgpack:pack(Spam),
{ok, Spam} = msgpack:unpack(Ham).
Stream deserialization
{Term0, Rest0} = msgpack:unpack_stream(Binary),
{Term1, Rest1} = msgpack:unpack_stream(Rest0),
...
Options, for packing and unpacking
{spec, new|old}

Both for packing and unpacking. Default is new. Major difference between old and new spec is:

  • raw family (0xa0~0xbf, 0xda, 0xdb) becomes new str family
  • 0xd9 is new as str8
  • new bin space (0xc4, 0xc5, 0xc6 as bin8, bin16, bin32)
  • new ext space (0xc7, 0xc8, 0xc9 as ext8, ext16, ext32)
  • new fixext space (0xd4, 0xd5, 0xd6, 0xd7, 0xd8 as fixext1, fixext2, fixext4, fixext8, fixext16),

The default is new spec. Old spec mode does not handle these new types but returns error. To use old spec mode, this option is explicitly added.

OldHam = msgpack:pack(Spam, [{spec, old}]),
{ok, Spam} = msgpack:unpack(OldHam, [{spec, old}]).
{allow_atom, none|pack}

Only in packing. Atoms are packed as binaries. Default value is pack. Otherwise, any term including atoms throws badarg.

{known_atoms, [atom()]}

Both in packing and unpacking. In packing, if an atom is in this list a binary is encoded as a binary. In unpacking, msgpacked binaries are decoded as atoms with erlang:binary_to_existing_atom/2 with encoding utf8. Default value is an empty list.

Even if allow_atom is none, known atoms are packed.

{unpack_str, as_binary|as_list}

A switch to choose decoded term style of str type when unpacking. Only available at new spec. Default is as_list.

mode        as_binary    as_list
-----------+------------+-------
bin         binary()     binary()
str         binary()     string()
{validate_string, boolean()}

Only in unpacking, UTF-8 validation at unpacking from str type will be enabled. Default value is false.

{pack_str, from_binary|from_list|none}

A switch to choose packing of string() when packing. Only available at new spec. Default is from_list for symmetry with unpack_str option.

mode        from_list    from_binary    none
-----------+------------+--------------+-----------------
binary()    bin          str*/bin       bin
string()    str*/array   array of int   array of int
list()      array        array          array

But the default option pays the cost of performance for symmetry. If the overhead of UTF-8 validation is unacceptable, choosing none as the option would be the best.

  • * Tries to pack as str if it is a valid string().
{map_format, map|jiffy|jsx}

Both at packing and unpacking. Default value is map.

msgpack:pack(#{ <<"key">> => <<"value">> }, []).
msgpack:pack(#{ <<"key">> => <<"value">> }, [{map_format, map}]).
msgpack:pack({[{<<"key">>, <<"value">>}]}, [{map_format, jiffy}]),
msgpack:pack([{<<"key">>, <<"value">>}], [{map_format, jsx}]).
{ext, {msgpack_ext_packer(), msgpack_ext_unpacker()}|module()}

At both. The default behaviour in case of facing ext data at decoding is to ignore them as its length is known.

Now msgpack-erlang supports ext type. Now you can serialize everything with your original (de)serializer. That will enable us to handle erlang- native types like pid(), ref() contained in tuple(). See test/msgpack_ext_example_tests.erl for example code.

Packer = fun({ref, Ref}, Opt) when is_reference(Ref) -> {ok, {12, term_to_binary(Ref)}} end,
Unpacker = fun(12, Bin) -> {ok, {ref, binary_to_term(Bin)}} end,
Ref = make_ref(),
Opt = [{ext,{Packer,Unpacker}}],
{ok, {ref, Ref}} = msgpack:unpack(msgpack:pack({ref, Ref}, Opt), Opt).
Misc
Float type

The Float type of Message Pack represents IEEE 754 floating point number, so it includes Nan and Infinity. In unpacking, msgpack-erlang returns nan, positive_infinity and negative_infinity.

License

Apache License 2.0

Release Notes
0.7.0
  • Support nan, positive_infinity and negative_infinity
0.6.0
  • Support OTP 19.0
0.5.0
  • Renewed optional arguments to pack/unpack interface. This is incompatible change from 0.4 series.
0.4.0
  • Deprecate nil
  • Moved to rebar3
  • Promote default map unpacker as default format when OTP is >= 17
  • Added QuickCheck tests
  • Since this version OTP older than R16B03-1 are no more supported
0.3.5 / 0.3.4
  • 0.3 series will be the last versions that supports R16B or older versions of OTP.
  • OTP 18.0 support
  • Promote default map unpacker as default format when OTP is >= 18
0.3.3
  • Add OTP 17 series to Travis-CI tests
  • Fix wrong numbering for ext types
  • Allow packing maps even when {format,map} is not set
  • Fix Dialyzer invalid contract warning
  • Proper use of null for jiffy-style encoding/decoding
0.3.2
  • set back default style as jiffy
  • fix bugs around nil/null handling
0.3.0
  • supports map new in 17.0
  • jiffy-style maps will be deprecated in near future
  • set default style as map
0.2.8

0.2 series works with OTP 17.0, R16, R15, and with MessagePack's new and old format. But does not support map type introduced in OTP 17.0.

It also supports JSX-compatible mode.

Gab-km/msgpack-fsharp https://github.com/Gab-km/msgpack-fsharp

MessagePack for F#
Build status
What is this?

MessagePack is a fast and compact binary serialization library.

MessagePack for F# is a MessagePack implementation of F#, by F#, for F#.

Usage
open MsgPack

[| 1uy; 2uy; 3uy |]
|> Array.map (Value.UInt8)
|> Value.Array
|> Packer.packOne
//=> val it : byte [] = [|147uy; 1uy; 2uy; 3uy|]

Unpacker.unpack [|147uy; 1uy; 2uy; 3uy|]
//=> [|Value.Array [|Value.UInt8 1uy; Value.UInt8 2uy; Value.UInt8 3uy|]|]
Copyright

Copyright (c) 2014- Kazuhiro Matsushima

License

Distributed under the Apache License, Version 2.0 .

pocketberserker/MessagePack.FSharpExtensions https://github.com/pocketberserker/MessagePack.FSharpExtensions

MessagePack.FSharpExtensions

NuGet Status

MessagePack.FSharpExtensions is a MessagePack-CSharp extension library for F#.

Usage
open System
open System.Buffers
open MessagePack
open MessagePack.Resolvers
open MessagePack.FSharp

[<MessagePackObject>]
type UnionSample =
  | Foo of XYZ : int
  | Bar of OPQ : string list

let convertAsMemory<'T> options (value: 'T) =
  let memory = ReadOnlyMemory(MessagePackSerializer.Serialize(value, options))
  MessagePackSerializer.Deserialize<'T>(memory, options)

let convertAsSequence<'T> options (value: 'T) =
  let sequence = ReadOnlySequence(MessagePackSerializer.Serialize(value, options))
  MessagePackSerializer.Deserialize<'T>(& sequence, options)

let dump = function
| Foo x ->
  printfn "%d" x
| Bar xs ->
  printfn "%A" xs

let resolver =
  Resolvers.CompositeResolver.Create(
    FSharpResolver.Instance,
    StandardResolver.Instance
)

let options = MessagePackSerializerOptions.Standard.WithResolver(resolver)

Foo 999
|> convertAsMemory options
|> dump

Bar ["example"]
|> convertAsSequence options
|> dump
Supported types
  • option
  • voption
  • list
  • map
  • set
  • Discriminated Union
  • Struct Discriminated Union

Records, Struct Records and Anonymous Records are serialized and deserialized using DynamicObjectResolver in MessagePack-CSharp.

vmihailenco/msgpack https://msgpack.uptrace.dev/

MessagePack encoding for Golang

Build Status PkgGoDev Documentation Chat

❤️ Uptrace.dev - All-in-one tool to optimize performance and monitor errors & logs

Other projects you may like:

  • Bun - fast and simple SQL client for PostgreSQL, MySQL, and SQLite.
  • BunRouter - fast and flexible HTTP router for Go.
Features
Installation

msgpack supports 2 last Go versions and requires support for Go modules. So make sure to initialize a Go module:

go mod init github.com/my/repo

And then install msgpack/v5 (note v5 in the import; omitting it is a popular mistake):

go get github.com/vmihailenco/msgpack/v5
Quickstart
import "github.com/vmihailenco/msgpack/v5"

func ExampleMarshal() {
    type Item struct {
        Foo string
    }

    b, err := msgpack.Marshal(&Item{Foo: "bar"})
    if err != nil {
        panic(err)
    }

    var item Item
    err = msgpack.Unmarshal(b, &item)
    if err != nil {
        panic(err)
    }
    fmt.Println(item.Foo)
    // Output: bar
}

ugorji/go https://github.com/ugorji/go

MessagePack and Binc Codec for Go Language.

A High Performance, Feature-Rich, Idiomatic encode/decode and rpc library.

To install:

go get github.com/ugorji/go/codec

Source: [http://github.com/ugorji/go]
Online documentation: [http://godoc.org/github.com/ugorji/go/codec]

Typical usage:

    // create and use decoder/encoder
    var (
        v interface{} // value to decode/encode into
        r io.Reader
        w io.Writer
        b []byte
        mh codec.MsgpackHandle
    )
    
    dec = codec.NewDecoder(r, &mh)
    dec = codec.NewDecoderBytes(b, &mh)
    err = dec.Decode(&v) 
    
    enc = codec.NewEncoder(w, &mh)
    enc = codec.NewEncoderBytes(&b, &mh)
    err = enc.Encode(v)
    
    //RPC Server
    go func() {
        for {
            conn, err := listener.Accept()
            rpcCodec := codec.GoRpc.ServerCodec(conn, h)
            //OR rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, h)
            rpc.ServeCodec(rpcCodec)
        }
    }()
    
    //RPC Communication (client side)
    conn, err = net.Dial("tcp", "localhost:5555")
    rpcCodec := codec.GoRpc.ClientCodec(conn, h)
    //OR rpcCodec := codec.MsgpackSpecRpc.ClientCodec(conn, h)
    client := rpc.NewClientWithCodec(rpcCodec)

tinylib/msgp https://github.com/tinylib/msgp

MessagePack Code Generator Build Status

This is a code generation tool and serialization library for MessagePack. You can read more about MessagePack in the wiki, or at msgpack.org.

Why? Quickstart

In a source file, include the following directive:

//go:generate msgp

The msgp command will generate serialization methods for all exported type declarations in the file.

You can read more about the code generation options here.

Use

Field names can be set in much the same way as the encoding/json package. For example:

type Person struct {
	Name       string `msg:"name"`
	Address    string `msg:"address"`
	Age        int    `msg:"age"`
	Hidden     string `msg:"-"` // this field is ignored
	unexported bool             // this field is also ignored
}

By default, the code generator will satisfy msgp.Sizer, msgp.Encodable, msgp.Decodable, msgp.Marshaler, and msgp.Unmarshaler. Carefully-designed applications can use these methods to do marshalling/unmarshalling with zero heap allocations.

While msgp.Marshaler and msgp.Unmarshaler are quite similar to the standard library's json.Marshaler and json.Unmarshaler, msgp.Encodable and msgp.Decodable are useful for stream serialization. (*msgp.Writer and *msgp.Reader are essentially protocol-aware versions of *bufio.Writer and *bufio.Reader, respectively.)

Features
  • Extremely fast generated code
  • Test and benchmark generation
  • JSON interoperability (see msgp.CopyToJSON() and msgp.UnmarshalAsJSON())
  • Support for complex type declarations
  • Native support for Go's time.Time, complex64, and complex128 types
  • Generation of both []byte-oriented and io.Reader/io.Writer-oriented methods
  • Support for arbitrary type system extensions
  • Preprocessor directives
  • File-based dependency model means fast codegen regardless of source tree size.

Consider the following:

const Eight = 8
type MyInt int
type Data []byte

type Struct struct {
	Which  map[string]*MyInt `msg:"which"`
	Other  Data              `msg:"other"`
	Nums   [Eight]float64    `msg:"nums"`
}

As long as the declarations of MyInt and Data are in the same file as Struct, the parser will determine that the type information for MyInt and Data can be passed into the definition of Struct before its methods are generated.

Extensions

MessagePack supports defining your own types through "extensions," which are just a tuple of the data "type" (int8) and the raw binary. You can see a worked example in the wiki.

Status

Mostly stable, in that no breaking changes have been made to the /msgp library in more than a year. Newer versions of the code may generate different code than older versions for performance reasons. I (@philhofer) am aware of a number of stability-critical commercial applications that use this code with good results. But, caveat emptor.

You can read more about how msgp maps MessagePack types onto Go types in the wiki.

Here some of the known limitations/restrictions:

  • Identifiers from outside the processed source file are assumed (optimistically) to satisfy the generator's interfaces. If this isn't the case, your code will fail to compile.
  • Like most serializers, chan and func fields are ignored, as well as non-exported fields.
  • Encoding of interface{} is limited to built-ins or types that have explicit encoding methods.
  • Maps must have string keys. This is intentional (as it preserves JSON interop.) Although non-string map keys are not forbidden by the MessagePack standard, many serializers impose this restriction. (It also means any well-formed struct can be de-serialized into a map[string]interface{}.) The only exception to this rule is that the deserializers will allow you to read map keys encoded as bin types, due to the fact that some legacy encodings permitted this. (However, those values will still be cast to Go strings, and they will be converted to str types when re-encoded. It is the responsibility of the user to ensure that map keys are UTF-8 safe in this case.) The same rules hold true for JSON translation.

If the output compiles, then there's a pretty good chance things are fine. (Plus, we generate tests for you.) Please, please, please file an issue if you think the generator is writing broken code.

Performance

If you like benchmarks, see here and here.

As one might expect, the generated methods that deal with []byte are faster for small objects, but the io.Reader/Writer methods are generally more memory-efficient (and, at some point, faster) for large (> 2KB) objects.

shamaton/msgpack https://github.com/shamaton/msgpack

MessagePack for Golang

Go Reference test Go Report Card codecov FOSSA Status

📣 Notice

If your application serializes only primitive types, array, map and struct, code generation is also recommended. You can get the fastest performance with msgpackgen.

Features
  • Supported types : primitive / array / slice / struct / map / interface{} and time.Time
  • Renaming fields via msgpack:"field_name"
  • Omitting fields via msgpack:"-"
  • Supports extend encoder / decoder
  • Can also Encoding / Decoding struct as array

This package requires more than version 1.13

Installation

Current version is msgpack/v2.

go get -u github.com/shamaton/msgpack/v2
Quick Start
package main

import (
  "github.com/shamaton/msgpack/v2"
)

func main() {
	type Struct struct {
		String string
	}
	v := Struct{String: "msgpack"}

	d, err := msgpack.Marshal(v)
	if err != nil {
		panic(err)
	}
	r := Struct{}
	err = msgpack.Unmarshal(d, &r)
	if err != nil {
		panic(err)
	}
}
Benchmark

This result made from shamaton/msgpack_bench

msgpack_bench

License

This library is under the MIT License.

reeze/msgpack-hhvm https://github.com/reeze/msgpack-hhvm

msgpack-hhvm

Build Status: Build Status

Msgpack for HHVM, It is a msgpack binding for HHVM

API
  • msgpack_pack(mixed $input) : string; pack a input to msgpack, object and resource are not supported, array and other types supported, false on failure.
  • msgpack_unpack(string $pac) : mixed; unpack a msgpack.
Installation
$ git clone https://github.com/reeze/msgpack-hhvm --depth=1
$ cd msgpack-hhvm
$ hphpize && cmake . && make
$ cp msgpack.so /path/to/your/hhvm/ext/dir

If you don't have hphpize program, please intall package hhvm-dev

$ sudo apt-get install hhvm-dev
Contribution and Issues

Feel free to send Pull Requests for bug report at: http://github.com/reeze/msgpack-hhvm/issues

Authors

msgpack/msgpack-haskell http://hackage.haskell.org/package/msgpack

rodrigosetti/messagepack http://hackage.haskell.org/package/messagepack

MessagePack for Haskell

This implementation defines an messagepack Object type, which is an instance of Serialize (from cereal ):

data Object = ObjectNil
            | ObjectUInt   Word64
            | ObjectInt    Int64
            | ObjectBool   Bool
            | ObjectFloat  Float
            | ObjectDouble Double
            | ObjectString ByteString
            | ObjectBinary ByteString
            | ObjectArray  [Object]
            | ObjectMap    (M.Map Object Object )
            | ObjectExt    !Int8 BS.ByteString
    deriving (Eq, Ord, Show)
    
instance Serialize Object where
    -- ...

Thus, you can use cereal's encode and decode to pack and unpack objects.

aaulia/msgpack-haxe https://github.com/aaulia/msgpack-haxe

MIT License Haxelib Version

msgpack-haxe

MessagePack (http://msgpack.org) serialization library for Haxe

How to install:

Simply use haxelib git to use this github repo or haxelib install msgpack-haxe to use the one in the haxelib repository.

Supported Type:
  • Null
  • Bool
  • Int
  • Float
  • Object
  • Bytes
  • String
  • Array
  • IntMap/StringMap
Example code:
package;
import org.msgpack.MsgPack;

class Example {
    public static function main() {
        var i = { a: 1, b: 2, c: "Hello World!" };
        var m = MsgPack.encode(i);
        var o = MsgPack.decode(m);

        trace(i);
        trace(m.toHex());
        trace(o);
    }
}

komamitsu/jackson-dataformat-msgpack https://github.com/komamitsu/jackson-dataformat-msgpack

jackson-dataformat-msgpack

This project is merged to msgpack-java!! Yay!

See msgpack-java/msgpack-jackson for the updated documents

Overview

Build Status

This Jackson extension library handles reading and writing of data encoded in MessagePack data format. It extends standard Jackson streaming API (JsonFactory, JsonParser, JsonGenerator), and as such works seamlessly with all the higher level data abstractions (data binding, tree model, and pluggable extensions).

Maven dependency

To use this module on Maven-based projects, use following dependency:

<dependency>
  <groupId>org.komamitsu</groupId>
  <artifactId>jackson-dataformat-msgpack</artifactId>
  <version>0.0.3</version>
</dependency>
Usage

Only thing you need to do is to instantiate MessagePackFactory and pass it to the constructor of ObjectMapper.

  ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
  ExamplePojo orig = new ExamplePojo("komamitsu");
  byte[] bytes = objectMapper.writeValueAsBytes(orig);
  ExamplePojo value = objectMapper.readValue(bytes, ExamplePojo.class);
  System.out.println(value.getName()); // => komamitsu

Also, you can exchange data among multiple languages.

Java

  // Serialize
  Map<String, Object> obj = new HashMap<String, Object>();
  obj.put("foo", "hello");
  obj.put("bar", "world");
  byte[] bs = objectMapper.writeValueAsBytes(obj);
  // bs => [-126, -93, 102, 111, 111, -91, 104, 101, 108, 108, 111,
  //        -93, 98, 97, 114, -91, 119, 111, 114, 108, 100]

Ruby

  require 'msgpack'

  # Deserialize
  xs = [-126, -93, 102, 111, 111, -91, 104, 101, 108, 108, 111,
        -93, 98, 97, 114, -91, 119, 111, 114, 108, 100]
  MessagePack.unpack(xs.pack("C*"))
  # => {"foo"=>"hello", "bar"=>"world"}

  # Serialize
  ["zero", 1, 2.0, nil].to_msgpack.unpack('C*')
  # => [148, 164, 122, 101, 114, 111, 1, 203, 64, 0, 0, 0, 0, 0, 0, 0, 192]

Java

  // Deserialize
  bs = new byte[] {(byte) 148, (byte) 164, 122, 101, 114, 111, 1,
                   (byte) 203, 64, 0, 0, 0, 0, 0, 0, 0, (byte) 192};
  TypeReference<List<Object>> typeReference = new TypeReference<List<Object>>(){};
  List<Object> xs = objectMapper.readValue(bs, typeReference);
  // xs => [zero, 1, 2.0, null]

msgpack/msgpack-java https://github.com/msgpack/msgpack-java

MessagePack for Java

MessagePack is a binary serialization format. If you need a fast and compact alternative of JSON, MessagePack is your friend. For example, a small integer can be encoded in a single byte, and short strings only need a single byte prefix + the original byte array. MessagePack implementation is already available in various languages (See also the list in http://msgpack.org) and works as a universal data format.

MessagePack v7 (or later) is a faster implementation of the previous version v06, and supports all of the message pack types, including extension format.

JavaDoc is available at javadoc.io.

Quick Start

Maven Central Javadoc

For Maven users:

<dependency>
   <groupId>org.msgpack</groupId>
   <artifactId>msgpack-core</artifactId>
   <version>(version)</version>
</dependency>

For sbt users:

libraryDependencies += "org.msgpack" % "msgpack-core" % "(version)"

For gradle users:

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.msgpack:msgpack-core:(version)'
}
Integration with Jackson ObjectMapper (jackson-databind)

msgpack-java supports serialization and deserialization of Java objects through jackson-databind. For details, see msgpack-jackson/README.md. The template-based serialization mechanism used in v06 is deprecated.

For MessagePack Developers Travis CI

msgpack-java uses sbt for building the projects. For the basic usage of sbt, see:

Coding style

Basic sbt commands

Enter the sbt console:

$ ./sbt

Here is a list of sbt commands for daily development:

> ~compile                                 # Compile source codes
> ~test:compile                            # Compile both source and test codes
> ~test                                    # Run tests upon source code change
> ~testOnly *MessagePackTest               # Run tests in the specified class
> ~testOnly *MessagePackTest -- (pattern)  # Run tests matching the pattern 
> project msgpack-core                     # Focus on a specific project
> package                                  # Create a jar file in the target folder of each project
> findbugs                                 # Produce findbugs report in target/findbugs
> jacoco:cover                             # Report the code coverage of tests to target/jacoco folder
> jcheckStyle                              # Run check style
> ;scalafmt;test:scalafmt;scalafmtSbt      # Reformat Scala codes
Publishing
> publishLocal            # Install to local .ivy2 repository
> publishM2               # Install to local .m2 Maven repository
> publish                 # Publishing a snapshot version to the Sonatype repository
Publish to Sonatype (Maven Central)

To publish a new version, you only need to add a new git tag and push it to GitHub. GitHub Action will deploy a new release version to Maven Central (Sonatype).

$ git tag v0.x.y
$ git push origin v0.x.y

To generate a release notes, you can use this command line:

$ git log v(last version).. --oneline | cut -f 2- -d ' ' | perl -npe 's/(.*)\(\#([0-9]+)\)/* \1\[\#\2\]\(http:\/\/github.com\/msgpack\/msgpack-java\/pull\/\2\)/g'
Publishing to Sonatype from Local Machine

If you need to publish to Maven central using a local machine, you need to configure sbt-sonatype plugin. First set Sonatype account information (user name and password) in the global sbt settings. To protect your password, never include this file in your project.

$HOME/.sbt/(sbt-version)/sonatype.sbt

credentials += Credentials("Sonatype Nexus Repository Manager",
        "oss.sonatype.org",
        "(Sonatype user name)",
        "(Sonatype password)")

You may also need to configure GPG. See the instruction in sbt-pgp.

Then, run publishedSigned followed by sonatypeBundleRelease:

# [optional] When you need to perform the individual release steps manually, use the following commands:
> publishSigned           # Publish GPG signed artifacts to the Sonatype repository
> sonatypeBundleRelease   # Publish to the Maven Central (It will be synched within less than 4 hours)

If some sporadic error happens (e.g., Sonatype timeout), rerun sonatypeBundleRelease again.

Project Structure
msgpack-core                 # Contains packer/unpacker implementation that never uses third-party libraries
msgpack-jackson              # Contains jackson-dataformat-java implementation

msgpack/msgpack-javascript https://msgpack.org/

MessagePack for JavaScript/ECMA-262

npm version CI codecov minzip tree-shaking

This is a JavaScript/ECMA-262 implementation of MessagePack, an efficient binary serilization format:

https://msgpack.org/

This library is a universal JavaScript, meaning it is compatible with all the major browsers and NodeJS. In addition, because it is implemented in TypeScript, type definition files (d.ts) are bundled in the distribution.

Note that this is the second version of MessagePack for JavaScript. The first version, which was implemented in ES5 and was never released to npmjs.com, is tagged as classic.

Synopsis
import { deepStrictEqual } from "assert";
import { encode, decode } from "@msgpack/msgpack";

const object = {
  nil: null,
  integer: 1,
  float: Math.PI,
  string: "Hello, world!",
  binary: Uint8Array.from([1, 2, 3]),
  array: [10, 20, 30],
  map: { foo: "bar" },
  timestampExt: new Date(),
};

const encoded: Uint8Array = encode(object);

deepStrictEqual(decode(encoded), object);
Table of Contents
Install

This library is published to npmjs.com as @msgpack/msgpack.

npm install @msgpack/msgpack
API
encode(data: unknown, options?: EncodeOptions): Uint8Array

It encodes data into a single MessagePack-encoded object, and returns a byte array as Uint8Array, throwing errors if data is, or includes, a non-serializable object such as a function or a symbol.

for example:

import { encode } from "@msgpack/msgpack";

const encoded: Uint8Array = encode({ foo: "bar" });
console.log(encoded);

If you'd like to convert an uint8array to a NodeJS Buffer, use Buffer.from(arrayBuffer, offset, length) in order not to copy the underlying ArrayBuffer, while Buffer.from(uint8array) copies it:

import { encode } from "@msgpack/msgpack";

const encoded: Uint8Array = encode({ foo: "bar" });

// `buffer` refers the same ArrayBuffer as `encoded`.
const buffer: Buffer = Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength);
console.log(buffer);
EncodeOptions
Name Type Default
extensionCodec ExtensionCodec ExtensionCodec.defaultCodec
maxDepth number 100
initialBufferSize number 2048
sortKeys boolean false
forceFloat32 boolean false
forceIntegerToFloat boolean false
ignoreUndefined boolean false
context user-defined -
decode(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): unknown

It decodes buffer that includes a MessagePack-encoded object, and returns the decoded object typed unknown.

buffer must be an array of bytes, which is typically Uint8Array or ArrayBuffer. BufferSource is defined as ArrayBuffer | ArrayBufferView.

In addition, buffer can include a single encoded object. If the buffer includes extra bytes after an object, it will throw RangeError. To decode buffer that includes multiple encoded objects, use decodeMulti() or decodeMultiStream() (recommended) instead.

for example:

import { decode } from "@msgpack/msgpack";

const encoded: Uint8Array;
const object = decode(encoded);
console.log(object);

NodeJS Buffer is also acceptable because it is a subclass of Uint8Array.

DecodeOptions
Name Type Default
extensionCodec ExtensionCodec ExtensionCodec.defaultCodec
maxStrLength number 4_294_967_295 (UINT32_MAX)
maxBinLength number 4_294_967_295 (UINT32_MAX)
maxArrayLength number 4_294_967_295 (UINT32_MAX)
maxMapLength number 4_294_967_295 (UINT32_MAX)
maxExtLength number 4_294_967_295 (UINT32_MAX)
context user-defined -

You can use max${Type}Length to limit the length of each type decoded.

decodeMulti(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): Generator<unknown, void, unknown>

It decodes buffer that includes multiple MessagePack-encoded objects, and returns decoded objects as a generator. That is, this is a synchronous variant for decodeMultiStream().

This function is not recommended to decode a MessagePack binary via I/O stream including sockets because it's synchronous. Instead, decodeMultiStream() decodes it asynchronously, typically spending less time and memory.

for example:

import { decode } from "@msgpack/msgpack";

const encoded: Uint8Array;

for (const object of decodeMulti(encoded)) {
  console.log(object);
}
decodeAsync(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): Promise<unknown>

It decodes stream, where ReadableStreamLike<T> is defined as ReadableStream<T> | AsyncIterable<T>, in an async iterable of byte arrays, and returns decoded object as unknown type, wrapped in Promise. This function works asynchronously. This is an async variant for decode().

DecodeAsyncOptions is the same as DecodeOptions for decode().

This function is designed to work with whatwg fetch() like this:

import { decodeAsync } from "@msgpack/msgpack";

const MSGPACK_TYPE = "application/x-msgpack";

const response = await fetch(url);
const contentType = response.headers.get("Content-Type");
if (contentType && contentType.startsWith(MSGPACK_TYPE) && response.body != null) {
  const object = await decodeAsync(response.body);
  // do something with object
} else { /* handle errors */ }
decodeArrayStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>

It is alike to decodeAsync(), but only accepts a stream that includes an array of items, and emits a decoded item one by one.

for example:

import { decodeArrayStream } from "@msgpack/msgpack";

const stream: AsyncIterator<Uint8Array>;

// in an async function:
for await (const item of decodeArrayStream(stream)) {
  console.log(item);
}
decodeMultiStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>

It is alike to decodeAsync() and decodeArrayStream(), but the input stream must consist of multiple MessagePack-encoded items. This is an asynchronous variant for decodeMulti().

In other words, it could decode an unlimited stream and emits a decoded item one by one.

for example:

import { decodeMultiStream } from "@msgpack/msgpack";

const stream: AsyncIterator<Uint8Array>;

// in an async function:
for await (const item of decodeMultiStream(stream)) {
  console.log(item);
}

This function is available since v2.4.0; previously it was called as decodeStream().

Reusing Encoder and Decoder instances

Encoder and Decoder classes is provided for better performance:

import { deepStrictEqual } from "assert";
import { Encoder, Decoder } from "@msgpack/msgpack";

const encoder = new Encoder();
const decoder = new Decoder();

const encoded: Uint8Array = encoder.encode(object);
deepStrictEqual(decoder.decode(encoded), object);

According to our benchmark, reusing Encoder instance is about 20% faster than encode() function, and reusing Decoder instance is about 2% faster than decode() function. Note that the result should vary in environments and data structure.

Extension Types

To handle MessagePack Extension Types, this library provides ExtensionCodec class.

Here is an example to setup custom extension types that handles Map and Set classes in TypeScript:

import { encode, decode, ExtensionCodec } from "@msgpack/msgpack";

const extensionCodec = new ExtensionCodec();

// Set<T>
const SET_EXT_TYPE = 0 // Any in 0-127
extensionCodec.register({
  type: SET_EXT_TYPE,
  encode: (object: unknown): Uint8Array | null => {
    if (object instanceof Set) {
      return encode([...object]);
    } else {
      return null;
    }
  },
  decode: (data: Uint8Array) => {
    const array = decode(data) as Array<unknown>;
    return new Set(array);
  },
});

// Map<T>
const MAP_EXT_TYPE = 1; // Any in 0-127
extensionCodec.register({
  type: MAP_EXT_TYPE,
  encode: (object: unknown): Uint8Array => {
    if (object instanceof Map) {
      return encode([...object]);
    } else {
      return null;
    }
  },
  decode: (data: Uint8Array) => {
    const array = decode(data) as Array<[unknown, unknown]>;
    return new Map(array);
  },
});

const encoded = encode([new Set<any>(), new Map<any, any>()], { extensionCodec });
const decoded = decode(encoded, { extensionCodec });

Not that extension types for custom objects must be [0, 127], while [-1, -128] is reserved for MessagePack itself.

ExtensionCodec context

When using an extension codec, it may be necessary to keep encoding/decoding state, to keep track of which objects got encoded/re-created. To do this, pass a context to the EncodeOptions and DecodeOptions (and if using typescript, type the ExtensionCodec too). Don't forget to pass the {extensionCodec, context} along recursive encoding/decoding:

import { encode, decode, ExtensionCodec } from "@msgpack/msgpack";

class MyContext {
  track(object: any) { /*...*/ }
}

class MyType { /* ... */ }

const extensionCodec = new ExtensionCodec<MyContext>();

// MyType
const MYTYPE_EXT_TYPE = 0 // Any in 0-127
extensionCodec.register({
  type: MYTYPE_EXT_TYPE,
  encode: (object, context) => {
    if (object instanceof MyType) {
      context.track(object); // <-- like this
      return encode(object.toJSON(), { extensionCodec, context });
    } else {
      return null;
    }
  },
  decode: (data, extType, context) => {
    const decoded = decode(data, { extensionCodec, context });
    const my = new MyType(decoded);
    context.track(my); // <-- and like this
    return my;
  },
});

// and later
import { encode, decode } from "@msgpack/msgpack";

const context = new MyContext();

const encoded = = encode({myType: new MyType<any>()}, { extensionCodec, context });
const decoded = decode(encoded, { extensionCodec, context });
Handling BigInt with ExtensionCodec

This library does not handle BigInt by default, but you can handle it with ExtensionCodec like this:

import { deepStrictEqual } from "assert";
import { encode, decode, ExtensionCodec } from "@msgpack/msgpack";

const BIGINT_EXT_TYPE = 0; // Any in 0-127
const extensionCodec = new ExtensionCodec();
extensionCodec.register({
    type: BIGINT_EXT_TYPE,
    encode: (input: unknown) => {
        if (typeof input === "bigint") {
            if (input <= Number.MAX_SAFE_INTEGER && input >= Number.MIN_SAFE_INTEGER) {
                return encode(parseInt(input.toString(), 10));
            } else {
                return encode(input.toString());
            }
        } else {
            return null;
        }
    },
    decode: (data: Uint8Array) => {
        return BigInt(decode(data));
    },
});

const value = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1);
const encoded: = encode(value, { extensionCodec });
deepStrictEqual(decode(encoded, { extensionCodec }), value);
The temporal module as timestamp extensions

There is a proposal for a new date/time representations in JavaScript:

This library maps Date to the MessagePack timestamp extension by default, but you can re-map the temporal module (or Temporal Polyfill) to the timestamp extension like this:

import { Instant } from "@std-proposal/temporal";
import { deepStrictEqual } from "assert";
import {
  encode,
  decode,
  ExtensionCodec,
  EXT_TIMESTAMP,
  encodeTimeSpecToTimestamp,
  decodeTimestampToTimeSpec,
} from "@msgpack/msgpack";

const extensionCodec = new ExtensionCodec();
extensionCodec.register({
  type: EXT_TIMESTAMP, // override the default behavior!
  encode: (input: any) => {
    if (input instanceof Instant) {
      const sec = input.seconds;
      const nsec = Number(input.nanoseconds - BigInt(sec) * BigInt(1e9));
      return encodeTimeSpecToTimestamp({ sec, nsec });
    } else {
      return null;
    }
  },
  decode: (data: Uint8Array) => {
    const timeSpec = decodeTimestampToTimeSpec(data);
    const sec = BigInt(timeSpec.sec);
    const nsec = BigInt(timeSpec.nsec);
    return Instant.fromEpochNanoseconds(sec * BigInt(1e9) + nsec);
  },
});

const instant = Instant.fromEpochMilliseconds(Date.now());
const encoded = encode(instant, { extensionCodec });
const decoded = decode(encoded, { extensionCodec });
deepStrictEqual(decoded, instant);

This will be default once the temporal module is standardizied, which is not a near-future, though.

Decoding a Blob

Blob is a binary data container provided by browsers. To read its contents, you can use Blob#arrayBuffer() or Blob#stream(). Blob#stream() is recommended if your target platform support it. This is because streaming decode should be faster for large objects. In both ways, you need to use asynchronous API.

async function decodeFromBlob(blob: Blob): unknown {
  if (blob.stream) {
    // Blob#stream(): ReadableStream<Uint8Array> (recommended)
    return await decodeAsync(blob.stream());
  } else {
    // Blob#arrayBuffer(): Promise<ArrayBuffer> (if stream() is not available)
    return decode(await blob.arrayBuffer());
  }
}
MessagePack Specification

This library is compatible with the "August 2017" revision of MessagePack specification at the point where timestamp ext was added:

  • str/bin separation, added at August 2013
  • extension types, added at August 2013
  • timestamp ext type, added at August 2017

The livinng specification is here:

https://github.com/msgpack/msgpack

Note that as of June 2019 there're no official "version" on the MessagePack specification. See https://github.com/msgpack/msgpack/issues/195 for the discussions.

MessagePack Mapping Table

The following table shows how JavaScript values are mapped to MessagePack formats and vice versa.

Source Value MessagePack Format Value Decoded
null, undefined nil null (*1)
boolean (true, false) bool family boolean (true, false)
number (53-bit int) int family number (53-bit int)
number (64-bit float) float family number (64-bit float)
string str family string
ArrayBufferView bin family Uint8Array (*2)
Array array family Array
Object map family Object (*3)
Date timestamp ext family Date (*4)
  • *1 Both null and undefined are mapped to nil (0xC0) type, and are decoded into null
  • *2 Any ArrayBufferViews including NodeJS's Buffer are mapped to bin family, and are decoded into Uint8Array
  • *3 In handling Object, it is regarded as Record<string, unknown> in terms of TypeScript
  • *4 MessagePack timestamps may have nanoseconds, which will lost when it is decoded into JavaScript Date. This behavior can be overridden by registering -1 for the extension codec.
Prerequisites

This is a universal JavaScript library that supports major browsers and NodeJS.

ECMA-262
  • ES5 language features
  • ES2018 standard library, including:
    • Typed arrays (ES2015)
    • Async iterations (ES2018)
    • Features added in ES2015-ES2018

ES2018 standard library used in this library can be polyfilled with core-js.

If you support IE11, import core-js in your application entrypoints, as this library does in testing for browsers.

NodeJS

NodeJS v10 is required, but NodeJS v12 or later is recommended because it includes the V8 feature of Improving DataView performance in V8.

NodeJS before v10 will work by importing @msgpack/msgpack/dist.es5+umd/msgpack.

TypeScript Compiler / Type Definitions

This module requires type definitions of AsyncIterator, SourceBuffer, whatwg streams, and so on. They are provided by "lib": ["ES2021", "DOM"] in tsconfig.json.

Regarding the TypeScript compiler version, only the latest TypeScript is tested in development.

Benchmark

Run-time performance is not the only reason to use MessagePack, but it's important to choose MessagePack libraries, so a benchmark suite is provided to monitor the performance of this library.

V8's built-in JSON has been improved for years, esp. JSON.parse() is significantly improved in V8/7.6, it is the fastest deserializer as of 2019, as the benchmark result bellow suggests.

However, MessagePack can handles binary data effectively, actual performance depends on situations. You'd better take benchmark on your own use-case if performance matters.

Benchmark on NodeJS/v12.18.3 (V8/7.8)

operation op ms op/s
buf = Buffer.from(JSON.stringify(obj)); 840700 5000 168140
buf = JSON.stringify(obj); 1249800 5000 249960
obj = JSON.parse(buf); 1648000 5000 329600
buf = require("msgpack-lite").encode(obj); 603500 5000 120700
obj = require("msgpack-lite").decode(buf); 315900 5000 63180
buf = require("@msgpack/msgpack").encode(obj); 945400 5000 189080
obj = require("@msgpack/msgpack").decode(buf); 770200 5000 154040
buf = /* @msgpack/msgpack */ encoder.encode(obj); 1162600 5000 232520
obj = /* @msgpack/msgpack */ decoder.decode(buf); 787800 5000 157560

Note that Buffer.from() for JSON.stringify() is necessary to emulate I/O where a JavaScript string must be converted into a byte array encoded in UTF-8, whereas MessagePack's encode() returns a byte array.

Distribution
NPM / npmjs.com

The NPM package distributed in npmjs.com includes both ES2015+ and ES5 files:

  • dist/ is compiled into ES2019 with CommomJS, provided for NodeJS v10
  • dist.es5+umd/ is compiled into ES5 with UMD
    • dist.es5+umd/msgpack.min.js - the minified file
    • dist.es5+umd/msgpack.js - the non-minified file
  • dist.es5+esm/ is compiled into ES5 with ES modules, provided for webpack-like bundlers and NodeJS's ESM-mode

If you use NodeJS and/or webpack, their module resolvers use the suitable one automatically.

CDN / unpkg.com

This library is available via CDN:

<script crossorigin src="https://unpkg.com/@msgpack/msgpack"></script>

It loads MessagePack module to the global object.

Deno Support

You can use this module on Deno.

See example/deno-*.ts for examples.

deno.land/x is not supported yet.

Maintenance
Testing

For simple testing:

npm run test
Continuous Integration

This library uses Travis CI.

test matrix:

  • TypeScript targets
    • target=es2019 / target=es5
  • JavaScript engines
    • NodeJS, browsers (Chrome, Firefox, Safari, IE11, and so on)

See test:* in package.json and .travis.yml for details.

Release Engineering
# run tests on NodeJS, Chrome, and Firefox
make test-all

# edit the changelog
code CHANGELOG.md

# bump version
npm version patch|minor|major

# run the publishing task
make publish
Updating Dependencies
npm run update-dependencies
License

Copyright 2019 The MessagePack community.

This software uses the ISC license:

https://opensource.org/licenses/ISC

See LICENSE for details.

kawanet/msgpack-lite https://www.npmjs.com/package/msgpack-lite

msgpack-lite npm version Build Status

Fast Pure JavaScript MessagePack Encoder and Decoder

Sauce Test Status

Online demo: http://kawanet.github.io/msgpack-lite/

Features
  • Pure JavaScript only (No node-gyp nor gcc required)
  • Faster than any other pure JavaScript libraries on node.js v4
  • Even faster than node-gyp C++ based msgpack library (90% faster on encoding)
  • Streaming encoding and decoding interface is also available. It's more faster.
  • Ready for Web browsers including Chrome, Firefox, Safari and even IE8
  • Tested on Node.js v0.10, v0.12, v4, v5 and v6 as well as Web browsers
Encoding and Decoding MessagePack
var msgpack = require("msgpack-lite");

// encode from JS Object to MessagePack (Buffer)
var buffer = msgpack.encode({"foo": "bar"});

// decode from MessagePack (Buffer) to JS Object
var data = msgpack.decode(buffer); // => {"foo": "bar"}

// if encode/decode receives an invalid argument an error is thrown
Writing to MessagePack Stream
var fs = require("fs");
var msgpack = require("msgpack-lite");

var writeStream = fs.createWriteStream("test.msp");
var encodeStream = msgpack.createEncodeStream();
encodeStream.pipe(writeStream);

// send multiple objects to stream
encodeStream.write({foo: "bar"});
encodeStream.write({baz: "qux"});

// call this once you're done writing to the stream.
encodeStream.end();
Reading from MessagePack Stream
var fs = require("fs");
var msgpack = require("msgpack-lite");

var readStream = fs.createReadStream("test.msp");
var decodeStream = msgpack.createDecodeStream();

// show multiple objects decoded from stream
readStream.pipe(decodeStream).on("data", console.warn);
Decoding MessagePack Bytes Array
var msgpack = require("msgpack-lite");

// decode() accepts Buffer instance per default
msgpack.decode(Buffer([0x81, 0xA3, 0x66, 0x6F, 0x6F, 0xA3, 0x62, 0x61, 0x72]));

// decode() also accepts Array instance
msgpack.decode([0x81, 0xA3, 0x66, 0x6F, 0x6F, 0xA3, 0x62, 0x61, 0x72]);

// decode() accepts raw Uint8Array instance as well
msgpack.decode(new Uint8Array([0x81, 0xA3, 0x66, 0x6F, 0x6F, 0xA3, 0x62, 0x61, 0x72]));
Command Line Interface

A CLI tool bin/msgpack converts data stream from JSON to MessagePack and vice versa.

$ echo '{"foo": "bar"}' | ./bin/msgpack -Jm | od -tx1
0000000    81  a3  66  6f  6f  a3  62  61  72

$ echo '{"foo": "bar"}' | ./bin/msgpack -Jm | ./bin/msgpack -Mj
{"foo":"bar"}
Installation
$ npm install --save msgpack-lite
Tests

Run tests on node.js:

$ make test

Run tests on browsers:

$ make test-browser-local
open the following url in a browser:
http://localhost:4000/__zuul
Browser Build

Browser version msgpack.min.js is also available. 50KB minified, 14KB gziped.

<!--[if lte IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.10/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<![endif]-->
<script src="https://rawgit.com/kawanet/msgpack-lite/master/dist/msgpack.min.js"></script>
<script>
// encode from JS Object to MessagePack (Uint8Array)
var buffer = msgpack.encode({foo: "bar"});

// decode from MessagePack (Uint8Array) to JS Object
var array = new Uint8Array([0x81, 0xA3, 0x66, 0x6F, 0x6F, 0xA3, 0x62, 0x61, 0x72]);
var data = msgpack.decode(array);
</script>
MessagePack With Browserify

Step #1: write some code at first.

var msgpack = require("msgpack-lite");
var buffer = msgpack.encode({"foo": "bar"});
var data = msgpack.decode(buffer);
console.warn(data); // => {"foo": "bar"}

Proceed to the next steps if you prefer faster browserify compilation time.

Step #2: add browser property on package.json in your project. This refers the global msgpack object instead of including whole of msgpack-lite source code.

{
  "dependencies": {
    "msgpack-lite": "*"
  },
  "browser": {
    "msgpack-lite": "msgpack-lite/global"
  }
}

Step #3: compile it with browserify and uglifyjs.

browserify src/main.js -o tmp/main.browserify.js -s main
uglifyjs tmp/main.browserify.js -m -c -o js/main.min.js
cp node_modules/msgpack-lite/dist/msgpack.min.js js/msgpack.min.js

Step #4: load msgpack.min.js before your code.

<script src="js/msgpack.min.js"></script>
<script src="js/main.min.js"></script>
Interoperability

It is tested to have basic compatibility with other Node.js MessagePack modules below:

Benchmarks

A benchmark tool lib/benchmark.js is available to compare encoding/decoding speed (operation per second) with other MessagePack modules. It counts operations of 1KB JSON document in 10 seconds.

$ npm install msgpack msgpack-js msgpack-js-v5 msgpack-unpack msgpack5 notepack
$ npm run benchmark 10
operation op ms op/s
buf = Buffer(JSON.stringify(obj)); 1055200 10000 105520
obj = JSON.parse(buf); 863800 10000 86380
buf = require("msgpack-lite").encode(obj); 969100 10000 96910
obj = require("msgpack-lite").decode(buf); 600300 10000 60030
buf = require("msgpack").pack(obj); 503500 10001 50344
obj = require("msgpack").unpack(buf); 560200 10001 56014
buf = Buffer(require("msgpack.codec").msgpack.pack(obj)); 653500 10000 65349
obj = require("msgpack.codec").msgpack.unpack(buf); 367500 10001 36746
buf = require("msgpack-js-v5").encode(obj); 189500 10002 18946
obj = require("msgpack-js-v5").decode(buf); 408900 10000 40890
buf = require("msgpack-js").encode(obj); 189200 10000 18920
obj = require("msgpack-js").decode(buf); 375600 10002 37552
buf = require("msgpack5")().encode(obj); 110500 10009 11040
obj = require("msgpack5")().decode(buf); 165500 10000 16550
buf = require("notepack")().encode(obj); 847800 10000 84780
obj = require("notepack")().decode(buf); 599800 10000 59980
obj = require("msgpack-unpack").decode(buf); 48100 10002 4809

Streaming benchmark tool lib/benchmark-stream.js is also available. It counts milliseconds for 1,000,000 operations of 30 bytes fluentd msgpack fragment. This shows streaming encoding and decoding are super faster.

$ npm run benchmark-stream 2
operation (1000000 x 2) op ms op/s
stream.write(msgpack.encode(obj)); 1000000 3027 330360
stream.write(notepack.encode(obj)); 1000000 2012 497017
msgpack.Encoder().on("data",ondata).encode(obj); 1000000 2956 338294
msgpack.createEncodeStream().write(obj); 1000000 1888 529661
stream.write(msgpack.decode(buf)); 1000000 2020 495049
stream.write(notepack.decode(buf)); 1000000 1794 557413
msgpack.Decoder().on("data",ondata).decode(buf); 1000000 2744 364431
msgpack.createDecodeStream().write(buf); 1000000 1341 745712

Test environment: msgpack-lite 0.1.14, Node v4.2.3, Intel(R) Xeon(R) CPU E5-2666 v3 @ 2.90GHz

MessagePack Mapping Table

The following table shows how JavaScript objects (value) will be mapped to MessagePack formats and vice versa.

Source Value MessagePack Format Value Decoded
null, undefined nil format family null
Boolean (true, false) bool format family Boolean (true, false)
Number (32bit int) int format family Number (int or double)
Number (64bit double) float format family Number (double)
String str format family String
Buffer bin format family Buffer
Array array format family Array
Map map format family Map (if usemap=true)
Object (plain object) map format family Object (or Map if usemap=true)
Object (see below) ext format family Object (see below)

Note that both null and undefined are mapped to nil 0xC1 type. This means undefined value will be upgraded to null in other words.

Extension Types

The MessagePack specification allows 128 application-specific extension types. The library uses the following types to make round-trip conversion possible for JavaScript native objects.

Type Object Type Object
0x00 0x10
0x01 EvalError 0x11 Int8Array
0x02 RangeError 0x12 Uint8Array
0x03 ReferenceError 0x13 Int16Array
0x04 SyntaxError 0x14 Uint16Array
0x05 TypeError 0x15 Int32Array
0x06 URIError 0x16 Uint32Array
0x07 0x17 Float32Array
0x08 0x18 Float64Array
0x09 0x19 Uint8ClampedArray
0x0A RegExp 0x1A ArrayBuffer
0x0B Boolean 0x1B Buffer
0x0C String 0x1C
0x0D Date 0x1D DataView
0x0E Error 0x1E
0x0F Number 0x1F

Other extension types are mapped to built-in ExtBuffer object.

Custom Extension Types (Codecs)

Register a custom extension type number to serialize/deserialize your own class instances.

var msgpack = require("msgpack-lite");

var codec = msgpack.createCodec();
codec.addExtPacker(0x3F, MyVector, myVectorPacker);
codec.addExtUnpacker(0x3F, myVectorUnpacker);

var data = new MyVector(1, 2);
var encoded = msgpack.encode(data, {codec: codec});
var decoded = msgpack.decode(encoded, {codec: codec});

function MyVector(x, y) {
  this.x = x;
  this.y = y;
}

function myVectorPacker(vector) {
  var array = [vector.x, vector.y];
  return msgpack.encode(array); // return Buffer serialized
}

function myVectorUnpacker(buffer) {
  var array = msgpack.decode(buffer);
  return new MyVector(array[0], array[1]); // return Object deserialized
}

The first argument of addExtPacker and addExtUnpacker should be an integer within the range of 0 and 127 (0x0 and 0x7F). myClassPacker is a function that accepts an instance of MyClass, and should return a buffer representing that instance. myClassUnpacker is the opposite: it accepts a buffer and should return an instance of MyClass.

If you pass an array of functions to addExtPacker or addExtUnpacker, the value to be encoded/decoded will pass through each one in order. This allows you to do things like this:

codec.addExtPacker(0x00, Date, [Number, msgpack.encode]);

You can also pass the codec option to msgpack.Decoder(options), msgpack.Encoder(options), msgpack.createEncodeStream(options), and msgpack.createDecodeStream(options).

If you wish to modify the default built-in codec, you can access it at msgpack.codec.preset.

Custom Codec Options

msgpack.createCodec() function accepts some options.

It does NOT have the preset extension types defined when no options given.

var codec = msgpack.createCodec();

preset: It has the preset extension types described above.

var codec = msgpack.createCodec({preset: true});

safe: It runs a validation of the value before writing it into buffer. This is the default behavior for some old browsers which do not support ArrayBuffer object.

var codec = msgpack.createCodec({safe: true});

useraw: It uses raw formats instead of bin and str.

var codec = msgpack.createCodec({useraw: true});

int64: It decodes msgpack's int64/uint64 formats with int64-buffer object.

var codec = msgpack.createCodec({int64: true});

binarraybuffer: It ties msgpack's bin format with ArrayBuffer object, instead of Buffer object.

var codec = msgpack.createCodec({binarraybuffer: true, preset: true});

uint8array: It returns Uint8Array object when encoding, instead of Buffer object.

var codec = msgpack.createCodec({uint8array: true});

usemap: Uses the global JavaScript Map type, if available, to unpack MessagePack map elements.

var codec = msgpack.createCodec({usemap: true});
Compatibility Mode

The compatibility mode respects for msgpack's old spec. Set true to useraw.

// default mode handles both str and bin formats individually
msgpack.encode("Aa"); // => <Buffer a2 41 61> (str format)
msgpack.encode(new Buffer([0x41, 0x61])); // => <Buffer c4 02 41 61> (bin format)

msgpack.decode(new Buffer([0xa2, 0x41, 0x61])); // => 'Aa' (String)
msgpack.decode(new Buffer([0xc4, 0x02, 0x41, 0x61])); // => <Buffer 41 61> (Buffer)

// compatibility mode handles only raw format both for String and Buffer
var options = {codec: msgpack.createCodec({useraw: true})};
msgpack.encode("Aa", options); // => <Buffer a2 41 61> (raw format)
msgpack.encode(new Buffer([0x41, 0x61]), options); // => <Buffer a2 41 61> (raw format)

msgpack.decode(new Buffer([0xa2, 0x41, 0x61]), options); // => <Buffer 41 61> (Buffer)
msgpack.decode(new Buffer([0xa2, 0x41, 0x61]), options).toString(); // => 'Aa' (String)
Repository See Also License

The MIT License (MIT)

Copyright (c) 2015-2016 Yusuke Kawasaki

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

ygoe/msgpack.js https://github.com/ygoe/msgpack.js

msgpack.js

This is a MessagePack serializer and deserializer written in JavaScript for web browsers (including IE 11) and Node.js.

It is compact but still fully-featured. This library supports the complete MessagePack specification released on 2017-08-09, including date/time values. No other extension types are implemented in this library, it’s only the standard types which is perfectly fine for interoperability with MessagePack codecs in other programming languages.

I’m using the MessagePack-CSharp library on the server side in my .NET applications.

NPM

MessagePack

MessagePack is an efficient binary serialisation format. It lets you exchange data among multiple languages like JSON. But it’s faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.

Size

This library is very lightweight. The source code has around 510 lines (incl. browser/Node detection), the minified file has 6.6 kB and can be GZip-compressed to 2.4 kB.

Performance

The file msgpack-tests.html contains some tests and a benchmark function that compares this library with msgpack-lite. Here are the results, in milliseconds (lower is better). All tests done on an Intel Core i7-3770 and Windows 10.

Function Chrome 72 Firefox 65 Edge 16 IE 11  
msgpack.js serialize 702 ms +6% 1232 ms −42% 2483 ms +41% 2493 ms −3%
msgpack-lite encode 663 ms 2124 ms 1762 ms 2578 ms
msgpack.js deserialize 652 ms +13% 869 ms +5% 821 ms −48% 651 ms −68%
msgpack-lite decode 577 ms 827 ms 1587 ms 2021 ms

The numbers show that this library is comparable with msgpack-lite. In Chrome it’s only 10% slower. But serializing in Firefox and deserializing in Microsoft browsers is twice as fast.

Usage
Browser

In browsers, a global msgpack object is created that contains the functions serialize and deserialize. The first can be called with any data and returns the serialized bytes. The second works in reverse, taking the serialized bytes and returning the runtime value.

Include the JavaScript file into your HTML document like this:

<script src="msgpack.min.js"></script>

You can use the library functions after loading the script.

If there should be a naming conflict with another library you want to load, you can change the global object name from msgpack to something else by setting msgpackJsName before loading the script file:

<script>
    msgpackJsName = "msgpackJs";
</script>
<script src="msgpack.min.js"></script>
Node.js

In Node.js, these functions are exported in the object you get from the require function.

var msgpack = require('@ygoe/msgpack');
Example

Here’s a simple example:

// Define some data
var sourceData = {
    number: 123,
    number2: -0.129,
    text: "Abc with Üñıçôðé and ユニコード",
    flag: true,
    list: [ 1, 2, 3 ],
    obj: { a: 1, b: "2", c: false, d: { a: 0, b: -1 } },
    time: Date.now()
};

// Serialize to byte array
var bytes = msgpack.serialize(sourceData);

// Deserialize again
var deserializedData = msgpack.deserialize(bytes);
Compatibility

You can also use the functions encode and decode which are aliases to serialize and deserialize. This makes it easier to replace other libraries that use these function names with msgpack.js.

New projects should use the preferred (and more precisely named) serialize and deserialize functions though.

License

MIT license

kriszyp/msgpackr https://github.com/kriszyp/msgpackr

msgpackr

npm version npm version encode decode types module license

The msgpackr package is an extremely fast MessagePack NodeJS/JavaScript implementation. Currently, it is significantly faster than any other known implementations, faster than Avro (for JS), and generally faster than native V8 JSON.stringify/parse, on NodeJS. It also includes an optional record extension (the r in msgpackr), for defining record structures that makes MessagePack even faster and more compact, often over twice as fast as even native JSON functions, several times faster than other JS implementations, and 15-50% more compact. See the performance section for more details. Structured cloning (with support for cyclical references) is also supported through optional extensions.

Basic Usage

Install with:

npm i msgpackr

And import or require it for basic standard serialization/encoding (pack) and deserialization/decoding (unpack) functions:

import { unpack, pack } from 'msgpackr';
let serializedAsBuffer = pack(value);
let data = unpack(serializedAsBuffer);

This pack function will generate standard MessagePack without any extensions that should be compatible with any standard MessagePack parser/decoder. It will serialize JavaScript objects as MessagePack maps by default. The unpack function will deserialize MessagePack maps as an Object with the properties from the map.

Node Usage

The msgpackr package runs on any modern JS platform, but is optimized for NodeJS usage (and will use a node addon for performance boost as an optional dependency).

Streams

We can use the including streaming functionality (which further improves performance). The PackrStream is a NodeJS transform stream that can be used to serialize objects to a binary stream (writing to network/socket, IPC, etc.), and the UnpackrStream can be used to deserialize objects from a binary sream (reading from network/socket, etc.):

import { PackrStream } from 'msgpackr';
let stream = new PackrStream();
stream.write(myData);

Or for a full example of sending and receiving data on a stream:

import { PackrStream } from 'msgpackr';
let sendingStream = new PackrStream();
let receivingStream = new UnpackrStream();
// we are just piping to our own stream, but normally you would send and
// receive over some type of inter-process or network connection.
sendingStream.pipe(receivingStream);
sendingStream.write(myData);
receivingStream.on('data', (data) => {
	// received data
});

The PackrStream and UnpackrStream instances will have also the record structure extension enabled by default (see below).

Deno Usage

Msgpackr modules are standard ESM modules and can be loaded directly from github (https://raw.githubusercontent.com/kriszyp/msgpackr/master/index.js) or downloaded and used directly in Deno. The standard pack/encode and unpack/decode functionality is available on Deno, like other platforms.

Browser Usage

Msgpackr works as standalone JavaScript as well, and runs on modern browsers. It includes a bundled script, at dist/index.js for ease of direct loading:

<script src="node_modules/msgpackr/dist/index.js"></script>

This is UMD based, and will register as a module if possible, or create a msgpackr global with all the exported functions.

For module-based development, it is recommended that you directly import the module of interest, to minimize dependencies that get pulled into your application:

import { unpack } from 'msgpackr/unpack' // if you only need to unpack
Structured Cloning

You can also use msgpackr for structured cloning. By enabling the structuredClone option, you can include references to other objects or cyclic references, and object identity will be preserved. Structured cloning also enables preserving certain typed objects like Error, Set, RegExp and TypedArray instances. For example:

let obj = {
	set: new Set(['a', 'b']),
	regular: /a\spattern/
};
obj.self = obj;
let packr = new Packr({ structuredClone: true });
let serialized = packr.pack(obj);
let copy = packr.unpack(serialized);
copy.self === copy // true
copy.set.has('a') // true

This option is disabled by default because it uses extensions and reference checking degrades performance (by about 25-30%). (Note this implementation doesn't serialize every class/type specified in the HTML specification since not all of them make sense for storing across platforms.)

Alternate Terminology

If you prefer to use encoder/decode terminology, msgpackr exports aliases, so decode is equivalent to unpack, encode is pack, Encoder is Packr, Decoder is Unpackr, and EncoderStream and DecoderStream can be used as well.

Record / Object Structures

There is a critical difference between maps (or dictionaries) that hold an arbitrary set of keys and values (JavaScript Map is designed for these), and records or object structures that have a well-defined set of fields. Typical JS objects/records may have many instances re(use) the same structure. By using the record extension, this distinction is preserved in MessagePack and the encoding can reuse structures and not only provides better type preservation, but yield much more compact encodings and increase decoding performance by 2-3x. Msgpackr automatically generates record definitions that are reused and referenced by objects with the same structure. There are a number of ways to use this to our advantage. For large object structures with repeating nested objects with similar structures, simply serializing with the record extension can yield significant benefits. To use the record structures extension, we create a new Packr instance. By default a new Packr instance will have the record extension enabled:

import { Packr } from 'msgpackr';
let packr = new Packr();
packr.pack(bigDataWithLotsOfObjects);

Another way to further leverage the benefits of the msgpackr record structures is to use streams that naturally allow for data to reuse based on previous record structures. The stream classes have the record structure extension enabled by default and provide excellent out-of-the-box performance.

When creating a new Packr, Unpackr, PackrStream, or UnpackrStream instance, we can enable or disable the record structure extension with the useRecords property. When this is false, the record structure extension will be disabled (standard/compatibility mode), and all objects will revert to being serialized using MessageMap maps, and all maps will be deserialized to JS Objects as properties (like the standalone pack and unpack functions).

Streaming with record structures works by encoding a structure the first time it is seen in a stream and referencing the structure in later messages that are sent across that stream. When an encoder can expect a decoder to understand previous structure references, this can be configured using the sequential: true flag, which is auto-enabled by streams, but can also be used with Packr instances.

Shared Record Structures

Another useful way of using msgpackr, and the record extension, is for storing data in a databases, files, or other storage systems. If a number of objects with common data structures are being stored, a shared structure can be used to greatly improve data storage and deserialization efficiency. In the simplest form, provide a structures array, which is updated if any new object structure is encountered:

import { Packr } from 'msgpackr';
let packr = new Packr({
	structures: [... structures that were last generated ...]
});

If you are working with persisted data, you will need to persist the structures data when it is updated. Msgpackr provides an API for loading and saving the structures on demand (which is robust and can be used in multiple-process situations where other processes may be updating this same structures array), we just need to provide a way to store the generated shared structure so it is available to deserialize stored data in the future:

import { Packr } from 'msgpackr';
let packr = new Packr({
	getStructures() {
		// storing our data in file (but we could also store in a db or key-value store)
		return unpack(readFileSync('my-shared-structures.mp')) || [];
	},
	saveStructures(structures) {
		writeFileSync('my-shared-structures.mp', pack(structures));
	}
});

Msgpackr will automatically add and saves structures as it encounters any new object structures (up to a limit of 32, by default). It will always add structures in an incremental/compatible way: Any object encoded with an earlier structure can be decoded with a later version (as long as it is persisted).

Shared Structures Options

By default there is a limit of 32 shared structures. This default is designed to record common shared structures, but also be resilient against sharing too many structures if there are many objects with dynamic properties that are likely to be repeated. This also allows for slightly more efficient one byte encoding. However, if your application has more structures that are commonly repeated, you can increase this limit by setting maxSharedStructures to a higher value. The maximum supported shared structures is 8160.

You can also provide a shouldShareStructure function in the options if you want to specifically indicate which structures should be shared. This is called during the encoding process with the array of keys for a structure that is being considered for addition to the shared structure. For example, you might want:

	maxSharedStructures: 100,
	shouldShareStructure(keys) {
		return !(keys[0] > 1) // don't share structures that consist of numbers as keys
	}
Reading Multiple Values

If you have a buffer with multiple values sequentially encoded, you can choose to parse and read multiple values. This can be done using the unpackMultiple function/method, which can return an array of all the values it can sequentially parse within the provided buffer. For example:

let data = new Uint8Array([1, 2, 3]) // encodings of values 1, 2, and 3
let values = unpackMultiple(data) // [1, 2, 3]

Alternately, you can provide a callback function that is called as the parsing occurs with each value, and can optionally terminate the parsing by returning false:

let data = new Uint8Array([1, 2, 3]) // encodings of values 1, 2, and 3
unpackMultiple(data, (value) => {
	// called for each value
	// return false if you wish to end the parsing
})
Options

The following options properties can be provided to the Packr or Unpackr constructor:

  • useRecords - Setting this to false disables the record extension and stores JavaScript objects as MessagePack maps, and unpacks maps as JavaScript Objects, which ensures compatibilty with other decoders.
  • structures - Provides the array of structures that is to be used for record extension, if you want the structures saved and used again. This array will be modified in place with new record structures that are serialized (if less than 32 structures are in the array).
  • structuredClone - This enables the structured cloning extensions that will encode object/cyclic references and additional built-in types/classes.
  • mapsAsObjects - If true, this will decode MessagePack maps and JS Objects with the map entries decoded to object properties. If false, maps are decoded as JavaScript Maps. This is disabled by default if useRecords is enabled (which allows Maps to be preserved), and is enabled by default if useRecords is disabled.
  • useFloat32 - This will enable msgpackr to encode non-integer numbers as float32. See next section for possible values.
  • variableMapSize - This will use varying map size definition (fixmap, map16, map32) based on the number of keys when encoding objects, which yields slightly more compact encodings (for small objects), but is typically 5-10% slower during encoding. This is necessary if you need to use objects with more than 65535 keys. This is only relevant when record extension is disabled.
  • copyBuffers - When decoding a MessagePack with binary data (Buffers are encoded as binary data), copy the buffer rather than providing a slice/view of the buffer. If you want your input data to be collected or modified while the decoded embedded buffer continues to live on, you can use this option (there is extra overhead to copying).
  • useTimestamp32 - Encode JS Dates in 32-bit format when possible by dropping the milliseconds. This is a more efficient encoding of dates. You can also cause dates to use 32-bit format by manually setting the milliseconds to zero (date.setMilliseconds(0)).
  • sequential - Encode structures in serialized data, and reference previously encoded structures with expectation that decoder will read the encoded structures in the same order as encoded, with unpackMultiple.
  • largeBigIntToFloat - If a bigint needs to be encoded that is larger than will fit in 64-bit integers, it will be encoded as a float-64 (otherwise will throw a RangeError).
  • encodeUndefinedAsNil - Encodes a value of undefined as a MessagePack nil, the same as a null.
  • int64AsNumber - This will decode uint64 and int64 numbers as standard JS numbers rather than as bigint numbers.
  • onInvalidDate - This can be provided as function that will be called when an invalid date is provided. The function can throw an error, or return a value that will be encoded in place of the invalid date. If not provided, an invalid date will be encoded as an invalid timestamp (which decodes with msgpackr back to an invalid date).
32-bit Float Options

By default all non-integer numbers are serialized as 64-bit float (double). This is fast, and ensures maximum precision. However, often real-world data doesn't not need 64-bits of precision, and using 32-bit encoding can be much more space efficient. There are several options that provide more efficient encodings. Using the decimal rounding options for encoding and decoding provides lossless storage of common decimal representations like 7.99, in more efficient 32-bit format (rather than 64-bit). The useFloat32 property has several possible options, available from the module as constants:

import { FLOAT32_OPTIONS } from 'msgpackr';
const { ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
  • ALWAYS (1) - Always will encode non-integers (absolute less than 2147483648) as 32-bit float.
  • DECIMAL_ROUND (3) - Always will encode non-integers as 32-bit float, and when decoding 32-bit float, round to the significant decimal digits (usually 7, but 6 or 8 digits for some ranges).
  • DECIMAL_FIT (4) - Only encode non-integers as 32-bit float if all significant digits (usually up to 7) can be unambiguously encoded as a 32-bit float, and decode/unpack with decimal rounding (same as above). This will ensure round-trip encoding/decoding without loss in precision and uses 32-bit when possible.

Note, that the performance is decreased with decimal rounding by about 20-25%, although if only 5% of your values are floating point, that will only have about a 1% impact overall.

In addition, msgpackr exports a roundFloat32(number) function that can be used to round floating point numbers to the maximum significant decimal digits that can be stored in 32-bit float, just as DECIMAL_ROUND does when decoding. This can be useful for determining how a number will be decoded prior to encoding it.

Performance
Native Acceleration

Msgpackr employs an optional native node-addon to accelerate the parsing of strings. This should be automatically installed and utilized on NodeJS. However, you can verify this by checking the isNativeAccelerationEnabled property that is exported from msgpackr. If this is false, the msgpackr-extract package may not have been properly installed, and you may want to verify that it is installed correctly:

import { isNativeAccelerationEnabled } from 'msgpackr'
if (!isNativeAccelerationEnabled)
	console.warn('Native acceleration not enabled, verify that install finished properly')
Benchmarks

Msgpackr is fast. Really fast. Here is comparison with the next fastest JS projects using the benchmark tool from msgpack-lite (and the sample data is from some clinical research data we use that has a good mix of different value types and structures). It also includes comparison to V8 native JSON functionality, and JavaScript Avro (avsc, a very optimized Avro implementation):

operation op ms op/s
buf = Buffer(JSON.stringify(obj)); 81600 5002 16313
obj = JSON.parse(buf); 90700 5004 18125
require("msgpackr").pack(obj); 169700 5000 33940
require("msgpackr").unpack(buf); 109700 5003 21926
msgpackr w/ shared structures: packr.pack(obj); 190400 5001 38072
msgpackr w/ shared structures: packr.unpack(buf); 422900 5000 84580
buf = require("msgpack-lite").encode(obj); 31300 5005 6253
obj = require("msgpack-lite").decode(buf); 15700 5007 3135
buf = require("@msgpack/msgpack").encode(obj); 103100 5003 20607
obj = require("@msgpack/msgpack").decode(buf); 59100 5004 11810
buf = require("notepack").encode(obj); 65500 5007 13081
obj = require("notepack").decode(buf); 33400 5009 6667
obj = require("msgpack-unpack").decode(buf); 6900 5036 1370
require("avsc")...make schema/type...type.toBuffer(obj); 89300 5005 17842
require("avsc")...make schema/type...type.fromBuffer(obj); 108400 5001 21675

All benchmarks were performed on Node 15 / V8 8.6 (Windows i7-4770 3.4Ghz). (avsc is schema-based and more comparable in style to msgpackr with shared structures).

Here is a benchmark of streaming data (again borrowed from msgpack-lite's benchmarking), where msgpackr is able to take advantage of the structured record extension and really demonstrate its performance capabilities:

operation (1000000 x 2) op ms op/s
new PackrStream().write(obj); 1000000 372 2688172
new UnpackrStream().write(buf); 1000000 247 4048582
stream.write(msgpack.encode(obj)); 1000000 2898 345065
stream.write(msgpack.decode(buf)); 1000000 1969 507872
stream.write(notepack.encode(obj)); 1000000 901 1109877
stream.write(notepack.decode(buf)); 1000000 1012 988142
msgpack.Encoder().on("data",ondata).encode(obj); 1000000 1763 567214
msgpack.createDecodeStream().write(buf); 1000000 2222 450045
msgpack.createEncodeStream().write(obj); 1000000 1577 634115
msgpack.Decoder().on("data",ondata).decode(buf); 1000000 2246 445235

See the benchmark.md for more benchmarks and information about benchmarking.

Custom Extensions

You can add your own custom extensions, which can be used to encode specific types/classes in certain ways. This is done by using the addExtension function, and specifying the class, extension type code (should be a number from 1-100, reserving negatives for MessagePack, 101-127 for msgpackr), and your pack and unpack functions (or just the one you need).

import { addExtension, Packr } from 'msgpackr';

class MyCustomClass {...}

let extPackr = new Packr();
addExtension({
	Class: MyCustomClass,
	type: 11, // register your own extension code (a type code from 1-100)
	pack(instance) {
		// define how your custom class should be encoded
		return Buffer.from([instance.myData]); // return a buffer
	}
	unpack(buffer) {
		// define how your custom class should be decoded
		let instance = new MyCustomClass();
		instance.myData = buffer[0];
		return instance; // decoded value from buffer
	}
});

If you want to use msgpackr to encode and decode the data within your extensions, you can use the read and write functions and read and write data/objects that will be encoded and decoded by msgpackr, which can be easier and faster than creating and receiving separate buffers (note that you can't just return the instance from write or msgpackr will recursively try to use extension infinitely):

import { addExtension, Packr } from 'msgpackr';

class MyCustomClass {...}

let extPackr = new Packr();
addExtension({
	Class: MyCustomClass,
	type: 11, // register your own extension code (a type code from 1-100)
	write(instance) {
		// define how your custom class should be encoded
		return instance.myData; // return some data to be encoded
	}
	read(data) {
		// define how your custom class should be decoded,
		// data will already be unpacked/decoded
		let instance = new MyCustomClass();
		instance.myData = data;
		return instance; // return decoded value
	}
});

You can also create an extension with Class and write methods, but no type (or read), if you just want to customize how a class is serialized without using MessagePack extension encoding.

Additional Performance Optimizations

Msgpackr is already fast, but here are some tips for making it faster:

Buffer Reuse

Msgpackr is designed to work well with reusable buffers. Allocating new buffers can be relatively expensive, so if you have Node addons, it can be much faster to reuse buffers and use memcpy to copy data into existing buffers. Then msgpackr unpack can be executed on the same buffer, with new data, and optionally take a second paramter indicating the effective size of the available data in the buffer.

Arena Allocation (useBuffer())

During the serialization process, data is written to buffers. Again, allocating new buffers is a relatively expensive process, and the useBuffer method can help allow reuse of buffers that will further improve performance. With useBuffer method, you can provide a buffer, serialize data into it, and when it is known that you are done using that buffer, you can call useBuffer again to reuse it. The use of useBuffer is never required, buffers will still be handled and cleaned up through GC if not used, it just provides a small performance boost.

Record Structure Extension Definition

The record struction extension uses extension id 0x72 ("r") to declare the use of this functionality. The extension "data" byte (or bytes) identifies the byte or bytes used to identify the start of a record in the subsequent MessagePack block or stream. The identifier byte (or the first byte in a sequence) must be from 0x40 - 0x7f (and therefore replaces one byte representations of positive integers 64 - 127, which can alternately be represented with int or uint types). The extension declaration must be immediately follow by an MessagePack array that defines the field names of the record structure.

Once a record identifier and record field names have been defined, the parser/decoder should proceed to read the next value. Any subsequent use of the record identifier as a value in the block or stream should parsed as a record instance, and the next n values, where is n is the number of fields (as defined in the array of field names), should be read as the values of the fields. For example, here we have defined a structure with fields "foo" and "bar", with the record identifier 0x40, and then read a record instance that defines the field values of 4 and 2, respectively:

+--------+--------+--------+~~~~~~~~~~~~~~~~~~~~~~~~~+--------+--------+--------+
|  0xd4  |  0x72  |  0x40  | array: [ "foo", "bar" ] |  0x40  |  0x04  |  0x02  |
+--------+--------+--------+~~~~~~~~~~~~~~~~~~~~~~~~~+--------+--------+--------+

Which should generate an object that would correspond to JSON:

{ "foo": 4, "bar": 2}
Additional value types

msgpackr supports undefined (using fixext1 + type: 0 + data: 0 to match other JS implementations), NaN, Infinity, and -Infinity (using standard IEEE 754 representations with doubles/floats).

Dates

msgpackr saves all JavaScript Dates using the standard MessagePack date extension (type -1), using the smallest of 32-bit, 64-bit or 96-bit format needed to store the date without data loss (or using 32-bit if useTimestamp32 options is specified).

Structured Cloning

With structured cloning enabled, msgpackr will also use extensions to store Set, Map, Error, RegExp, ArrayBufferView objects and preserve their types.

Alternate Encoding/Package

The high-performance serialization and deserialization algorithms in the msgpackr package are also available in the cbor-x for the CBOR format, with the same API and design. A quick summary of the pros and cons of using MessagePack vs CBOR are:

  • MessagePack has wider adoption, and, at least with this implementation is slightly more efficient (by roughly 1%).
  • CBOR has an official IETF standardization track, and the record extensions is conceptually/philosophically a better fit for CBOR tags.
License

MIT

Browser Consideration

MessagePack can be a great choice for high-performance data delivery to browsers, as reasonable data size is possible without compression. And msgpackr works very well in modern browsers. However, it is worth noting that if you want highly compact data, brotli or gzip are most effective in compressing, and MessagePack's character frequency tends to defeat Huffman encoding used by these standard compression algorithms, resulting in less compact data than compressed JSON.

Credits

Various projects have been inspirations for this, and code has been borrowed from https://github.com/msgpack/msgpack-javascript and https://github.com/mtth/avsc.

davalapar/what-the-pack http://npmjs.com/package/what-the-pack

what-the-pack

Ultra-fast MessagePack for NodeJS & Browsers.

implementation notes
  • this implementation uses pre-allocated buffers and buffer.copy() for encoding, instead of regular arrays
  • uses a buffer polyfill if used in browser environments
  • has dictionary support, to further reduce payload size
backward compatibility notes with other libraries
  • used extensions
    • fixext 1, type 0, data 0 = undefined
    • fixext 1, type 0, data 1 = NaN
    • fixext 1, type 0, data 2 = +Infinity
    • fixext 1, type 0, data 3 = -Infinity
  • Buffers, ArrayBuffers and TypedArrays
    • Buffers : encoded as Buffers, decoded as Buffers
    • ArrayBuffers : encoded as Buffers, decoded as Buffers
    const decoded = decode(encoded);
    const your_arraybuffer = decoded.buffer;
    • TypedArrays : encoded as Buffers, decoded as Buffers
    const decoded = decode(encoded);
    const your_typedarray = new Uint8Array(decoded.buffer);
usage
yarn add what-the-pack
const MessagePack = require('what-the-pack');
const { encode, decode } = MessagePack.initialize(2**22); // 4MB

const data = {
  name: 'Lunox',
  age: 20
};

const encoded = encode(data);
const decoded = decode(encoded);

console.log({
  encoded,
  decoded
});
result
{ encoded: <Buffer 82 a4 6e 61 6d 65 a5 4c 75 6e 6f 78 a3 61 67 65 14>,
  decoded: { name: 'Lunox', age: 20 } }
pre-allocating a larger buffer
const MessagePack = require('what-the-pack');
const { encode, decode } = MessagePack.initialize(2**30); // 1GB
const data = {
  // large data goes here
};
2^7 = 128 B
2^8 = 256 B
2^9 = 512 B
2^10 = 1.02 kB
2^11 = 2.05 kB
2^12 = 4.1 kB
2^13 = 8.19 kB
2^14 = 16.4 kB
2^15 = 32.8 kB
2^16 = 65.5 kB
2^17 = 131 kB
2^18 = 262 kB
2^19 = 524 kB
2^20 = 1.05 MB
2^21 = 2.1 MB
2^22 = 4.19 MB
2^23 = 8.39 MB
2^24 = 16.8 MB
2^25 = 33.6 MB
2^26 = 67.1 MB
2^27 = 134 MB
2^28 = 268 MB
2^29 = 537 MB
2^30 = 1.07 GB
using dictionaries (added in 1.1.3)
  • this feature isn't in MessagePack spec but added as a convenience feature in 1.1.3
  • dictionaries allow us to decrease our buffer output size by recognizing strings used as object keys and replacing them with shorter-byte integer values during the encoding process
  • these shorter-byte placeholder values are then restored to their respective strings during the decoding process
  • the trade-off in using dictionaries is an insignificantly slower encoding and decoding time in exchange of a significantly smaller buffer output, which results into a lower network bandwidth and storage consumption in the long run
  • the best part: the byte placeholders starts from -32 then increments upwards, values -32 to 127 are encoded in single byte, which means your first (32 + 128) = 160 keys will be encoded as a single byte instead of encoding the whole string
const MessagePack = require('what-the-pack');
const { encode, decode, register } = MessagePack.initialize(2**22); // 4MB
let encoded, decoded, data;
data = { name: 'Lunox', age: 20 };

encoded = encode(data);
decoded = decode(encoded);
console.log({ encoded, decoded });
/**
 - encoded: <Buffer 82 a4 6e 61 6d 65 a5 4c 75 6e 6f 78 a3 61 67 65 14> (17)
 - decoded: { name: 'Lunox', age: 20 }
 **/

register('name', 'age');
encoded = encode(data);
decoded = decode(encoded);
console.log({ encoded, decoded });
/**
 - encoded: <Buffer 82 e0 a5 4c 75 6e 6f 78 e1 14> (10)
 - decoded: { name: 'Lunox', age: 20 }
 **/
minified build for browsers
<!-- latest umd build -->
<script src="https://unpkg.com/what-the-pack/dist/MessagePack.min.js"></script>

<!-- exposed as 'MessagePack' -->
<script>
  const { encode, decode } = MessagePack.initialize(2**22); // 4MB
  const data = {
    name: 'Lunox',
    age: 20
  };
  const encoded = encode(data);
  const decoded = decode(encoded);
  console.log({ encoded, decoded });
</script>
using with browser websockets
server
const WebSocket = require('ws');
const MessagePack = require('what-the-pack');
const { encode, decode } = MessagePack.initialize(2**22); // 4MB

const wss = new WebSocket.Server(
  /- options go here */
);
wss.on('connection', (client, req) => {
  console.log('A client has connected.');
  console.log('IP address:', req.connection.remoteAddress);
  client.send(
    encode({
      message: 'something'
    })
  );
});
client
  • On browsers, Buffer object is exposed as MessagePack.Buffer
  • On browsers, call MessagePack.Buffer.from(x) on received ArrayBuffers
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
const { encode, decode, Buffer } = MessagePack.initialize(2**22); // 4MB

// Connection opened
socket.addEventListener('open', (event) => {
  socket.binaryType = 'arraybuffer'; // important
  console.log('Connected to server.');
});

// Listen for messages
socket.addEventListener('message', (event) => {
  const data = MessagePack.decode(
    Buffer.from(event.data)
  );
  console.log(data);
  // logs: { message: 'something' }
});
benchmarks
yarn run benchmark
$ yarn run benchmark
JSON stringify tiny x 1,477,866 ops/sec ±0.58% (93 runs sampled)
JSON stringify small x 232,645 ops/sec ±0.25% (91 runs sampled)
JSON stringify medium x 117,357 ops/sec ±0.31% (93 runs sampled)
JSON stringify large x 24.01 ops/sec ±0.37% (43 runs sampled)
JSON parse tiny x 1,301,925 ops/sec ±3.18% (82 runs sampled)
JSON parse small x 264,410 ops/sec ±0.57% (90 runs sampled)
JSON parse medium x 133,865 ops/sec ±0.52% (87 runs sampled)
JSON parse large x 31.52 ops/sec ±0.34% (53 runs sampled)
what-the-pack encode tiny x 1,175,981 ops/sec ±0.39% (92 runs sampled)
what-the-pack encode small x 365,533 ops/sec ±0.85% (90 runs sampled)
what-the-pack encode medium x 173,746 ops/sec ±0.41% (91 runs sampled)
what-the-pack encode large x 218 ops/sec ±0.85% (82 runs sampled)
what-the-pack decode tiny x 1,130,260 ops/sec ±0.30% (91 runs sampled)
what-the-pack decode small x 254,931 ops/sec ±0.79% (94 runs sampled)
what-the-pack decode medium x 146,809 ops/sec ±0.79% (92 runs sampled)
what-the-pack decode large x 211 ops/sec ±0.37% (87 runs sampled)
notepack.encode tiny x 1,291,361 ops/sec ±0.22% (95 runs sampled)
notepack encode small x 325,882 ops/sec ±1.20% (95 runs sampled)
notepack encode medium x 133,398 ops/sec ±0.20% (94 runs sampled)
notepack encode large x 231 ops/sec ±1.65% (81 runs sampled)
notepack decode tiny x 1,097,597 ops/sec ±0.67% (93 runs sampled)
notepack decode small x 231,895 ops/sec ±0.69% (96 runs sampled)
notepack decode medium x 137,385 ops/sec ±2.45% (86 runs sampled)
notepack decode large x 210 ops/sec ±0.85% (86 runs sampled)
tests
yarn run test
$ yarn run test
 PASS  ./test.js
  √ fixstr (6ms)
  √ str 8 (2ms)
  √ str 16 (1ms)
  √ str 32 (1ms)
  √ zero
  √ positive fixint (1ms)
  √ negative  fixint (1ms)
  √ uint 8 (1ms)
  √ uint 16 (1ms)
  √ uint 32
  √ uint 64 (1ms)
  √ int 8 (1ms)
  √ int 16
  √ int 32 (2ms)
  √ int 64
  √ float 32 (2ms)
  √ float 64 (1ms)
  √ true, false, undefined, NaN, +Infinity, -Infinity (2ms)
  √ flat & nested empty arrays (1ms)
  √ flat arrays (456ms)
  √ nested arrays (5ms)
  √ highly nested arrays (2ms)
  √ buffers, bin8 (2ms)
  √ buffers, bin16 (96ms)
  √ buffers, bin32 (473ms)
  √ arraybuffers as buffer (54ms)
  √ typedarrays as buffer (7ms)
  √ tiny object (1ms)
  √ small object
  √ medium object (1ms)
  √ large object (1736ms)

  console.log index.js:49
    MessagePack: Setting buffer limit to 1.07 GB

Test Suites: 1 passed, 1 total
Tests:       31 passed, 31 total
Snapshots:   0 total
Time:        5.477s
Ran all test suites.
Done in 6.59s.
changelog
  • 1.x
    • basic support
    • dictionary support
  • 2.0.0
    • rewrite to use raw functions instead of classes
    • update dev-deps
    • jest test-cov @ 86.06%
      • statements 389/452
      • branches 137/169
      • functions 11/12
      • lines 374/428
  • 2.0.x
    • fix tempBufferLength check
    • rebuild for browser
    • fix leak on buffer decode
references

MIT | @davalapar

davethomas11/MoshiPack https://github.com/davethomas11/MoshiPack

MoshiPack

CircleCI

Gradle
implementation com.daveanthonythomas.moshipack:moshipack:1.0.1

Optional Retrofit support:

implementation com.daveanthonythomas.moshipack:moshipack-retrofit:1.0.1
About

This is a Kotilin implementation of MessagePack serialization and deserialization built ontop of Moshi to take advantage of Moshi's type adapters and utilizes okio for reading and writing MessagePack bytes.

The library is intended to be consumed in a Kotlin project, and is not intended for Java use.

Inspired by Kaushik Gopal's tweet

See Moshi for adapter usage and reference.

Convert an object to MessagePack format
data class MessagePackWebsitePlug(var compact: Boolean = true, var schema: Int = 0)

val moshiPack = MoshiPack()
val packed: BufferedSource = moshiPack.pack(MessagePackWebsitePlug())

println(packed.readByteString().hex())

This prints the MessagePack bytes as a hex string 82a7636f6d70616374c3a6736368656d6100

  • 82 - Map with two entries
  • a7 - String of seven bytes
  • 63 6f 6d 70 61 63 74 - UTF8 String "compact"
  • c3 - Boolean value true
  • a6 - String of size bytes
  • 73 63 68 65 6d 61 - UTF8 String "schema"
  • 00 - Integer value 0
Convert binary MessagePack back to an Object
val bytes = ByteString.decodeHex("82a7636f6d70616374c3a6736368656d6100").toByteArray()

val moshiPack = MoshiPack()
val plug: MessagePackWebsitePlug = moshiPack.unpack(bytes)
Static API

If you prefer to not instantiate a MoshiPack instance you can access the API in a static fashion as well. Note this will create a new Moshi instance every time you make an API call. You may want to use the API this way if you aren't providing MoshiPack by some form of dependency injection and you do not have any specific builder parameters for Moshi


Format Support

See MessagePack format spec for further reference.

format name first byte (in binary) first byte (in hex) Supported
positive fixint 0xxxxxxx 0x00 - 0x7f Yes
fixmap 1000xxxx 0x80 - 0x8f Yes
fixarray 1001xxxx 0x90 - 0x9f Yes
fixstr 101xxxxx 0xa0 - 0xbf Yes
nil 11000000 0xc0 Yes
(never used) 11000001 0xc1 Yes
false 11000010 0xc2 Yes
true 11000011 0xc3 Yes
bin 8 11000100 0xc4 No
bin 16 11000101 0xc5 No
bin 32 11000110 0xc6 No
ext 8 11000111 0xc7 No
ext 16 11001000 0xc8 No
ext 32 11001001 0xc9 No
float 32 11001010 0xca Yes
float 64 11001011 0xcb Yes
uint 8 11001100 0xcc Yes
uint 16 11001101 0xcd Yes
uint 32 11001110 0xce Yes
uint 64 11001111 0xcf Yes
int 8 11010000 0xd0 Yes
int 16 11010001 0xd1 Yes
int 32 11010010 0xd2 Yes
int 64 11010011 0xd3 Yes
fixext 1 11010100 0xd4 No
fixext 2 11010101 0xd5 No
fixext 4 11010110 0xd6 No
fixext 8 11010111 0xd7 No
fixext 16 11011000 0xd8 No
str 8 11011001 0xd9 Yes
str 16 11011010 0xda Yes
str 32 11011011 0xdb Yes
array 16 11011100 0xdc Yes
array 32 11011101 0xdd Yes
map 16 11011110 0xde Yes
map 32 11011111 0xdf Yes
negative fixint 111xxxxx 0xe0 - 0xff Yes

API
pack

Serializes an object into MessagePack. Returns: okio.BufferedSource

Instance version:

MoshiPack().pack(anyObject)

Static version:

MoshiPack.pack(anyObject)
packToByeArray

If you prefer to get a ByteArray instead of a BufferedSource you can use this method.

Instance version only

MoshiPack().packToByteArray(anObject)

Static can be done

MoshiPack.pack(anObject).readByteArray()
unpack

Deserializes MessagePack bytes into an Object. Returns: T: Any Works with ByteArray and okio.BufferedSource

Instance version:

// T must be valid type so Moshi knows what to deserialize to
val unpacked: T = MoshiPack().unpack(byteArray)

Static version:

val unpacked: T = MoshiPack.upack(byteArray)

Instance version:

val unpacked: T = MoshiPack().unpack(bufferedSource)

Static version:

val unpacked: T = MoshiPack.upack(bufferedSource)

T can be an Object, a List, a Map, and can include generics. Unlike Moshi you do not need to specify a parameterized type to deserialize to a List with generics. MoshiPack can infer the paramterized type for you.

The following examples are valid for MoshiPack:

A typed List

val listCars: List<Car> = MoshiPack.unpack(carMsgPkBytes)

A List of Any

val listCars: List<Any> = MoshiPack.unpack(carMsgPkBytes)

An Object

val car: Car = MoshiPack.unpack(carBytes)

A Map of Any, Any

val car: Map<Any, Any> = MoshiPack.unpack(carBytes)
msgpackToJson

Convert directly from MessagePack bytes to JSON. Use this method for the most effecient implementation as no objects are instantiated in the process. This uses the FormatInterchange class to match implementations of JsonReader and a JsonWriter. If you wanted to say support XML as a direct conversion to and from, you could implement Moshi's JsonReader and JsonWriter classes and use the FormatInterchange class to convert directly to other formats. Returns String containing a JSON representation of the MessagePack data

Instance versions: (takes ByteArray or BufferedSource)

MoshiPack().msgpackToJson(byteArray)
MoshiPack().msgpackToJson(bufferedSource)

Static versions: (takes ByteArray or BufferedSource)

MoshiPack.msgpackToJson(byteArray)
MoshiPack.msgpackToJson(bufferedSource)
jsonToMsgpack

Convert directly from JSON to MessagePack bytes. Use this method for the most effecient implementation as no objects are instantiated in the process. Returns BufferedSource

Instance versions: (takes String or BufferedSource)

MoshiPack().jsonToMsgpack(jsonString)
MoshiPack().jsonToMsgpack(bufferedSource)

Static versions: (takes String or BufferedSource)

MoshiPack.jsonToMsgpack(jsonString)
MoshiPack.jsonToMsgpack(bufferedSource)
MoshiPack - constructor + Moshi builder

The MoshiPack constructor takes an optional Moshi.Builder.() -> Unit lambda which is applied to the builder that is used to instantiate the Moshi instance it uses.

Example adding custom adapter:

val moshiPack = MoshiPack({
  add(customAdapter)
})

Moshi is also a settable property which can be changed on a MoshiPack instance:

val m = MoshiPack()
m.moshi = Moshi.Builder().build()

The static version of the API also can be passed a lambda to applied to the Moshi.Builder used to instantiate Moshi:

MoshiPack.pack(someBytes) { add(customAdapter) }
Forcing integers to write as certain format
  • new in v1.0.1

This will force all integers to be packed as the type given. By default the smallest message pack type is used for integers.

val moshiPack = MoshiPack().apply {
    writerOptions.writeAllIntsAs = MsgpackIntByte.INT_64
}

Kotiln Support

Since this library is intended for Kotlin use, the moshi-kotlin artifact is included as a depedency. A KotlinJsonAdapterFactory is added by default to the instantiated Moshi that MoshiPack uses. This adapter allows for the use of Moshi's annotaions in Kotlin. To learn more about it see the Moshi documentation.

If you'd like to use Moshi with out a KotlinJsonAdapterFactory supply a Moshi instance for MoshiPack:

MoshiPack(moshi = Moshi.Builder().build)
ProGuard

From Moshi's README.md; If you are using ProGuard you might need to add the following options:

-dontwarn okio.**
-dontwarn javax.annotation.**
-keepclasseswithmembers class * {
    @com.squareup.moshi.* <methods>;
}
-keep @com.squareup.moshi.JsonQualifier interface *
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}
Retrofit

An example of using the retorfit adapter can be found here: https://github.com/davethomas11/MoshiPack_AndroidAppExample

markstinson/lua-MessagePack http://fperrad.github.io/lua-MessagePack/

lua-MessagePack : a pure Lua implementation (spec v5)

Build Status

Introduction

MessagePack is an efficient binary serialization format.

It lets you exchange data among multiple languages like JSON but it's faster and smaller.

It's a pure Lua implementation, without dependency.

And it's really fast with LuaJIT.

Links

The homepage is at http://fperrad.github.io/lua-MessagePack/, and the sources are hosted at http://github.com/fperrad/lua-MessagePack/.

Copyright and License

Copyright (c) 2012-2013 Francois Perrad

This library is licensed under the terms of the MIT/X11 license, like Lua itself.

kieselsteini/msgpack https://github.com/kieselsteini/msgpack

MessagePack for Lua 5.3
Overview

This is a pure Lua implementation for encoding/decoding MessagePack (https://msgpack.org).

Please report any bugs you encounter!

Features:

  • written in pure Lua 5.3 (using string.pack() / string.unpack())
  • can distinguish between integer / float / double numbers
  • can distinguish between UTF-8 strings and binary data
  • public domain license (http://unlicense.org)
  • pretty fast decoding

What's missing:

  • extendend types fixent

Example code:

local msgpack = require('msgpack')

local value = msgpack.decode(binary_msgpack_data) -- decode to Lua value

local binary_data = msgpack.encode(lua_value) -- encode Lua value to MessagePack
API
msgpack.encode_one(value)

Encodes the given Lua value to a binary MessagePack representation. It will return the binary string on succes or nil plus an error message if it fails.

The encoder will encode Lua strings as MessagePack strings when they are properly UTF-8 encoded otherwise they will become MessagePack binary objects.

There is also a check if a Lua number can be lossless encoded as a 32-bit float.

NOTE: Empty Lua tables will be encoded as empty arrays!

msgpack.encode(...)

Encodes all given values to a binary MessagePack representation. It will return the binary string or nil plus an error message if it fails.

local binary = msgpack.encode('Hello', 1024, true, { 2, 3, 4 })
msgpack.decode_one(binary_data[, position])

Decode the given MessagePack binary string to a corresponding Lua value. It will return the decoded Lua value and the position for next byte in stream or nil plus an error message if decoding went wrong. You can use the returned position to decode multiple MessagePack values in a stream.

The optional position argument is used to start the decoding at a specific position inside the the binary_data string.

NOTE: Extended types are not supported. Decoding will fail!

NOTE: Binary data will be decoded as Lua strings

NOTE: Arrays will be decoded as Lua tables starting with index 1 (like Lua uses tables as arrays)

NOTE: Values which are nil will cause the key, value pair to disappear in a Lua table (that's how it works in Lua)

msgpack.decode(binary_data[, position])

Decode the given MessagePack binary string to one or more Lua values. It will return all decoded Lua values or nil plus an error message if decoding failed.

local a, b, c = msgpack.decode(binary)
License
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>

bastibe/matlab-msgpack https://github.com/bastibe/matlab-msgpack

A MessagePack implementation for Matlab and Octave

The code is written in pure Matlab, and has no dependencies beyond Matlab itself. And it works in recent versions of Octave, too.

The files in this repository are taken from Transplant.

Basic Usage:
data = {'life, the universe, and everything', struct('the_answer', 42)};
bytes = dumpmsgpack(data)
data = parsemsgpack(bytes)
% returns: {'life, the universe, and everything', containers.Map('the_answer', 42)}
Converting Matlab to MsgPack:
Matlab MsgPack
string string
scalar number
logical true/false
vector array of numbers
uint8 vector bin
matrix array of array of numbers
empty matrix nil
cell array array
cell matrix array of arrays
struct map
containers.Map map
struct array array of maps
handles raise error

There is no way of encoding exts

Converting MsgPack to Matlab
MsgPack Matlab
string string
number scalar
true/false logical
nil empty matrix
array cell array
map containers.Map
bin uint8
ext uint8

Note that since structs don't support arbitrary field names, they can't be used for representing maps. We use containers.Map instead.

Tests
runtests()
License

MATLAB (R) is copyright of the Mathworks

Copyright (c) 2014 Bastian Bechtold All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

akiradeveloper/msgpack-nim https://github.com/akiradeveloper/msgpack-nim

I will start this project once Nim compiler reaches 1.0

msgpack-nim

A MessagePack binding for Nim

Build Status

API: https://rawgit.com/akiradeveloper/msgpack-nim/master/msgpack.html

msgpack-nim currently provides only the basic functionality. Please see what's listed in Todo section. Compared to other language bindings, it's well-tested by 1000 auto-generated test cases by Haskell QuickCheck, which always runs on every commit to Github repository. Please try make quickcheck on your local machine to see what happens (It will take a bit while. Be patient). Have a nice packing!

Overview

Install
$ nimble update
$ nimble install msgpack
Example
import msgpack
import streams

# You can use any stream subclasses to serialize/deserialize
# messages. e.g. FileStream
let st: Stream = newStringStream()

assert(st.getPosition == 0)

# Type checking protects you from making trivial mistakes.
# Now we pack {"a":[5,-3], "b":[1,2,3]} but more complex
# combination of any Msg types is allowed.
#
# In xs we can mix specific conversion (PFixNum) and generic
# conversion (unwrap).
let xs: Msg = wrap(@[PFixNum(5), (-3).wrap])
let ys: Msg = wrap(@[("a".wrap, xs.wrap), ("b".wrap, @[1, 2, 3].wrap)])
st.pack(ys.wrap) # Serialize!

# We need to reset the cursor to the beginning of the target
# byte sequence.
st.setPosition(0)

let msg = st.unpack # Deserialize!

# output:
# a
# 5
# -3
# b
# 1
# 2
# 3
for e in msg.unwrapMap:
  echo e.key.unwrapStr
  for e in e.val.unwrapArray:
    echo e.unwrapInt
Todo
  • Implement unwrapInto to convert Msg object to Nim object handily
  • Evaluate performance and scalability
  • Talk with offical Ruby implementation
  • Don't repeat yourself: The code now has too much duplications. Using templates?
Author

Akira Hayakawa ([email protected])

jangko/msgpack4nim https://github.com/jangko/msgpack4nim

msgpack4nim

MessagePack implementation written in pure nim

why another implementation?

I am fully aware of another msgpack implementation written in nim. But I want something easier to use. Another motivation come from the nim language itself. The current version of nim compiler offer many improvements, including 'generics ' specialization. I found out nim compiler is smart enough to make serialization/deserialization to/from msgpack easy and convenient.

requirement: nim ver 0.18.1 or later

Build Status (Travis) Windows build status (Appveyor) nimble license

Example
import msgpack4nim, streams

type
  #lets try with a rather complex object
  CustomType = object
    count: int
    content: seq[int]
    name: string
    ratio: float
    attr: array[0..5, int]
    ok: bool

proc initCustomType(): CustomType =
  result.count = -1
  result.content = @[1,2,3]
  result.name = "custom"
  result.ratio = 1.0
  for i in 0..5: result.attr[i] = i
  result.ok = false

var x = initCustomType()
var s = MsgStream.init() # besides MsgStream, you can also use Nim StringStream or FileStream
s.pack(x) #here the magic happened

var ss = MsgStream.init(s.data)
var xx: CustomType
ss.unpack(xx) #and here too

assert xx == x
echo "OK ", xx.name

see? you only need to call 'pack' and 'unpack', and the compiler do the hard work for you. Very easy, convenient, and works well

if you think setting up a MsgStream too much for you, you can simply call pack(yourobject) and it will return a string containing msgpack data.

  var a = @[1,2,3,4,5,6,7,8,9,0]
  var buf = pack(a)
  var aa: seq[int]
  unpack(buf, aa)
  assert a == aa

in case the compiler cannot decide how to serialize or deserialize your very very complex object, you can help it in easy way by defining your own handler pack_type/unpack_type

type
  #not really complex, just for example
  mycomplexobject = object
    a: someSimpleType
    b: someSimpleType

# help the compiler to decide
# ByteStream is any Stream Compatible object such as MsgStream, FileStream, StringStream
proc pack_type*[ByteStream](s: ByteStream, x: mycomplexobject) =
  s.pack(x.a) # let the compiler decide
  s.pack(x.b) # let the compiler decide

# help the compiler to decide
# ByteStream is any Stream Compatible object
proc unpack_type*[ByteStream](s: ByteStream, x: var mycomplexobject) =
  s.unpack(x.a)
  s.unpack(x.b)

var s = MsgStream.init() # besides MsgStream, you can also use Nim StringStream or FileStream
var x: mycomplexobject
s.pack(x) #pack as usual

var ss = MsgStream.init(s.data)
ss.unpack(x) #unpack as usual
Data Conversion
nim msgpack JsonNode
int8/16/32/64 int8/16/32/64 JInt
uint8/16/32/64 uint8/16/32/64 JInt
true/false true/false JBool
nil nil JNull
procedural type ignored n/a
cstring ignored n/a
pointer ignored n/a
ptr see ref-types n/a
ref see ref-types n/a
circular ref see ref-types n/a
distinct types** converted to base type applicable base type
float32/64 float32/64 JFloat
string string8/16/32 JString
array/seq array JArray
set array JArray
range/subrange int8/16/32/64 JInt
enum int8/16/32/64 JInt
IntSet,Doubly/SinglyLinkedList* array JArray
Doubly/SinglyLinkedRing* array JArray
Queue,HashSet,OrderedSet* array JArray
Table,TableRef* map JObject
OrderedTable,OrderedTableRef* map JObject
StringTableRef* map JObject
CritBitTree[T]* map JObject
CritBitTree[void]* array JArray
object/tuple array/map JObject
  • (*) please import msgpakc4collection for Nim standard library collections, they are no longer part of codec core
  • (**) provide your own implementation if you want to override default behavior
distinct types

If distinct types encountered, it will be converted back to it's base type. If you don't like this behavior, since version 0.2.9 msgpack4nim allow you to override this default behavior by supplying your own implementation of pack_type and unpack_type.

import msgpack4nim, strutils

type
  Guid = distinct string

proc pack_type*[ByteStream](s: ByteStream, v: Guid) =
  s.pack_bin(len(v.string))
  s.write(v.string)

proc unpack_type*[ByteStream](s: ByteStream, v: var Guid) =
  let L = s.unpack_bin()
  v = Guid(s.readStr(L))

var b = Guid("AA")
var s = b.pack
echo s.tohex == "C4024141"
echo s.stringify == "BIN: 4141 "

var bb: Guid
s.unpack(bb)
check bb.string == b.string
object and tuple

object and tuple by default converted to msgpack array, however you can tell the compiler to convert it to map by supplying --define:msgpack_obj_to_map

nim c --define:msgpack_obj_to_map yourfile.nim

or --define:msgpack_obj_to_stream to convert object/tuple fields value into stream of msgpack objects

nim c --define:msgpack_obj_to_stream yourfile.nim

What this means? It means by default, each object/tuple will be converted to one msgpack array contains field(s) value only without their field(s) name.

If you specify that the object/tuple will be converted to msgpack map, then each object/tuple will be converted to one msgpack map contains key-value pairs. The key will be field name, and the value will be field value.

If you specify that the object/tuple will be converted to msgpack stream, then each object/tuple will be converted into one or more msgpack's type for each object's field and then the resulted stream will be concatenated to the msgpack stream buffer.

Which one should I use?

Usually, other msgpack libraries out there convert object/tuple/record/struct or whatever structured data supported by the language into msgpack array, but always make sure to consult the documentation first. If both of the serializer and deserializer agreed to one convention, then usually there will be no problem. No matter which library/language you use, you can exchange msgpack data among them.

since version 0.2.4, you can set encoding mode at runtime to choose which encoding you would like to perform

note: the runtime encoding mode only available if you use MsgStream, otherwise only compile time flag available

mode msgpack_obj_to_map msgpack_obj_to_array msgpack_obj_to_stream default
MSGPACK_OBJ_TO_DEFAULT map array stream array
MSGPACK_OBJ_TO_ARRAY array array array array
MSGPACK_OBJ_TO_MAP map map map map
MSGPACK_OBJ_TO_STREAM stream stream stream stream
ref-types:

ref something :

  • if ref value is nil, it will be packed into msgpack nil, and when unpacked, you will get nil too
  • if ref value not nil, it will be dereferenced e.g. pack(val[]) or unpack(val[])
  • ref subject to some restriction. see restriction below
  • ptr will be treated like ref during pack
  • unpacking ptr will invoke alloc, so you must dealloc it

circular reference: altough detecting circular reference is not too difficult(using set of pointers), the current implementation does not provide circular reference detection. If you pack something contains circular reference, you know something bad will happened

Restriction: For objects their type is not serialized. This means essentially that it does not work if the object has some other runtime type than its compiletime type:

import streams, msgpack4nim

type
  TA = object of RootObj
  TB = object of TA
    f: int

var
  a: ref TA
  b: ref TB

new(b)
a = b

echo stringify(pack(a))
#produces "[ ]" or "{ }"
#not "[ 0 ]" or '{ "f" : 0 }'
limitation:

these types will be ignored:

  • procedural type
  • cstring(it is not safe to assume it always terminated by null)
  • pointer

these types cannot be automatically pack/unpacked:

  • void (will cause compile time error)

however, you can provide your own handler for cstring and pointer

Gotchas: because data conversion did not preserve original data types(only partial preservation), the following code is perfectly valid and will raise no exception

import msgpack4nim, streams, tables, sets, strtabs

type
  Horse = object
    legs: int
    foals: seq[string]
    attr: Table[string, string]

  Cat = object
    legs: uint8
    kittens: HashSet[string]
    traits: StringTableRef

proc initHorse(): Horse =
  result.legs = 4
  result.foals = @["jilly", "colt"]
  result.attr = initTable[string, string]()
  result.attr["color"] = "black"
  result.attr["speed"] = "120mph"

var stallion = initHorse()
var tom: Cat

var buf = pack(stallion) #pack a Horse here
unpack(buf, tom)
#abracadabra, it will unpack into a Cat

echo "legs: ", $tom.legs
echo "kittens: ", $tom.kittens
echo "traits: ", $tom.traits

another gotcha:

  type
    KAB = object of RootObj
      aaa: int
      bbb: int

    KCD = object of KAB
      ccc: int
      ddd: int

    KEF = object of KCD
      eee: int
      fff: int

  var kk = KEF()
  echo stringify(pack(kk))
  # will produce "{ "eee" : 0, "fff" : 0, "ccc" : 0, "ddd" : 0, "aaa" : 0, "bbb" : 0 }"
  # not "{ "aaa" : 0, "bbb" : 0, "ccc" : 0, "ddd" : 0, "eee" : 0, "fff" : 0 }"
bin and ext format

this implementation provide function to encode/decode msgpack bin/ext format header, but for the body, you must write it yourself or read it yourself to/from the MsgStream

  • proc pack_bin*[ByteStream](s: ByteStream, len: int)
  • proc pack_ext*[ByteStream](s: ByteStream, len: int, exttype: int8)
  • proc unpack_bin*[ByteStream](s: ByteStream): int
  • proc unpack_ext*[ByteStream](s: ByteStream): tuple[exttype:uint8, len: int]
import streams, msgpack4nim

const exttype0 = 0

var s = MsgStream.init()
var body = "this is the body"

s.pack_ext(body.len, exttype0)
s.write(body)

#the same goes to bin format
s.pack_bin(body.len)
s.write(body)

var ss = MsgStream.init(s.data)
#unpack_ext return tuple[exttype:uint8, len: int]
let (extype, extlen) = ss.unpack_ext()
var extbody = ss.readStr(extlen)

assert extbody == body

let binlen = ss.unpack_bin()
var binbody = ss.readStr(binlen)

assert binbody == body
stringify

you can convert msgpack data to readable string using stringify function

  type
    Horse = object
      legs: int
      speed: int
      color: string
      name: string

  var cc = Horse(legs:4, speed:150, color:"black", name:"stallion")
  var zz = pack(cc)
  echo stringify(zz)

the result will be:

default:
[ 4, 150, "black", "stallion" ]

msgpack_obj_to_map defined:
{ "legs" : 4, "speed" : 150, "color" : "black", "name" : "stallion" }

msgpack_obj_to_stream defined:
4 150 "black" "stallion"
toAny

toAny takes a string of msgpack data or a stream, then it will produce msgAny which you can interrogate of it's type and value during runtime by accessing it's member kind

toAny recognize all valid msgpack message and translate it into a group of types:

msgMap, msgArray, msgString, msgBool,
msgBin, msgExt, msgFloat32, msgFloat64,
msgInt, msgUint, msgNull

for example, msg is a msgpack data with content [1, "hello", {"a": "b"}], you can interrogate it like this:

var a = msg.toAny()
assert a.kind == msgArray
assert a.arrayVal[0].kind == msgInt
assert a.arrayVal[0].intVal == 1
assert a.arrayVal[1].kind == msgString
assert a.arrayVal[1].stringVal == "hello"
assert a.arrayVal[2].kind == msgMap
var c = a[2]
assert c[anyString("a")] == anyString("b")

since version 0.2.1, toAny was put into separate module msgpack2any, it has functionality similar with json, with support of msgpack bin and ext natively

msgpack2any also support pretty printing similar with json pretty printing.

Primary usage for msgpack2any is to provide higher level API while dynamically querying underlying msgpack data at runtime. Currently, msgpack2any decode all msgpack stream at once. There are room for improvements such as progressive decoding at runtime, or selective decoding at runtime. Both of this improvements are not implemented, yet they are important for applications that need for finer control over decoding step.

JSON

Start version 0.2.0, msgpack4nim receive additional family member, msgpack2json module. It consists of toJsonNode and fromJsonNode to interact with stdlib's json module.

Installation via nimble

nimble install msgpack4nim

Implementation specific

If an object can be represented in multiple possible output formats, serializers SHOULD use the format which represents the data in the smallest number of bytes.

According to the spec, the serializer should use smallest number of bytes, and this behavior is implemented in msgpack4nim. Therefore, some valid encoding would never produced by msgpack4nim.

For example: although 0xcdff00 and 0xceff000000 encoding is valid according to the spec which is decoded into positive integer 255, msgpack4nim never produce it, because the internal algorithm will select the smallest number of bytes needed, which is 0xccff.

However, if msgpack4nim received encoded streams from other msgpack library contains those longer than needed sequence, as long as it conforms to the spec, msgpack4nim will happily decoded it and convert it to the destination storage(variable) type.

Other msgpack library who consume msgpack4nim stream, will also decode it properly, although they might not produce smallest number of bytes required.

enjoy it, happy nim-ing

mcollina/msgpack5 https://github.com/mcollina/msgpack5

msgpack5  CI

A msgpack v5 implementation for node.js and the browser, with extension point support.

Install
npm install msgpack5 --save
Usage
var msgpack = require('msgpack5')() // namespace our extensions
  , a       = new MyType(2, 'a')
  , encode  = msgpack.encode
  , decode  = msgpack.decode

msgpack.register(0x42, MyType, mytipeEncode, mytipeDecode)

console.log(encode({ 'hello': 'world' }).toString('hex'))
// 81a568656c6c6fa5776f726c64
console.log(decode(encode({ 'hello': 'world' })))
// { hello: 'world' }
console.log(encode(a).toString('hex'))
// d5426161
console.log(decode(encode(a)) instanceof MyType)
// true
console.log(decode(encode(a)))
// { value: 'a', size: 2 }

function MyType(size, value) {
  this.value = value
  this.size  = size
}

function mytipeEncode(obj) {
  var buf = new Buffer(obj.size)
  buf.fill(obj.value)
  return buf
}

function mytipeDecode(data) {
  var result = new MyType(data.length, data.toString('utf8', 0, 1))
    , i

  for (i = 0; i < data.length; i++) {
    if (data.readUInt8(0) != data.readUInt8(i)) {
      throw new Error('should all be the same')
    }
  }

  return result
}
In the Browser

This library is compatible with Browserify.

If you want to use standalone, grab the file in the dist folder of this repo, and use in your own HTML page, the module will expose a msgpack5 global.

<script type="text/javascript"
        src="./msgpack5.min.js">
</script>
To build
	npm run build
API

API

msgpack(options(obj))

Creates a new instance on which you can register new types for being encoded.

options:

  • forceFloat64, a boolean to that forces all floats to be encoded as 64-bits floats. Defaults to false.
  • sortKeys, a boolean to force a determinate keys order
  • compatibilityMode, a boolean that enables "compatibility mode" which doesn't use str 8 format. Defaults to false.
  • disableTimestampEncoding, a boolean that when set disables the encoding of Dates into the timestamp extension type. Defaults to false.
  • preferMap, a boolean that forces all maps to be decoded to Maps rather than plain objects. This ensures that decode(encode(new Map())) instanceof Map and that iteration order is preserved. Defaults to false.
  • protoAction, a string which can be error|ignore|remove that determines what happens when decoding a plain object with a __proto__ property which would cause prototype poisoning. error (default) throws an error, remove removes the property, ignore (not recommended) allows the property, thereby causing prototype poisoning on the decoded object.

encode(object)

Encodes object in msgpack, returns a bl.


decode(buf)

Decodes buf from in msgpack. buf can be a Buffer or a bl instance.

In order to support a stream interface, a user must pass in a bl instance.