uyha/mzg
A MessagePack library for Zig
mzg is a MessagePack library for Zig with no allocations, and the API favors
the streaming usage.
Run the following command to add this project as a dependency
zig fetch --save git+https://github.com/uyha/mzg.git#v0.2.1
In your build.zig, add the following
const mzg = b.dependency("mzg", .{
.target = target,
.optimize = optimize,
});
// Replace `exe` with your actual library or executable
exe.root_module.addImport("mzg", mzg.module("mzg"));
pack is for packing Zig values using a writerpackAdapted is like pack but it accepts a map that defines how certain
types should be packed.packWithOptions is like pack but it accepts a mzg.PackOptions parameter
to control the packing behavior.packAdaptedWithOptions is like packWithOptions but it accepts a map that
defines how certain types should be packed.unpack is for unpacking a MessagePack message into a Zig object and it does
not allocate memory.unpackAdapted is like unpack but it accepts a map that defines how
certain types should be unpacked.unpackAllocate is like unpack but it accepts an Allocator that allows
the unpacking process to allocate memory.unpackAdaptedAllocate is like unpackAllocate but it accepts a map that
defines how a particular type should be packed.adapter.packArray and adapter.unpackArray pack and unpack structs like
std.ArrayListUnmanagedadapter.packMap and adapter.unpackMap pack and unpack structs like
std.ArrayHashMapUnmanaged.adapter.packStream and adapter.unpackStream pack and unpack structs in a
stream like manner (length is not specified in the beginning).There are a set of pack* and unpack* functions that translate between a
precise set of Zig types and MessagePack. These functions can be used when
implementing custom packing and unpacking.
All examples live in examples directory.
Converting a byte slice to MessagePack and back
pub fn main() !void {
const allocator = std.heap.page_allocator;
var allocWriter: Writer.Allocating = .init(allocator);
defer allocWriter.deinit();
const writer: *Writer = &allocWriter.writer;
try mzg.pack(
"a string with some characters for demonstration purpose",
writer,
);
var string: []const u8 = undefined;
const size = try mzg.unpack(writer.buffer[0..writer.end], &string);
std.debug.print("Consumed {} bytes\n", .{size});
std.debug.print("string: {s}\n", .{string});
}
const std = @import("std");
const Writer = std.Io.Writer;
const mzg = @import("mzg");
Certain types can be packed and unpacked by default (refer to Mapping for more details)
pub fn main() !void {
const allocator = std.heap.page_allocator;
var allocWriter: Writer.Allocating = .init(allocator);
defer allocWriter.deinit();
const writer: *Writer = &allocWriter.writer;
try mzg.pack(
Targets{ .position = .init(2000), .velocity = .init(10) },
writer,
);
var targets: Targets = undefined;
const size = try mzg.unpack(writer.buffer[0..writer.end], &targets);
std.debug.print("Consumed {} bytes\n", .{size});
std.debug.print("Targets: {}\n", .{targets});
}
const Position = enum(i32) {
_,
pub fn init(raw: std.meta.Tag(@This())) @This() {
return @enumFromInt(raw);
}
};
const Velocity = enum(i32) {
_,
pub fn init(raw: std.meta.Tag(@This())) @This() {
return @enumFromInt(raw);
}
};
const Targets = struct {
position: Position,
velocity: Velocity,
};
const std = @import("std");
const Writer = std.Io.Writer;
const mzg = @import("mzg");
const adapter = mzg.adapter;
Many container types in the std library cannot be packed or unpacked by
default, but the adapter functions make it easy to work with these types.
pub fn main() !void {
const allocator = std.heap.page_allocator;
var allocWriter: Writer.Allocating = .init(allocator);
defer allocWriter.deinit();
const writer: *Writer = &allocWriter.writer;
var in: std.ArrayList(u32) = .empty;
defer in.deinit(allocator);
try in.append(allocator, 42);
try in.append(allocator, 75);
try mzg.pack(adapter.packArray(&in), writer);
var out: std.ArrayList(u32) = .empty;
defer out.deinit(allocator);
const size = try mzg.unpackAllocate(
allocator,
writer.buffer[0..writer.end],
adapter.unpackArray(&out),
);
std.debug.print("Consumed {} bytes\n", .{size});
std.debug.print("out: {any}\n", .{out.items});
}
const std = @import("std");
const Writer = std.Io.Writer;
const mzg = @import("mzg");
const adapter = mzg.adapter;
| Zig Type | MessagePack |
|---|---|
void, null |
nil |
bool |
bool |
| integers (<=64 bits) | int |
| floats (<=64 bits) | float |
?T |
nil if value is null, pack according to T otherwise |
| enums | int |
| enum literals | str |
| tagged unions | array of 2 elements: int and the value of the union |
| packed structs | int |
| structs and tuples | array of fields in the order they are declared |
[N], [], [:X], and @Vec of u8 |
str |
[N], [], [:X], and @Vec of T |
array of T |
Ext |
ext |
Timestamp |
Timestamp |
*T |
T |
| MessagePack | Compatible Zig Type |
|---|---|
nil |
void, ?T |
bool |
bool |
int |
integers, enums |
float |
floats |
str |
[]const u8 |
bin |
[]const u8 |
array |
The length can be read into integers |
map |
The length can be read into integers |
ext |
Ext |
Timestamp |
Timestamp |
When an enum/union/struct has an mzgPack function that returns
mzg.PackError!void can be called with
// Value cares about the options passed by the caller
value.mzgPack(options, map, writer);
With the arguments being
value is the enum/union/struct being packed.options is mzg.PackOptions.map is a tuple of 2 element tuples whose 1st element being a type and 2nd
element being a packing adapter function.writer is std.Io.Writer.The function will be called when the mzg.pack function is used.
When an enum/union/struct has
mzgUnpack function that returns UnpackError!usize and can be called with
out.mzgUnpack(map, buffer);
mzgUnpackAllocate function that returns UnpackAllocateError!usize and can
be called with
out.mzgUnpackAllocate(allocator, map, buffer);
With the arguments being
out is the enum/union/struct being unpacked.allocator is an std.mem.Allocator.map is a tuple of 2 element tuples whose 1st element being a type and 2nd
element being an unpacking adapter function.buffer is []const u8.The mzgUnpack function is called when either mzg.unpack or
mzg.unpackAdapted is used.
The mzgUnpackAllocate function is called when either mzg.unpackAllocate or
mzg.unpackAdaptedAllocate is used.