diff --git a/src/root.zig b/src/root.zig index c154e04..bb94b4e 100644 --- a/src/root.zig +++ b/src/root.zig @@ -34,8 +34,8 @@ pub const Object = union(enum) { float64: f64, string: []const u8, binary: []const u8, - array: []Object, - map: []MapEntry, + array: []const Object, + map: []const MapEntry, extension: struct { type: u8, bytes: []u8 }, pub fn deinit(self: Object, alloc: std.mem.Allocator) void { diff --git a/src/serialise.zig b/src/serialise.zig index 092eb8f..d990b1a 100644 --- a/src/serialise.zig +++ b/src/serialise.zig @@ -13,165 +13,82 @@ fn intInRange(comptime T: type, i: i64) bool { return std.math.minInt(T) <= i and i <= std.math.maxInt(T); } -fn serialise_int(alloc: std.mem.Allocator, i: i64) SerialiseError![]u8 { - if (i < 0 and i >= std.math.minInt(i6)) { - // neg fixint - const bytes = try alloc.alloc(u8, 1); +fn serialise_int(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), i: i64) SerialiseError!void { + if (i < 0 and i >= std.math.minInt(i6)) { // neg fixint + std.mem.writeInt(i8, try buf.addManyAsArray(alloc, 1), @intCast(i), .big); - std.mem.writeInt(i8, bytes[0..1], @intCast(i), .big); - - return bytes; + return; } - if (intInRange(i8, i)) { // int8 - const bytes = try alloc.alloc(u8, 1 + 1); + inline for (0.., &[_]type{ i8, i16, i32, i64 }) |n, T| { + if (intInRange(T, i)) { + try buf.ensureUnusedCapacity(alloc, 1 + @sizeOf(T)); - bytes[0] = 0xd0; + try buf.append(alloc, 0xd0 + n); - std.mem.writeInt(i8, bytes[1..2], @intCast(i), .big); + std.mem.writeInt(T, try buf.addManyAsArray(alloc, @sizeOf(T)), @intCast(i), .big); - return bytes; + return; + } } - if (intInRange(i16, i)) { // int16 - const bytes = try alloc.alloc(u8, 1 + 2); - - bytes[0] = 0xd1; - - std.mem.writeInt(i16, bytes[1..3], @intCast(i), .big); - - return bytes; - } - - if (intInRange(i32, i)) { // int32 - const bytes = try alloc.alloc(u8, 1 + 4); - - bytes[0] = 0xd2; - - std.mem.writeInt(i32, bytes[1..5], @intCast(i), .big); - - return bytes; - } - - // int64 - const bytes = try alloc.alloc(u8, 1 + 8); - - bytes[0] = 0xd3; - - std.mem.writeInt(i64, bytes[1..9], i, .big); - - return bytes; + unreachable; } fn uintInRange(comptime T: type, u: u64) bool { return u <= std.math.maxInt(T); } -fn serialise_uint(alloc: std.mem.Allocator, u: u64) SerialiseError![]u8 { +fn serialise_uint(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), u: u64) SerialiseError!void { if (uintInRange(u7, u)) { // pos fixint - const bytes = try alloc.alloc(u8, 1); + std.mem.writeInt(u8, try buf.addManyAsArray(alloc, 1), @intCast(u), .big); - std.mem.writeInt(u8, bytes[0..1], @intCast(u), .big); - - return bytes; + return; } - if (uintInRange(u8, u)) { // uint8 - const bytes = try alloc.alloc(u8, 1 + 1); + inline for (0.., &[_]type{ u8, u16, u32, u64 }) |n, T| { + if (uintInRange(T, u)) { + try buf.ensureUnusedCapacity(alloc, 1 + @sizeOf(T)); - bytes[0] = 0xcc; + try buf.append(alloc, 0xcc + n); - std.mem.writeInt(u8, bytes[1..2], @intCast(u), .big); + std.mem.writeInt(T, try buf.addManyAsArray(alloc, @sizeOf(T)), @intCast(u), .big); - return bytes; + return; + } } - if (uintInRange(u16, u)) { // uint16 - const bytes = try alloc.alloc(u8, 1 + 2); - - bytes[0] = 0xcd; - - std.mem.writeInt(u16, bytes[1..3], @intCast(u), .big); - - return bytes; - } - - if (uintInRange(u32, u)) { // uint32 - const bytes = try alloc.alloc(u8, 1 + 4); - - bytes[0] = 0xce; - - std.mem.writeInt(u32, bytes[1..5], @intCast(u), .big); - - return bytes; - } - - // uint64 - const bytes = try alloc.alloc(u8, 1 + 8); - - bytes[0] = 0xcf; - - std.mem.writeInt(u64, bytes[1..9], u, .big); - - return bytes; + unreachable; } -fn serialise_raw(alloc: std.mem.Allocator, s: []const u8, comptime kind: enum { string, binary }) SerialiseError![]u8 { - // TODO: should we validate that s in UTF-8 here? +fn serialise_raw(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), s: []const u8, comptime kind: enum { string, binary }) SerialiseError!void { + // TODO: should we validate that `s` is UTF-8 here? if (kind == .string and s.len <= 31) { // fixstr - const bytes = try alloc.alloc(u8, 1 + s.len); + try buf.ensureUnusedCapacity(alloc, 1 + s.len); - bytes[0] = 0b101_00000 | @as(u8, @intCast(s.len)); + try buf.append(alloc, 0b101_00000 | @as(u8, @intCast(s.len))); - @memcpy(bytes[1..], s); + try buf.appendSlice(alloc, s); - return bytes; + return; } - if (s.len <= std.math.maxInt(u8)) { // str8 / bin8 - const bytes = try alloc.alloc(u8, 1 + 1 + s.len); + inline for (0.., &[_]type{ u8, u16, u32 }) |i, T| { + if (s.len <= std.math.maxInt(T)) { + try buf.ensureUnusedCapacity(alloc, 1 + @sizeOf(T) + s.len); - bytes[0] = switch (kind) { - .string => 0xd9, - .binary => 0xc4, - }; + switch (kind) { + .string => try buf.append(alloc, 0xd9 + i), + .binary => try buf.append(alloc, 0xc4 + i), + } - bytes[1] = @as(u8, @intCast(s.len)); + std.mem.writeInt(T, try buf.addManyAsArray(alloc, @sizeOf(T)), @intCast(s.len), .big); - @memcpy(bytes[2..], s); + try buf.appendSlice(alloc, s); - return bytes; - } - - if (s.len <= std.math.maxInt(u16)) { // str16 / bin16 - const bytes = try alloc.alloc(u8, 1 + 2 + s.len); - - bytes[0] = switch (kind) { - .string => 0xda, - .binary => 0xc5, - }; - - std.mem.writeInt(u16, bytes[1..3], @intCast(s.len), .big); - - @memcpy(bytes[3..], s); - - return bytes; - } - - if (s.len <= std.math.maxInt(u32)) { // str16 / bin16 - const bytes = try alloc.alloc(u8, 1 + 4 + s.len); - - bytes[0] = switch (kind) { - .string => 0xdb, - .binary => 0xc6, - }; - - std.mem.writeInt(u32, bytes[1..5], @intCast(s.len), .big); - - @memcpy(bytes[5..], s); - - return bytes; + return; + } } return switch (kind) { @@ -180,40 +97,44 @@ fn serialise_raw(alloc: std.mem.Allocator, s: []const u8, comptime kind: enum { }; } +fn serialise_array(alloc: std.mem.Allocator, buf: *std.ArrayListUnmanaged(u8), array: []const Object) SerialiseError!void { + _ = alloc; + _ = buf; + _ = array; +} + pub fn serialise(alloc: std.mem.Allocator, obj: Object) SerialiseError![]u8 { + var buf = std.ArrayListUnmanaged(u8){}; + defer buf.deinit(alloc); + switch (obj) { - .nil => return try alloc.dupe(u8, &[_]u8{0xc0}), + .nil => try buf.append(alloc, 0xc0), .bool => |b| if (b) { - return try alloc.dupe(u8, &[_]u8{0xc3}); + try buf.append(alloc, 0xc3); } else { - return try alloc.dupe(u8, &[_]u8{0xc2}); + try buf.append(alloc, 0xc2); }, - .integer => |i| return try serialise_int(alloc, i), - .uinteger => |u| return try serialise_uint(alloc, u), + .integer => |i| try serialise_int(alloc, &buf, i), + .uinteger => |u| try serialise_uint(alloc, &buf, u), .float32 => |f| { - const bytes = try alloc.alloc(u8, 1 + 4); + try buf.ensureUnusedCapacity(alloc, 1 + 4); - bytes[0] = 0xca; + try buf.append(alloc, 0xca); - std.mem.writeInt(u32, bytes[1..5], @bitCast(f), .big); - - return bytes; + std.mem.writeInt(u32, try buf.addManyAsArray(alloc, 4), @bitCast(f), .big); }, .float64 => |f| { - const bytes = try alloc.alloc(u8, 1 + 8); + try buf.ensureUnusedCapacity(alloc, 1 + 8); - bytes[0] = 0xcb; + try buf.append(alloc, 0xcb); - std.mem.writeInt(u64, bytes[1..9], @bitCast(f), .big); - - return bytes; + std.mem.writeInt(u64, try buf.addManyAsArray(alloc, 8), @bitCast(f), .big); }, - .string => |s| return try serialise_raw(alloc, s, .string), - .binary => |b| return try serialise_raw(alloc, b, .binary), + .string => |s| try serialise_raw(alloc, &buf, s, .string), + .binary => |b| try serialise_raw(alloc, &buf, b, .binary), + .array => |array| try serialise_array(alloc, &buf, array), else => unreachable, } - // const bytes = try alloc.alloc(u8, 0); - - // return bytes; + return try buf.toOwnedSlice(alloc); }