diff --git a/src/coder.zig b/src/coder.zig index ffdc431..700eb62 100644 --- a/src/coder.zig +++ b/src/coder.zig @@ -1,12 +1,6 @@ const std = @import("std"); const testing = std.testing; -pub const ParseError = error{ - Overflow, - EndOfStream, - OutOfMemory, -}; - pub const Uint64Coder = struct { pub const primitive = u64; @@ -15,49 +9,41 @@ pub const Uint64Coder = struct { return std.math.max(divCeil(u64, bits, 7), 1); } - pub fn encode(buffer: []u8, data: u64) []u8 { - if (data == 0) { - buffer[0] = 0; - return buffer[0..1]; - } - var i = usize(0); + pub fn encodeInto(out_stream: var, data: u64) !void { var value = data; - while (value > 0) : (i += 1) { - buffer[i] = u8(0x80) + @truncate(u7, value); - value >>= 7; + while (value >= 0x80) : (value >>= 7) { + try out_stream.writeByte(u8(0x80) + @truncate(u7, value)); } - buffer[i - 1] &= 0x7F; - return buffer[0..i]; + try out_stream.writeByte(@truncate(u7, value)); } - pub fn decode(bytes: []const u8, len: *usize) ParseError!u64 { + pub fn decodeFrom(in_stream: var) !u64 { var value = u64(0); - for (bytes) |byte, i| { - if (i >= 10) { - return error.Overflow; - } + var i = usize(0); + while (i < 10) : (i += 1) { + const byte = try in_stream.readByte(); value += @intCast(u64, 0x7F & byte) << (7 * @intCast(u6, i)); if (byte & 0x80 == 0) { - len.* = i + 1; return value; } } - // TODO: stream in bytes - return error.EndOfStream; + + return error.Overflow; } }; test "Uint64Coder" { - var len: usize = 0; - - var uint = try Uint64Coder.decode([_]u8{1}, &len); + var mem_stream = std.io.SliceInStream.init([_]u8{1}); + var uint = try Uint64Coder.decodeFrom(&mem_stream.stream); testing.expectEqual(u64(1), uint); - uint = try Uint64Coder.decode([_]u8{ 0b10101100, 0b00000010 }, &len); + mem_stream = std.io.SliceInStream.init([_]u8{ 0b10101100, 0b00000010 }); + uint = try Uint64Coder.decodeFrom(&mem_stream.stream); testing.expectEqual(u64(300), uint); - uint = try Uint64Coder.decode([_]u8{ 0b10010110, 0b00000001 }, &len); + mem_stream = std.io.SliceInStream.init([_]u8{ 0b10010110, 0b00000001 }); + uint = try Uint64Coder.decodeFrom(&mem_stream.stream); testing.expectEqual(u64(150), uint); } @@ -68,24 +54,24 @@ pub const Int64Coder = struct { return Uint64Coder.encodeSize(@bitCast(u64, data)); } - pub fn encode(buffer: []u8, data: i64) []u8 { - return Uint64Coder.encode(buffer, @bitCast(u64, data)); + pub fn encodeInto(out_stream: var, data: i64) !void { + try Uint64Coder.encodeInto(out_stream, @bitCast(u64, data)); } - pub fn decode(bytes: []const u8, len: *usize) ParseError!i64 { - return @bitCast(i64, try Uint64Coder.decode(bytes, len)); + pub fn decodeFrom(in_stream: var) !i64 { + return @bitCast(i64, try Uint64Coder.decodeFrom(in_stream)); } }; test "Int64Coder" { var buf1: [1000]u8 = undefined; var buf2: [1000]u8 = undefined; + var out1 = std.io.SliceOutStream.init(buf1[0..]); + var out2 = std.io.SliceOutStream.init(buf2[0..]); - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], std.math.maxInt(u64)), - Int64Coder.encode(buf2[0..], -1), - ); + try Uint64Coder.encodeInto(&out1.stream, std.math.maxInt(u64)); + try Int64Coder.encodeInto(&out2.stream, -1); + testing.expectEqualSlices(u8, out1.getWritten(), out2.getWritten()); } pub const Sint64Coder = struct { @@ -95,12 +81,12 @@ pub const Sint64Coder = struct { return Uint64Coder.encodeSize(@bitCast(u64, (data << 1) ^ (data >> 63))); } - pub fn encode(buffer: []u8, data: i64) []u8 { - return Uint64Coder.encode(buffer, @bitCast(u64, (data << 1) ^ (data >> 63))); + pub fn encodeInto(out_stream: var, data: i64) !void { + try Uint64Coder.encodeInto(out_stream, @bitCast(u64, (data << 1) ^ (data >> 63))); } - pub fn decode(bytes: []const u8, len: *usize) ParseError!i64 { - const source = try Uint64Coder.decode(bytes, len); + pub fn decodeFrom(in_stream: var) !i64 { + const source = try Uint64Coder.decodeFrom(in_stream); const raw = @bitCast(i64, source >> 1); return if (@mod(source, 2) == 0) raw else -(raw + 1); } @@ -109,24 +95,24 @@ pub const Sint64Coder = struct { test "Sint64Coder" { var buf1: [1000]u8 = undefined; var buf2: [1000]u8 = undefined; - - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], 1), - Sint64Coder.encode(buf2[0..], -1), - ); - - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], 4294967294), - Sint64Coder.encode(buf2[0..], 2147483647), - ); - - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], 4294967295), - Sint64Coder.encode(buf2[0..], -2147483648), - ); + var out1 = std.io.SliceOutStream.init(buf1[0..]); + var out2 = std.io.SliceOutStream.init(buf2[0..]); + + try Uint64Coder.encodeInto(&out1.stream, 1); + try Sint64Coder.encodeInto(&out2.stream, -1); + testing.expectEqualSlices(u8, out1.getWritten(), out2.getWritten()); + + out1.reset(); + out2.reset(); + try Uint64Coder.encodeInto(&out1.stream, 4294967294); + try Sint64Coder.encodeInto(&out2.stream, 2147483647); + testing.expectEqualSlices(u8, out1.getWritten(), out2.getWritten()); + + out1.reset(); + out2.reset(); + try Uint64Coder.encodeInto(&out1.stream, 4294967295); + try Sint64Coder.encodeInto(&out2.stream, -2147483648); + testing.expectEqualSlices(u8, out1.getWritten(), out2.getWritten()); } pub const Fixed64Coder = struct { @@ -136,15 +122,14 @@ pub const Fixed64Coder = struct { return 8; } - pub fn encode(buffer: []u8, data: u64) []u8 { - var result = buffer[0..encodeSize(data)]; - std.mem.writeIntSliceLittle(u64, result, data); - return result; + pub fn encodeInto(out_stream: var, data: u64) !void { + var buffer = [_]u8{0} ** 8; + std.mem.writeIntSliceLittle(u64, buffer[0..], data); + try out_stream.write(buffer[0..]); } - pub fn decode(bytes: []const u8, len: *usize) ParseError!u64 { - len.* = 8; - return std.mem.readIntSliceLittle(u64, bytes); + pub fn decodeFrom(in_stream: var) !u64 { + return in_stream.readIntLittle(u64); } }; @@ -155,15 +140,14 @@ pub const Fixed32Coder = struct { return 4; } - pub fn encode(buffer: []u8, data: u32) []u8 { - var result = buffer[0..encodeSize(data)]; - std.mem.writeIntSliceLittle(u32, result, data); - return result; + pub fn encodeInto(out_stream: var, data: u32) !void { + var buffer = [_]u8{0} ** 4; + std.mem.writeIntSliceLittle(u32, buffer[0..], data); + try out_stream.write(buffer[0..]); } - pub fn decode(bytes: []const u8, len: *usize) ParseError!u32 { - len.* = 4; - return std.mem.readIntSliceLittle(u32, bytes); + pub fn decodeFrom(in_stream: var) !u32 { + return try in_stream.readIntLittle(u32); } }; @@ -173,34 +157,31 @@ pub const BytesCoder = struct { return header_size + data.len; } - pub fn encode(buffer: []u8, data: []const u8) []u8 { - const header = Uint64Coder.encode(buffer, data.len); - // TODO: use a generator instead of buffer overflow - std.mem.copy(u8, buffer[header.len..], data); - return buffer[0 .. header.len + data.len]; + pub fn encodeInto(out_stream: var, data: []const u8) !void { + try Uint64Coder.encodeInto(out_stream, data.len); + try out_stream.write(data); } - pub fn decode(buffer: []const u8, len: *usize, allocator: *std.mem.Allocator) ![]u8 { - var header_len: usize = undefined; - const header = try Uint64Coder.decode(buffer, &header_len); - - var data = try allocator.alloc(u8, header); - errdefer allocator.free(data); + pub fn decodeFrom(in_stream: var, allocator: *std.mem.Allocator) ![]u8 { + const size = try Uint64Coder.decodeFrom(in_stream); - std.mem.copy(u8, data, buffer[header_len .. header_len + data.len]); - len.* = header_len + data.len; + var result = try allocator.alloc(u8, size); + errdefer allocator.free(result); - return data; + try in_stream.readNoEof(result); + return result; } }; test "BytesCoder" { - var buffer: [1000]u8 = undefined; + var buf: [1000]u8 = undefined; + var out = std.io.SliceOutStream.init(buf[0..]); + try BytesCoder.encodeInto(&out.stream, "testing"); testing.expectEqualSlices( u8, [_]u8{ 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67 }, - BytesCoder.encode(buffer[0..], "testing"), + out.getWritten(), ); } diff --git a/src/main.zig b/src/main.zig index 958f198..c684884 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,91 +22,37 @@ pub const String = types.String; pub const Repeated = types.Repeated; -pub fn StreamingMarshal(comptime T: type) type { - return struct { - const Self = @This(); - - // TODO: this is so terrible. - // Temporarily sticking this here because I can't make spin a method due to circular references - var out: ?[]const u8 = [_]u8{}; - - item: T, - frame: @Frame(spin), - - pub fn init(item: T) Self { - return Self{ - .item = item, - .frame = async spin(item), - }; - } - - fn spin(item: T) void { - var buffer: [1000]u8 = undefined; - var bufslice = buffer[0..]; - - inline for (@typeInfo(T).Struct.fields) |field, i| { - switch (@typeInfo(field.field_type)) { - .Struct => { - if (@hasDecl(field.field_type, "field_meta")) { - suspend; - Self.out = field.field_type.field_meta.encodeInto(bufslice); - - suspend; - Self.out = @field(item, field.name).encodeInto(bufslice); - } else { - std.debug.warn("{} - unknown struct\n", field.name); - } - }, - else => { - std.debug.warn("{} - not a struct\n", field.name); - }, +pub fn encodeInto(comptime T: type, item: T, out_stream: var) !void { + inline for (@typeInfo(T).Struct.fields) |field, i| { + switch (@typeInfo(field.field_type)) { + .Struct => { + if (@hasDecl(field.field_type, "field_meta")) { + try field.field_type.field_meta.encodeInto(out_stream); + try @field(item, field.name).encodeInto(out_stream); + } else { + std.debug.warn("{} - unknown struct\n", field.name); } - } - suspend; - Self.out = null; - } - - pub fn next(self: *Self) ?[]const u8 { - if (out != null) { - resume self.frame; - return out; - } - return null; + }, + else => { + std.debug.warn("{} - not a struct\n", field.name); + }, } - }; -} - -pub fn marshal(comptime T: type, allocator: *std.mem.Allocator, item: T) ![]u8 { - var buffer = std.ArrayList(u8).init(allocator); - errdefer buffer.deinit(); - - var stream = StreamingMarshal(T).init(item); - - while (stream.next()) |data| { - try buffer.appendSlice(data); } - - return buffer.toOwnedSlice(); } -pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []u8) !T { +pub fn decodeFrom(comptime T: type, allocator: *std.mem.Allocator, in_stream: var) !T { var result = init(T); - errdefer deinit(T, result); - - var cursor = usize(0); - while (cursor < bytes.len) { - var len: usize = undefined; - const info = try types.FieldMeta.decode(bytes[cursor..], &len); - cursor += len; + errdefer deinit(T, &result); + while (types.FieldMeta.decode(in_stream)) |meta| { inline for (@typeInfo(T).Struct.fields) |field, i| { switch (@typeInfo(field.field_type)) { .Struct => { - if (info.number == field.field_type.field_meta.number) { + if (meta.number == field.field_type.field_meta.number) { if (@hasDecl(field.field_type, "decodeFromAlloc")) { - cursor += try @field(result, field.name).decodeFromAlloc(bytes[cursor..], allocator); + try @field(result, field.name).decodeFromAlloc(in_stream, allocator); } else { - cursor += try @field(result, field.name).decodeFrom(bytes[cursor..]); + try @field(result, field.name).decodeFrom(in_stream); } break; } @@ -116,6 +62,9 @@ pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []u8) ! }, } } + } else |err| switch (err) { + error.EndOfStream => {}, + else => return err, } inline for (@typeInfo(T).Struct.fields) |field, i| { @@ -172,25 +121,27 @@ test "end-to-end" { }; var start = init(Example); - testing.expectEqual(i64(0), start.sint.data); - testing.expectEqual(false, start.boo.data); - testing.expectEqual(start.str.data, ""); + testing.expectEqual(i64(0), start.sint.value); + testing.expectEqual(false, start.boo.value); + testing.expectEqual(start.str.value, ""); testing.expectEqual(start.str.allocator, null); - start.sint.data = -17; - start.boo.data = true; - start.str.data = "weird"; + start.sint.value = -17; + start.boo.value = true; + start.str.value = "weird"; - const binary = try marshal(Example, std.heap.direct_allocator, start); - defer std.heap.direct_allocator.free(binary); + var buf: [1000]u8 = undefined; + var out = std.io.SliceOutStream.init(buf[0..]); + try encodeInto(Example, start, &out.stream); - var result = try unmarshal(Example, std.heap.direct_allocator, binary); - testing.expectEqual(start.sint.data, result.sint.data); - testing.expectEqual(start.boo.data, result.boo.data); - testing.expectEqualSlices(u8, start.str.data, result.str.data); + var mem_in = std.io.SliceInStream.init(out.getWritten()); + var result = try decodeFrom(Example, std.heap.direct_allocator, &mem_in.stream); + testing.expectEqual(start.sint.value, result.sint.value); + testing.expectEqual(start.boo.value, result.boo.value); + testing.expectEqualSlices(u8, start.str.value, result.str.value); testing.expectEqual(std.heap.direct_allocator, result.str.allocator.?); deinit(Example, &result); - testing.expectEqual(result.str.data, ""); + testing.expectEqual(result.str.value, ""); testing.expectEqual(result.str.allocator, null); } diff --git a/src/types.zig b/src/types.zig index d078914..760acde 100644 --- a/src/types.zig +++ b/src/types.zig @@ -2,8 +2,6 @@ const std = @import("std"); const coder = @import("coder.zig"); const testing = std.testing; -const ParseError = coder.ParseError; - const WireType = enum(u3) { Varint = 0, _64bit = 1, @@ -24,13 +22,13 @@ pub const FieldMeta = struct { }; } - pub fn encodeInto(self: FieldMeta, buffer: []u8) []u8 { + pub fn encodeInto(self: FieldMeta, out_stream: var) !void { const uint = (@intCast(u64, self.number) << 3) + @enumToInt(self.wire_type); - return coder.Uint64Coder.encode(buffer, uint); + try coder.Uint64Coder.encodeInto(out_stream, uint); } - pub fn decode(buffer: []const u8, len: *usize) ParseError!FieldMeta { - const raw = try coder.Uint64Coder.decode(buffer, len); + pub fn decode(in_stream: var) !FieldMeta { + const raw = try coder.Uint64Coder.decodeFrom(in_stream); return init(raw); } }; @@ -87,23 +85,21 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in return struct { const Self = @This(); - data: TargetPrimitive = 0, + value: TargetPrimitive = 0, pub const field_meta = info; pub fn encodeSize(self: Self) usize { - return Coder.encodeSize(@bitCast(Coder.primitive, self.data)); + return Coder.encodeSize(@bitCast(Coder.primitive, self.value)); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return Coder.encode(buffer, @bitCast(Coder.primitive, self.data)); + pub fn encodeInto(self: Self, out_stream: var) !void { + try Coder.encodeInto(out_stream, @bitCast(Coder.primitive, self.value)); } - pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { - var len: usize = undefined; - const raw = try Coder.decode(buffer, &len); - self.data = @bitCast(TargetPrimitive, raw); - return len; + pub fn decodeFrom(self: *Self, in_stream: var) !void { + const raw = try Coder.decodeFrom(in_stream); + self.value = @bitCast(TargetPrimitive, raw); } }; } @@ -112,59 +108,63 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime return struct { const Self = @This(); - data: TargetPrimitive = 0, + value: TargetPrimitive = 0, pub const field_meta = info; pub fn encodeSize(self: Self) usize { - return Coder.encodeSize(self.data); + return Coder.encodeSize(self.value); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return Coder.encode(buffer, self.data); + pub fn encodeInto(self: Self, out_stream: var) !void { + try Coder.encodeInto(out_stream, self.value); } - pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { - var len: usize = undefined; - const raw = try Coder.decode(buffer, &len); - self.data = @intCast(TargetPrimitive, raw); - return len; + pub fn decodeFrom(self: *Self, in_stream: var) !void { + const raw = try Coder.decodeFrom(in_stream); + self.value = @intCast(TargetPrimitive, raw); } }; } var rng = std.rand.DefaultPrng.init(0); fn testEncodeDecode(comptime T: type, base: T) !void { - var buf: [1000]u8 = undefined; + var buffer: [1000]u8 = undefined; + + var out = std.io.SliceOutStream.init(buffer[0..]); - const encoded_slice = base.encodeInto(buf[0..]); - testing.expectEqual(base.encodeSize(), encoded_slice.len); + try base.encodeInto(&out.stream); + testing.expectEqual(base.encodeSize(), out.getWritten().len); + var mem_in = std.io.SliceInStream.init(out.getWritten()); var decoded: T = undefined; - const decoded_len = try decoded.decodeFrom(encoded_slice); - testing.expectEqual(base.data, decoded.data); - testing.expectEqual(base.encodeSize(), decoded_len); + const decoded_len = try decoded.decodeFrom(&mem_in.stream); + testing.expectEqual(base.value, decoded.value); + testing.expectEqual(base.encodeSize(), mem_in.pos); } fn testEncodeDecodeSlices(comptime T: type, base: T) !void { - var buf: [1000]u8 = undefined; + var buffer: [1000]u8 = undefined; - const encoded_slice = base.encodeInto(buf[0..]); - testing.expectEqual(base.encodeSize(), encoded_slice.len); + var out = std.io.SliceOutStream.init(buffer[0..]); + try base.encodeInto(&out.stream); + testing.expectEqual(base.encodeSize(), out.getWritten().len); + + var mem_in = std.io.SliceInStream.init(out.getWritten()); var decoded: T = undefined; - const decoded_len = try decoded.decodeFromAlloc(encoded_slice, std.heap.direct_allocator); - testing.expectEqualSlices(u8, base.data, decoded.data); - testing.expectEqual(base.encodeSize(), decoded_len); + try decoded.decodeFromAlloc(&mem_in.stream, std.heap.direct_allocator); + testing.expectEqualSlices(u8, base.value, decoded.value); + testing.expectEqual(base.encodeSize(), mem_in.pos); } test "Var int" { inline for ([_]type{ Uint64(1), Int64(1), Sint64(1), Uint32(1), Int32(1), Sint32(1) }) |T| { - const data_field = std.meta.fieldInfo(T, "data"); + const value_field = std.meta.fieldInfo(T, "value"); var i = usize(0); while (i < 100) : (i += 1) { - const base = T{ .data = rng.random.int(data_field.field_type) }; + const base = T{ .value = rng.random.int(value_field.field_type) }; try testEncodeDecode(T, base); } } @@ -209,21 +209,21 @@ pub fn Float(comptime number: u63) type { test "Fixed numbers" { inline for ([_]type{ Fixed64(1), Fixed32(1), Sfixed64(1), Sfixed32(1) }) |T| { - const data_field = std.meta.fieldInfo(T, "data"); + const value_field = std.meta.fieldInfo(T, "value"); var i = usize(0); while (i < 100) : (i += 1) { - const base = T{ .data = rng.random.int(data_field.field_type) }; + const base = T{ .value = rng.random.int(value_field.field_type) }; try testEncodeDecode(T, base); } } inline for ([_]type{ Double(1), Float(1) }) |T| { - const data_field = std.meta.fieldInfo(T, "data"); + const value_field = std.meta.fieldInfo(T, "value"); var i = usize(0); while (i < 100) : (i += 1) { - const base = T{ .data = rng.random.float(data_field.field_type) }; + const base = T{ .value = rng.random.float(value_field.field_type) }; try testEncodeDecode(T, base); } } @@ -233,7 +233,7 @@ pub fn Bytes(comptime number: u63) type { return struct { const Self = @This(); - data: []u8 = [_]u8{}, + value: []u8 = [_]u8{}, allocator: ?*std.mem.Allocator = null, pub const field_meta = FieldMeta{ @@ -243,24 +243,22 @@ pub fn Bytes(comptime number: u63) type { pub fn deinit(self: *Self) void { if (self.allocator) |alloc| { - alloc.free(self.data); + alloc.free(self.value); self.* = Self{}; } } pub fn encodeSize(self: Self) usize { - return coder.BytesCoder.encodeSize(self.data); + return coder.BytesCoder.encodeSize(self.value); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return coder.BytesCoder.encode(buffer, self.data); + pub fn encodeInto(self: Self, out_stream: var) !void { + try coder.BytesCoder.encodeInto(out_stream, self.value); } - pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { - var len: usize = undefined; - self.data = try coder.BytesCoder.decode(buffer, &len, allocator); + pub fn decodeFromAlloc(self: *Self, in_stream: var, allocator: *std.mem.Allocator) !void { + self.value = try coder.BytesCoder.decodeFrom(in_stream, allocator); self.allocator = allocator; - return len; } }; } @@ -269,7 +267,7 @@ pub fn String(comptime number: u63) type { return struct { const Self = @This(); - data: []const u8 = "", + value: []const u8 = "", allocator: ?*std.mem.Allocator = null, pub const field_meta = FieldMeta{ @@ -279,25 +277,23 @@ pub fn String(comptime number: u63) type { pub fn deinit(self: *Self) void { if (self.allocator) |alloc| { - alloc.free(self.data); + alloc.free(self.value); self.* = Self{}; } } pub fn encodeSize(self: Self) usize { - return coder.BytesCoder.encodeSize(self.data); + return coder.BytesCoder.encodeSize(self.value); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return coder.BytesCoder.encode(buffer, self.data); + pub fn encodeInto(self: Self, out_stream: var) !void { + try coder.BytesCoder.encodeInto(out_stream, self.value); } - pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { + pub fn decodeFromAlloc(self: *Self, in_stream: var, allocator: *std.mem.Allocator) !void { // TODO: validate unicode - var len: usize = undefined; - self.data = try coder.BytesCoder.decode(buffer, &len, allocator); + self.value = try coder.BytesCoder.decodeFrom(in_stream, allocator); self.allocator = allocator; - return len; } }; } @@ -309,7 +305,7 @@ test "Bytes/String" { var base_buf: [500]u8 = undefined; rng.random.bytes(base_buf[0..]); - const base = T{ .data = base_buf[0..] }; + const base = T{ .value = base_buf[0..] }; try testEncodeDecodeSlices(T, base); } } @@ -319,7 +315,7 @@ pub fn Bool(comptime number: u63) type { return struct { const Self = @This(); - data: bool = false, + value: bool = false, pub const field_meta = FieldMeta{ .wire_type = .Varint, @@ -330,20 +326,17 @@ pub fn Bool(comptime number: u63) type { return 1; } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - buffer[0] = if (self.data) u8(1) else 0; - return buffer[0..1]; + pub fn encodeInto(self: Self, out_stream: var) !void { + try out_stream.writeByte(if (self.value) u8(1) else 0); } - pub fn decodeFrom(self: *Self, bytes: []const u8) ParseError!usize { - var len: usize = undefined; - const raw = try coder.Uint64Coder.decode(bytes, &len); + pub fn decodeFrom(self: *Self, in_stream: var) !void { + const raw = try in_stream.readByte(); // TODO: verify that bools *must* be 0 or 1 if (raw > 1) { return error.Overflow; } - self.data = raw != 0; - return len; + self.value = raw != 0; } }; } @@ -351,7 +344,7 @@ pub fn Bool(comptime number: u63) type { test "Bool" { @"fuzz": { inline for ([_]bool{ false, true }) |b| { - const base = Bool(1){ .data = b }; + const base = Bool(1){ .value = b }; try testEncodeDecode(Bool(1), base); } } @@ -360,84 +353,77 @@ test "Bool" { pub fn Repeated(comptime number: u63, comptime Tfn: var) type { const T = Tfn(number); - std.debug.assert(@hasField(T, "data")); + std.debug.assert(@hasField(T, "value")); std.debug.assert(@hasDecl(T, "encodeSize")); std.debug.assert(@hasDecl(T, "encodeInto")); std.debug.assert(@hasDecl(T, "decodeFrom")); - const DataType = std.meta.fieldInfo(T, "data").field_type; + const ValueType = std.meta.fieldInfo(T, "value").field_type; return struct { const Self = @This(); - const List = std.ArrayList(DataType); + const List = std.ArrayList(ValueType); - data: []DataType = [_]DataType{}, + value: []ValueType = [_]ValueType{}, allocator: ?*std.mem.Allocator = null, _decode_builder: ?List = null, pub fn deinit(self: *Self) void { if (self._decode_builder) |*decode_builder| { - std.debug.assert(self.data.len == 0); + std.debug.assert(self.value.len == 0); decode_builder.deinit(); self.* = Self{}; } else if (self.allocator) |alloc| { - alloc.free(self.data); + alloc.free(self.value); self.* = Self{}; } } pub fn encodeSize(self: Self) usize { var sum = usize(0); - for (self.data) |item| { - const wrapper = DataType{ .data = item }; + for (self.value) |item| { + const wrapper = ValueType{ .value = item }; sum += wrapper.encodeSize(); } return sum; } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - var cursor = usize(0); - for (self.data) |item| { - const wrapper = DataType{ .data = item }; - const result = wrapper.encodeInto(buffer[cursor..]); - cursor += result.len; + pub fn encodeInto(self: Self, out_stream: var) !void { + for (self.value) |item| { + const wrapper = ValueType{ .value = item }; + wrapper.encodeInto(out_stream); } - return buffer[0..cursor]; } - pub fn decodeFromAlloc(self: *Self, raw: []const u8, allocator: *std.mem.Allocator) ParseError!usize { + pub fn decodeFromAlloc(self: *Self, in_stream: var, allocator: *std.mem.Allocator) !void { if (self._decode_builder == null) { self.deinit(); self._decode_builder = List.init(allocator); } var base: T = undefined; - const len = try base.decodeFrom(raw); - try self._decode_builder.?.append(base.data); - return len; + try base.decodeFrom(in_stream); + try self._decode_builder.?.append(base.value); } - pub fn decodePacked(self: *Self, raw: []const u8, allocator: *std.mem.Allocator) ParseError!usize { - var header_len: usize = undefined; - const header = try Uint64.decode(raw, &header_len); + pub fn decodePacked(self: *Self, in_stream: var, allocator: *std.mem.Allocator) !usize { + const header = try Uint64.decodeFrom(in_stream); var items_len = usize(0); - while (items_len < header.data) { - items_len += try self.decodeFromAlloc(raw[header_len + items_len ..], &len, allocator); + while (items_len < header.value) { + items_len += try self.decodeFromAlloc(in_stream, allocator); } - if (items_len > header.data) { + if (items_len > header.value) { // Header listed length != items actual length return error.Overflow; } - - len.* = header_len + items_len; } pub fn decodeComplete(self: *Self) void { if (self._decode_builder) |*decode_builder| { - std.debug.assert(self.data.len == 0); + std.debug.assert(self.value.len == 0); self.allocator = decode_builder.allocator; - self.data = decode_builder.toOwnedSlice(); + self.value = decode_builder.toOwnedSlice(); self._decode_builder = null; } } @@ -445,12 +431,12 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { } test "Repeated" { - const twelve = [_]u8{ 12, 0, 0, 0 }; - const hundred = [_]u8{ 100, 0, 0, 0 }; + var twelve = std.io.SliceInStream.init([_]u8{ 12, 0, 0, 0 }); + var hundred = std.io.SliceInStream.init([_]u8{ 100, 0, 0, 0 }); var repeated_field = Repeated(1, Fixed32){}; - _ = try repeated_field.decodeFromAlloc(twelve[0..], std.heap.direct_allocator); - _ = try repeated_field.decodeFromAlloc(hundred[0..], std.heap.direct_allocator); + _ = try repeated_field.decodeFromAlloc(&twelve.stream, std.heap.direct_allocator); + _ = try repeated_field.decodeFromAlloc(&hundred.stream, std.heap.direct_allocator); repeated_field.decodeComplete(); - testing.expectEqualSlices(u32, [_]u32{ 12, 100 }, repeated_field.data); + testing.expectEqualSlices(u32, [_]u32{ 12, 100 }, repeated_field.value); }