more deserialiser work

This commit is contained in:
Moritz Gmeiner 2025-04-02 18:49:28 +02:00
commit b2d93a466e
2 changed files with 127 additions and 13 deletions

View file

@ -1,12 +1,16 @@
const std = @import("std"); const std = @import("std");
const Object = @import("root.zig").Object; const Object = @import("root.zig").Object;
const Tag = @import("root.zig").Tag;
const MapEntry = @import("root.zig").MapEntry;
const SerialiseError = error{ const SerialiseError = error{
OutOfMemory, OutOfMemory,
StringTooLong, StringTooLong,
BinaryTooLong, BinaryTooLong,
ArrayTooLong,
MapTooLong,
ExtTooLong,
}; };
fn intInRange(comptime T: type, i: i64) bool { fn intInRange(comptime T: type, i: i64) bool {
@ -98,15 +102,82 @@ fn serialise_raw(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), s:
} }
fn serialise_array(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), array: []const Object) SerialiseError!void { fn serialise_array(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), array: []const Object) SerialiseError!void {
_ = alloc; if (array.len <= 15) {
_ = buf; try buf.append(alloc, 0b1001_0000 | @as(u8, @intCast(array.len)));
_ = array; } else if (array.len <= std.math.maxInt(u16)) {
try buf.append(alloc, 0xdc);
std.mem.writeInt(u16, try buf.addManyAsArray(alloc, @sizeOf(u16)), @intCast(array.len), .big);
} else if (array.len <= std.math.maxInt(u32)) {
try buf.append(alloc, 0xdd);
std.mem.writeInt(u32, try buf.addManyAsArray(alloc, @sizeOf(u32)), @intCast(array.len), .big);
} else {
return error.ArrayTooLong;
}
for (array) |obj| {
try serialise_into_buf(alloc, buf, obj);
}
} }
pub fn serialise(alloc: std.mem.Allocator, obj: Object) SerialiseError![]u8 { fn serialise_map(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), map: []const MapEntry) SerialiseError!void {
var buf = std.ArrayListUnmanaged(u8){}; if (map.len <= 15) {
defer buf.deinit(alloc); try buf.append(alloc, 0b1000_0000 | @as(u8, @intCast(map.len)));
} else if (map.len <= std.math.maxInt(u16)) {
try buf.append(alloc, 0xde);
std.mem.writeInt(u16, try buf.addManyAsArray(alloc, @sizeOf(u16)), @intCast(map.len), .big);
} else if (map.len <= std.math.maxInt(u32)) {
try buf.append(alloc, 0xdf);
std.mem.writeInt(u32, try buf.addManyAsArray(alloc, @sizeOf(u32)), @intCast(map.len), .big);
} else {
return error.MapTooLong;
}
for (map) |entry| {
try serialise_into_buf(alloc, buf, entry.key);
try serialise_into_buf(alloc, buf, entry.value);
}
}
fn serialise_ext(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), type_: u8, bytes: []u8) SerialiseError!void {
// fixext
for (0.., [_]usize{ 1, 2, 4, 8, 16 }) |n, len| {
if (bytes.len == len) {
try buf.ensureUnusedCapacity(alloc, 1 + 1 + len);
try buf.append(alloc, 0xd4 + @as(u8, @intCast(n)));
try buf.append(alloc, type_);
try buf.appendSlice(alloc, bytes);
return;
}
}
// ext
inline for (0.., [_]type{ u8, u16, u32 }) |n, T| {
if (bytes.len <= std.math.maxInt(T)) {
try buf.ensureUnusedCapacity(alloc, 1 + @sizeOf(T) + bytes.len);
try buf.append(alloc, 0xc7 + n);
std.mem.writeInt(T, try buf.addManyAsArray(alloc, @sizeOf(T)), @intCast(bytes.len), .big);
try buf.append(alloc, type_);
try buf.appendSlice(alloc, bytes);
return;
}
}
return error.ExtTooLong;
}
fn serialise_into_buf(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), obj: Object) SerialiseError!void {
switch (obj) { switch (obj) {
.nil => try buf.append(alloc, 0xc0), .nil => try buf.append(alloc, 0xc0),
.bool => |b| if (b) { .bool => |b| if (b) {
@ -114,8 +185,8 @@ pub fn serialise(alloc: std.mem.Allocator, obj: Object) SerialiseError![]u8 {
} else { } else {
try buf.append(alloc, 0xc2); try buf.append(alloc, 0xc2);
}, },
.integer => |i| try serialise_int(alloc, &buf, i), .integer => |i| try serialise_int(alloc, buf, i),
.uinteger => |u| try serialise_uint(alloc, &buf, u), .uinteger => |u| try serialise_uint(alloc, buf, u),
.float32 => |f| { .float32 => |f| {
try buf.ensureUnusedCapacity(alloc, 1 + 4); try buf.ensureUnusedCapacity(alloc, 1 + 4);
@ -130,11 +201,19 @@ pub fn serialise(alloc: std.mem.Allocator, obj: Object) SerialiseError![]u8 {
std.mem.writeInt(u64, try buf.addManyAsArray(alloc, 8), @bitCast(f), .big); std.mem.writeInt(u64, try buf.addManyAsArray(alloc, 8), @bitCast(f), .big);
}, },
.string => |s| try serialise_raw(alloc, &buf, s, .string), .string => |s| try serialise_raw(alloc, buf, s, .string),
.binary => |b| try serialise_raw(alloc, &buf, b, .binary), .binary => |b| try serialise_raw(alloc, buf, b, .binary),
.array => |array| try serialise_array(alloc, &buf, array), .array => |array| try serialise_array(alloc, buf, array),
else => unreachable, .map => |map| try serialise_map(alloc, buf, map),
.extension => |ext| try serialise_ext(alloc, buf, ext.type, ext.bytes),
} }
}
pub fn serialise(alloc: std.mem.Allocator, obj: Object) SerialiseError![]u8 {
var buf = std.ArrayListUnmanaged(u8){};
defer buf.deinit(alloc);
try serialise_into_buf(alloc, &buf, obj);
return try buf.toOwnedSlice(alloc); return try buf.toOwnedSlice(alloc);
} }

View file

@ -81,3 +81,38 @@ test "f64" {
try std.testing.expectEqualSlices(u8, &[_]u8{ 0xcb, 0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, bytes); try std.testing.expectEqualSlices(u8, &[_]u8{ 0xcb, 0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, bytes);
} }
} }
test "array" {
const alloc = std.testing.allocator;
{
// const obj = try deserialise(alloc, &[_]u8{ 0x92, 0xa2, 0x6c, 0x65, 0xa4, 0x73, 0x68, 0x69, 0x74 });
// defer obj.deinit(alloc);
// try std.testing.expectEqual(2, obj.array.len);
// try std.testing.expectEqualStrings("le", obj.array[0].string);
// try std.testing.expectEqualStrings("shit", obj.array[1].string);
const bytes = try serialise(alloc, .{ .array = &[_]Object{ .{ .string = "le" }, .{ .string = "shit" } } });
defer alloc.free(bytes);
try std.testing.expectEqualSlices(u8, &[_]u8{ 0x92, 0xa2, 0x6c, 0x65, 0xa4, 0x73, 0x68, 0x69, 0x74 }, bytes);
}
}
test "map" {
const alloc = std.testing.allocator;
const MapEntry = msgpack.MapEntry;
{
const bytes = try serialise(alloc, .{ .map = &[_]MapEntry{
.{ .key = .{ .uinteger = 0 }, .value = .{ .string = "le" } },
.{ .key = .{ .uinteger = 1 }, .value = .{ .string = "shit" } },
} });
defer alloc.free(bytes);
try std.testing.expectEqualSlices(u8, &[_]u8{ 0x82, 0x00, 0xa2, 0x6c, 0x65, 0x01, 0xa4, 0x73, 0x68, 0x69, 0x74 }, bytes);
}
}