From 6b2e369d8dfdb4195c49d2bdbc1aa9b1d3369a04 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 23 Aug 2019 18:07:49 -0500 Subject: [PATCH 1/9] Async-ify all encoding --- src/types.zig | 76 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/src/types.zig b/src/types.zig index d078914..e6b8faa 100644 --- a/src/types.zig +++ b/src/types.zig @@ -4,6 +4,21 @@ const testing = std.testing; const ParseError = coder.ParseError; +pub const AsyncContext = struct { + suspended: ?anyframe = null, + buffer: []u8 = [_]u8{}, + out: []u8 = [_]u8{}, + + fn next(self: *Self, buffer: []u8) ?[]u8 { + if (self.suspended) |frame| { + self.buffer = buffer; + resume frame; + return self.out; + } + return null; + } +}; + const WireType = enum(u3) { Varint = 0, _64bit = 1, @@ -24,9 +39,11 @@ pub const FieldMeta = struct { }; } - pub fn encodeInto(self: FieldMeta, buffer: []u8) []u8 { + pub fn encodeInto(self: FieldMeta, ctx: *AsyncContext) void { const uint = (@intCast(u64, self.number) << 3) + @enumToInt(self.wire_type); - return coder.Uint64Coder.encode(buffer, uint); + ctx.suspended = @frame(); + ctx.out = coder.Uint64Coder.encode(ctx.buffer, uint); + suspend; } pub fn decode(buffer: []const u8, len: *usize) ParseError!FieldMeta { @@ -95,8 +112,10 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in return Coder.encodeSize(@bitCast(Coder.primitive, self.data)); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return Coder.encode(buffer, @bitCast(Coder.primitive, self.data)); + pub fn encodeInto(self: Self, ctx: *AsyncContext) void { + ctx.suspended = @frame(); + ctx.out = Coder.encode(ctx.buffer, @bitCast(Coder.primitive, self.data)); + suspend; } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -120,8 +139,10 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime return Coder.encodeSize(self.data); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return Coder.encode(buffer, self.data); + pub fn encodeInto(self: Self, ctx: *AsyncContext) void { + ctx.suspended = @frame(); + ctx.out = Coder.encode(ctx.buffer, self.data); + suspend; } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -137,11 +158,13 @@ var rng = std.rand.DefaultPrng.init(0); fn testEncodeDecode(comptime T: type, base: T) !void { var buf: [1000]u8 = undefined; - const encoded_slice = base.encodeInto(buf[0..]); - testing.expectEqual(base.encodeSize(), encoded_slice.len); + var ctx = AsyncContext{ .buffer = buf[0..] }; + + _ = async base.encodeInto(&ctx); + testing.expectEqual(base.encodeSize(), ctx.out.len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFrom(encoded_slice); + const decoded_len = try decoded.decodeFrom(ctx.out); testing.expectEqual(base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -149,11 +172,13 @@ fn testEncodeDecode(comptime T: type, base: T) !void { fn testEncodeDecodeSlices(comptime T: type, base: T) !void { var buf: [1000]u8 = undefined; - const encoded_slice = base.encodeInto(buf[0..]); - testing.expectEqual(base.encodeSize(), encoded_slice.len); + var ctx = AsyncContext{ .buffer = buf[0..] }; + + _ = async base.encodeInto(&ctx); + testing.expectEqual(base.encodeSize(), ctx.out.len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFromAlloc(encoded_slice, std.heap.direct_allocator); + const decoded_len = try decoded.decodeFromAlloc(ctx.out, std.heap.direct_allocator); testing.expectEqualSlices(u8, base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -252,8 +277,10 @@ pub fn Bytes(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return coder.BytesCoder.encode(buffer, self.data); + pub fn encodeInto(self: Self, ctx: *AsyncContext) void { + ctx.suspended = @frame(); + ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); + suspend; } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -288,8 +315,10 @@ pub fn String(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - return coder.BytesCoder.encode(buffer, self.data); + pub fn encodeInto(self: Self, ctx: *AsyncContext) void { + ctx.suspended = @frame(); + ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); + suspend; } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -330,9 +359,11 @@ 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, ctx: *AsyncContext) void { + ctx.suspended = @frame(); + ctx.buffer[0] = if (self.data) u8(1) else 0; + ctx.out = ctx.buffer[0..1]; + suspend; } pub fn decodeFrom(self: *Self, bytes: []const u8) ParseError!usize { @@ -395,14 +426,11 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { return sum; } - pub fn encodeInto(self: Self, buffer: []u8) []u8 { - var cursor = usize(0); + pub fn encodeInto(self: Self, ctx: *AsyncContext) void { for (self.data) |item| { const wrapper = DataType{ .data = item }; - const result = wrapper.encodeInto(buffer[cursor..]); - cursor += result.len; + const result = wrapper.encodeInto(ctx); } - return buffer[0..cursor]; } pub fn decodeFromAlloc(self: *Self, raw: []const u8, allocator: *std.mem.Allocator) ParseError!usize { From ba7d45ae38361717252a03e9d4bc27b4424dca14 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 23 Aug 2019 21:29:37 -0500 Subject: [PATCH 2/9] Restructured streaming_encode --- src/main.zig | 80 +++++++++++++++------------------------------------ src/types.zig | 19 ++++++------ 2 files changed, 34 insertions(+), 65 deletions(-) diff --git a/src/main.zig b/src/main.zig index 958f198..a116a10 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,76 +22,42 @@ 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 streaming_encode(comptime T: type, item: T, ctx: *types.AsyncContext) void { + inline for (@typeInfo(T).Struct.fields) |field, i| { + switch (@typeInfo(field.field_type)) { + .Struct => { + if (@hasDecl(field.field_type, "field_meta")) { + field.field_type.field_meta.encodeInto(ctx); + @field(item, field.name).encodeInto(ctx); + } 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 result = std.ArrayList(u8).init(allocator); + errdefer result.deinit(); - var stream = StreamingMarshal(T).init(item); + var stream = types.AsyncContext{}; + _ = async streaming_encode(T, item, &stream); - while (stream.next()) |data| { - try buffer.appendSlice(data); + var scratch_pad: [1000]u8 = undefined; + while (stream.next(scratch_pad[0..])) |data| { + try result.appendSlice(data); } - return buffer.toOwnedSlice(); + return result.toOwnedSlice(); } pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []u8) !T { var result = init(T); - errdefer deinit(T, result); + errdefer deinit(T, &result); var cursor = usize(0); while (cursor < bytes.len) { diff --git a/src/types.zig b/src/types.zig index e6b8faa..e438538 100644 --- a/src/types.zig +++ b/src/types.zig @@ -9,10 +9,13 @@ pub const AsyncContext = struct { buffer: []u8 = [_]u8{}, out: []u8 = [_]u8{}, - fn next(self: *Self, buffer: []u8) ?[]u8 { + fn next(self: *AsyncContext, buffer: []u8) ?[]u8 { if (self.suspended) |frame| { + // Copy elision thing... maybe bug? + const frame_copy = frame; self.buffer = buffer; - resume frame; + self.suspended = null; + resume frame_copy; return self.out; } return null; @@ -42,8 +45,8 @@ pub const FieldMeta = struct { pub fn encodeInto(self: FieldMeta, ctx: *AsyncContext) void { const uint = (@intCast(u64, self.number) << 3) + @enumToInt(self.wire_type); ctx.suspended = @frame(); - ctx.out = coder.Uint64Coder.encode(ctx.buffer, uint); suspend; + ctx.out = coder.Uint64Coder.encode(ctx.buffer, uint); } pub fn decode(buffer: []const u8, len: *usize) ParseError!FieldMeta { @@ -114,8 +117,8 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in pub fn encodeInto(self: Self, ctx: *AsyncContext) void { ctx.suspended = @frame(); - ctx.out = Coder.encode(ctx.buffer, @bitCast(Coder.primitive, self.data)); suspend; + ctx.out = Coder.encode(ctx.buffer, @bitCast(Coder.primitive, self.data)); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -141,8 +144,8 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime pub fn encodeInto(self: Self, ctx: *AsyncContext) void { ctx.suspended = @frame(); - ctx.out = Coder.encode(ctx.buffer, self.data); suspend; + ctx.out = Coder.encode(ctx.buffer, self.data); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -279,8 +282,8 @@ pub fn Bytes(comptime number: u63) type { pub fn encodeInto(self: Self, ctx: *AsyncContext) void { ctx.suspended = @frame(); - ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); suspend; + ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -317,8 +320,8 @@ pub fn String(comptime number: u63) type { pub fn encodeInto(self: Self, ctx: *AsyncContext) void { ctx.suspended = @frame(); - ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); suspend; + ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -361,9 +364,9 @@ pub fn Bool(comptime number: u63) type { pub fn encodeInto(self: Self, ctx: *AsyncContext) void { ctx.suspended = @frame(); + suspend; ctx.buffer[0] = if (self.data) u8(1) else 0; ctx.out = ctx.buffer[0..1]; - suspend; } pub fn decodeFrom(self: *Self, bytes: []const u8) ParseError!usize { From 91085baa5078950ed7bf5af876150fc378e1d06d Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 23 Aug 2019 21:53:38 -0500 Subject: [PATCH 3/9] Localize suspend point --- src/main.zig | 3 +++ src/types.zig | 59 ++++++++++++++++++++++++--------------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/main.zig b/src/main.zig index a116a10..5657e66 100644 --- a/src/main.zig +++ b/src/main.zig @@ -23,6 +23,9 @@ pub const String = types.String; pub const Repeated = types.Repeated; pub fn streaming_encode(comptime T: type, item: T, ctx: *types.AsyncContext) void { + ctx.suspended = @frame(); + suspend; + inline for (@typeInfo(T).Struct.fields) |field, i| { switch (@typeInfo(field.field_type)) { .Struct => { diff --git a/src/types.zig b/src/types.zig index e438538..752e738 100644 --- a/src/types.zig +++ b/src/types.zig @@ -5,21 +5,30 @@ const testing = std.testing; const ParseError = coder.ParseError; pub const AsyncContext = struct { - suspended: ?anyframe = null, + suspended: anyframe = undefined, buffer: []u8 = [_]u8{}, - out: []u8 = [_]u8{}, + cursor: usize = std.math.maxInt(usize), fn next(self: *AsyncContext, buffer: []u8) ?[]u8 { - if (self.suspended) |frame| { - // Copy elision thing... maybe bug? - const frame_copy = frame; + if (self.cursor > 0) { self.buffer = buffer; - self.suspended = null; - resume frame_copy; - return self.out; + self.cursor = 0; + resume self.suspended; + return self.out(); } return null; } + + fn out(self: AsyncContext) []u8 { + return self.buffer[0..self.cursor]; + } + + fn append(self: *AsyncContext, items: []const u8) void { + std.mem.copy(u8, self.buffer, items); + self.cursor = items.len; + self.suspended = @frame(); + suspend; + } }; const WireType = enum(u3) { @@ -44,9 +53,7 @@ pub const FieldMeta = struct { pub fn encodeInto(self: FieldMeta, ctx: *AsyncContext) void { const uint = (@intCast(u64, self.number) << 3) + @enumToInt(self.wire_type); - ctx.suspended = @frame(); - suspend; - ctx.out = coder.Uint64Coder.encode(ctx.buffer, uint); + ctx.append(coder.Uint64Coder.encode(ctx.buffer, uint)); } pub fn decode(buffer: []const u8, len: *usize) ParseError!FieldMeta { @@ -116,9 +123,7 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in } pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.suspended = @frame(); - suspend; - ctx.out = Coder.encode(ctx.buffer, @bitCast(Coder.primitive, self.data)); + ctx.append(Coder.encode(ctx.buffer, @bitCast(Coder.primitive, self.data))); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -143,9 +148,7 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime } pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.suspended = @frame(); - suspend; - ctx.out = Coder.encode(ctx.buffer, self.data); + ctx.append(Coder.encode(ctx.buffer, self.data)); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -164,10 +167,10 @@ fn testEncodeDecode(comptime T: type, base: T) !void { var ctx = AsyncContext{ .buffer = buf[0..] }; _ = async base.encodeInto(&ctx); - testing.expectEqual(base.encodeSize(), ctx.out.len); + testing.expectEqual(base.encodeSize(), ctx.out().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFrom(ctx.out); + const decoded_len = try decoded.decodeFrom(ctx.out()); testing.expectEqual(base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -178,10 +181,10 @@ fn testEncodeDecodeSlices(comptime T: type, base: T) !void { var ctx = AsyncContext{ .buffer = buf[0..] }; _ = async base.encodeInto(&ctx); - testing.expectEqual(base.encodeSize(), ctx.out.len); + testing.expectEqual(base.encodeSize(), ctx.out().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFromAlloc(ctx.out, std.heap.direct_allocator); + const decoded_len = try decoded.decodeFromAlloc(ctx.out(), std.heap.direct_allocator); testing.expectEqualSlices(u8, base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -281,9 +284,7 @@ pub fn Bytes(comptime number: u63) type { } pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.suspended = @frame(); - suspend; - ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); + ctx.append(coder.BytesCoder.encode(ctx.buffer, self.data)); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -319,9 +320,7 @@ pub fn String(comptime number: u63) type { } pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.suspended = @frame(); - suspend; - ctx.out = coder.BytesCoder.encode(ctx.buffer, self.data); + ctx.append(coder.BytesCoder.encode(ctx.buffer, self.data)); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -363,10 +362,8 @@ pub fn Bool(comptime number: u63) type { } pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.suspended = @frame(); - suspend; - ctx.buffer[0] = if (self.data) u8(1) else 0; - ctx.out = ctx.buffer[0..1]; + const value = if (self.data) [_]u8{1} else [_]u8{0}; + ctx.append(value[0..]); } pub fn decodeFrom(self: *Self, bytes: []const u8) ParseError!usize { From 0d0364b93192b848c0c2adfe06c767439d1105a3 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 24 Aug 2019 12:20:13 -0500 Subject: [PATCH 4/9] Convert BytesCoder into using Writer.write() --- src/coder.zig | 15 +++++++++------ src/types.zig | 17 +++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/coder.zig b/src/coder.zig index ffdc431..064eb3b 100644 --- a/src/coder.zig +++ b/src/coder.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const types = @import("types.zig"); const testing = std.testing; pub const ParseError = error{ @@ -173,11 +174,11 @@ 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(comptime Writer: type, writer: *Writer, data: []const u8) !void { + var buffer: [100]u8 = undefined; + const header = Uint64Coder.encode(&buffer, data.len); + try writer.write(header); + try writer.write(data); } pub fn decode(buffer: []const u8, len: *usize, allocator: *std.mem.Allocator) ![]u8 { @@ -196,11 +197,13 @@ pub const BytesCoder = struct { test "BytesCoder" { var buffer: [1000]u8 = undefined; + var ctx = types.AsyncContext{ .buffer = buffer[0..], .cursor = 0 }; + try BytesCoder.encodeInto(types.AsyncContext, &ctx, "testing"); testing.expectEqualSlices( u8, [_]u8{ 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67 }, - BytesCoder.encode(buffer[0..], "testing"), + ctx.out(), ); } diff --git a/src/types.zig b/src/types.zig index 752e738..84fcfb7 100644 --- a/src/types.zig +++ b/src/types.zig @@ -23,6 +23,11 @@ pub const AsyncContext = struct { return self.buffer[0..self.cursor]; } + fn write(self: *AsyncContext, bytes: []const u8) std.os.WriteError!void { + std.mem.copy(u8, self.buffer[self.cursor..], bytes); + self.cursor += bytes.len; + } + fn append(self: *AsyncContext, items: []const u8) void { std.mem.copy(u8, self.buffer, items); self.cursor = items.len; @@ -164,7 +169,7 @@ var rng = std.rand.DefaultPrng.init(0); fn testEncodeDecode(comptime T: type, base: T) !void { var buf: [1000]u8 = undefined; - var ctx = AsyncContext{ .buffer = buf[0..] }; + var ctx = AsyncContext{ .buffer = buf[0..], .cursor = 0 }; _ = async base.encodeInto(&ctx); testing.expectEqual(base.encodeSize(), ctx.out().len); @@ -178,7 +183,7 @@ fn testEncodeDecode(comptime T: type, base: T) !void { fn testEncodeDecodeSlices(comptime T: type, base: T) !void { var buf: [1000]u8 = undefined; - var ctx = AsyncContext{ .buffer = buf[0..] }; + var ctx = AsyncContext{ .buffer = buf[0..], .cursor = 0 }; _ = async base.encodeInto(&ctx); testing.expectEqual(base.encodeSize(), ctx.out().len); @@ -283,8 +288,8 @@ pub fn Bytes(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.append(coder.BytesCoder.encode(ctx.buffer, self.data)); + pub fn encodeInto(self: Self, ctx: *AsyncContext) !void { + try coder.BytesCoder.encodeInto(AsyncContext, ctx, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -319,8 +324,8 @@ pub fn String(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.append(coder.BytesCoder.encode(ctx.buffer, self.data)); + pub fn encodeInto(self: Self, ctx: *AsyncContext) !void { + try coder.BytesCoder.encodeInto(AsyncContext, ctx, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { From eaee2294c486f168c33dce529069cef8994a7ffe Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sun, 25 Aug 2019 17:13:18 -0500 Subject: [PATCH 5/9] Switch from AsyncContext into OutStream interface --- src/coder.zig | 111 +++++++++++++++++++++++++------------------------- src/main.zig | 23 ++++------- src/types.zig | 90 +++++++++++++++++++--------------------- 3 files changed, 104 insertions(+), 120 deletions(-) diff --git a/src/coder.zig b/src/coder.zig index 064eb3b..b8cb003 100644 --- a/src/coder.zig +++ b/src/coder.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const types = @import("types.zig"); const testing = std.testing; pub const ParseError = error{ @@ -16,19 +15,18 @@ 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]; - } + pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: u64) !void { + var buffer = [_]u8{0}; + var i = usize(0); var value = data; - while (value > 0) : (i += 1) { - buffer[i] = u8(0x80) + @truncate(u7, value); + while (value >= 0x80) : (i += 1) { + buffer[0] = u8(0x80) + @truncate(u7, value); + try out_stream.write(buffer[0..]); value >>= 7; } - buffer[i - 1] &= 0x7F; - return buffer[0..i]; + buffer[0] = @truncate(u7, value); + try out_stream.write(buffer[0..]); } pub fn decode(bytes: []const u8, len: *usize) ParseError!u64 { @@ -69,8 +67,8 @@ 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(comptime OutStream: type, out_stream: *OutStream, data: i64) !void { + try Uint64Coder.encodeInto(OutStream, out_stream, @bitCast(u64, data)); } pub fn decode(bytes: []const u8, len: *usize) ParseError!i64 { @@ -79,14 +77,12 @@ pub const Int64Coder = struct { }; test "Int64Coder" { - var buf1: [1000]u8 = undefined; - var buf2: [1000]u8 = undefined; + var out1 = TestOutStream{}; + var out1 = TestOutStream{}; - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], std.math.maxInt(u64)), - Int64Coder.encode(buf2[0..], -1), - ); + try Uint64Coder.encodeInto(TestOutStream, &out1, std.math.maxInt(u64)); + try Int64Coder.encodeInto(TestOutStream, &out2, -1); + testing.expectEqualSlices(u8, out1.slice(), out2.slice()); } pub const Sint64Coder = struct { @@ -96,8 +92,8 @@ 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(comptime OutStream: type, out_stream: *OutStream, data: i64) !void { + try Uint64Coder.encodeInto(OutStream, out_stream, @bitCast(u64, (data << 1) ^ (data >> 63))); } pub fn decode(bytes: []const u8, len: *usize) ParseError!i64 { @@ -108,26 +104,20 @@ pub const Sint64Coder = struct { }; test "Sint64Coder" { - var buf1: [1000]u8 = undefined; - var buf2: [1000]u8 = undefined; + var out1 = TestOutStream{}; + var out2 = TestOutStream{}; - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], 1), - Sint64Coder.encode(buf2[0..], -1), - ); + try Uint64Coder.encodeInto(TestOutStream, &out1, 1); + try Sint64Coder.encodeInto(TestOutStream, &out2, -1); + testing.expectEqualSlices(u8, out1.slice(), out2.slice()); - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], 4294967294), - Sint64Coder.encode(buf2[0..], 2147483647), - ); + try Uint64Coder.encodeInto(TestOutStream, &out1, 4294967294); + try Sint64Coder.encodeInto(TestOutStream, &out2, 2147483647); + testing.expectEqualSlices(u8, out1.slice(), out2.slice()); - testing.expectEqualSlices( - u8, - Uint64Coder.encode(buf1[0..], 4294967295), - Sint64Coder.encode(buf2[0..], -2147483648), - ); + try Uint64Coder.encodeInto(TestOutStream, &out1, 4294967295); + try Sint64Coder.encodeInto(TestOutStream, &out2, -2147483648); + testing.expectEqualSlices(u8, out1.slice(), out2.slice()); } pub const Fixed64Coder = struct { @@ -137,10 +127,10 @@ 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(comptime OutStream: type, out_stream: *OutStream, 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 { @@ -156,10 +146,10 @@ 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(comptime OutStream: type, out_stream: *OutStream, 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 { @@ -174,11 +164,9 @@ pub const BytesCoder = struct { return header_size + data.len; } - pub fn encodeInto(comptime Writer: type, writer: *Writer, data: []const u8) !void { - var buffer: [100]u8 = undefined; - const header = Uint64Coder.encode(&buffer, data.len); - try writer.write(header); - try writer.write(data); + pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: []const u8) !void { + try Uint64Coder.encodeInto(OutStream, out_stream, data.len); + try out_stream.write(data); } pub fn decode(buffer: []const u8, len: *usize, allocator: *std.mem.Allocator) ![]u8 { @@ -196,17 +184,30 @@ pub const BytesCoder = struct { }; test "BytesCoder" { - var buffer: [1000]u8 = undefined; - var ctx = types.AsyncContext{ .buffer = buffer[0..], .cursor = 0 }; - try BytesCoder.encodeInto(types.AsyncContext, &ctx, "testing"); + var out_stream = TestOutStream{}; + try BytesCoder.encodeInto(TestOutStream, &out_stream, "testing"); testing.expectEqualSlices( u8, [_]u8{ 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67 }, - ctx.out(), + out_stream.slice(), ); } fn divCeil(comptime T: type, numerator: T, denominator: T) T { return (numerator + denominator - 1) / denominator; } + +const TestOutStream = struct { + buffer: [1000]u8 = undefined, + cursor: usize = 0, + + fn write(self: *TestOutStream, bytes: []const u8) std.os.WriteError!void { + std.mem.copy(u8, self.buffer[self.cursor..], bytes); + self.cursor += bytes.len; + } + + fn slice(self: TestOutStream) []u8 { + return self.buffer[0..self.cursor]; + } +}; diff --git a/src/main.zig b/src/main.zig index 5657e66..e1cb4a3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,16 +22,13 @@ pub const String = types.String; pub const Repeated = types.Repeated; -pub fn streaming_encode(comptime T: type, item: T, ctx: *types.AsyncContext) void { - ctx.suspended = @frame(); - suspend; - +pub fn encode(comptime T: type, item: T, writer: *types.BufferedWriter) !void { inline for (@typeInfo(T).Struct.fields) |field, i| { switch (@typeInfo(field.field_type)) { .Struct => { if (@hasDecl(field.field_type, "field_meta")) { - field.field_type.field_meta.encodeInto(ctx); - @field(item, field.name).encodeInto(ctx); + try field.field_type.field_meta.encodeInto(types.BufferedWriter, writer); + try @field(item, field.name).encodeInto(types.BufferedWriter, writer); } else { std.debug.warn("{} - unknown struct\n", field.name); } @@ -44,18 +41,12 @@ pub fn streaming_encode(comptime T: type, item: T, ctx: *types.AsyncContext) voi } pub fn marshal(comptime T: type, allocator: *std.mem.Allocator, item: T) ![]u8 { - var result = std.ArrayList(u8).init(allocator); - errdefer result.deinit(); - - var stream = types.AsyncContext{}; - _ = async streaming_encode(T, item, &stream); + var writer = types.BufferedWriter.init(allocator); + errdefer writer.deinit(); - var scratch_pad: [1000]u8 = undefined; - while (stream.next(scratch_pad[0..])) |data| { - try result.appendSlice(data); - } + try encode(T, item, &writer); - return result.toOwnedSlice(); + return writer.toOwnedSlice(); } pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []u8) !T { diff --git a/src/types.zig b/src/types.zig index 84fcfb7..050001e 100644 --- a/src/types.zig +++ b/src/types.zig @@ -4,35 +4,31 @@ const testing = std.testing; const ParseError = coder.ParseError; -pub const AsyncContext = struct { - suspended: anyframe = undefined, - buffer: []u8 = [_]u8{}, - cursor: usize = std.math.maxInt(usize), - - fn next(self: *AsyncContext, buffer: []u8) ?[]u8 { - if (self.cursor > 0) { - self.buffer = buffer; - self.cursor = 0; - resume self.suspended; - return self.out(); - } - return null; +pub const BufferedWriter = struct { + list: std.ArrayList(u8), + + pub fn init(allocator: *std.mem.Allocator) BufferedWriter { + return BufferedWriter{ + .list = std.ArrayList(u8).init(allocator), + }; + } + + pub fn deinit(self: *BufferedWriter) void { + return self.deinit(); } - fn out(self: AsyncContext) []u8 { - return self.buffer[0..self.cursor]; + pub fn write(self: *BufferedWriter, bytes: []const u8) std.os.WriteError!void { + self.list.appendSlice(bytes) catch |err| switch (err) { + error.OutOfMemory => return error.NoSpaceLeft, + }; } - fn write(self: *AsyncContext, bytes: []const u8) std.os.WriteError!void { - std.mem.copy(u8, self.buffer[self.cursor..], bytes); - self.cursor += bytes.len; + pub fn toSlice(self: *BufferedWriter) []u8 { + return self.list.toSlice(); } - fn append(self: *AsyncContext, items: []const u8) void { - std.mem.copy(u8, self.buffer, items); - self.cursor = items.len; - self.suspended = @frame(); - suspend; + pub fn toOwnedSlice(self: *BufferedWriter) []u8 { + return self.list.toOwnedSlice(); } }; @@ -56,9 +52,9 @@ pub const FieldMeta = struct { }; } - pub fn encodeInto(self: FieldMeta, ctx: *AsyncContext) void { + pub fn encodeInto(self: FieldMeta, comptime Writer: type, writer: *Writer) !void { const uint = (@intCast(u64, self.number) << 3) + @enumToInt(self.wire_type); - ctx.append(coder.Uint64Coder.encode(ctx.buffer, uint)); + try coder.Uint64Coder.encodeInto(Writer, writer, uint); } pub fn decode(buffer: []const u8, len: *usize) ParseError!FieldMeta { @@ -127,8 +123,8 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in return Coder.encodeSize(@bitCast(Coder.primitive, self.data)); } - pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.append(Coder.encode(ctx.buffer, @bitCast(Coder.primitive, self.data))); + pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { + try Coder.encodeInto(Writer, writer, @bitCast(Coder.primitive, self.data)); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -152,8 +148,8 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime return Coder.encodeSize(self.data); } - pub fn encodeInto(self: Self, ctx: *AsyncContext) void { - ctx.append(Coder.encode(ctx.buffer, self.data)); + pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { + try Coder.encodeInto(Writer, writer, self.data); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -167,29 +163,25 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime var rng = std.rand.DefaultPrng.init(0); fn testEncodeDecode(comptime T: type, base: T) !void { - var buf: [1000]u8 = undefined; + var writer = BufferedWriter.init(std.heap.direct_allocator); - var ctx = AsyncContext{ .buffer = buf[0..], .cursor = 0 }; - - _ = async base.encodeInto(&ctx); - testing.expectEqual(base.encodeSize(), ctx.out().len); + try base.encodeInto(BufferedWriter, &writer); + testing.expectEqual(base.encodeSize(), writer.toSlice().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFrom(ctx.out()); + const decoded_len = try decoded.decodeFrom(writer.toSlice()); testing.expectEqual(base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } fn testEncodeDecodeSlices(comptime T: type, base: T) !void { - var buf: [1000]u8 = undefined; - - var ctx = AsyncContext{ .buffer = buf[0..], .cursor = 0 }; + var writer = BufferedWriter.init(std.heap.direct_allocator); - _ = async base.encodeInto(&ctx); - testing.expectEqual(base.encodeSize(), ctx.out().len); + try base.encodeInto(BufferedWriter, &writer); + testing.expectEqual(base.encodeSize(), writer.toSlice().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFromAlloc(ctx.out(), std.heap.direct_allocator); + const decoded_len = try decoded.decodeFromAlloc(writer.toSlice(), std.heap.direct_allocator); testing.expectEqualSlices(u8, base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -288,8 +280,8 @@ pub fn Bytes(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, ctx: *AsyncContext) !void { - try coder.BytesCoder.encodeInto(AsyncContext, ctx, self.data); + pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { + try coder.BytesCoder.encodeInto(Writer, writer, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -324,8 +316,8 @@ pub fn String(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, ctx: *AsyncContext) !void { - try coder.BytesCoder.encodeInto(AsyncContext, ctx, self.data); + pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { + try coder.BytesCoder.encodeInto(Writer, writer, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -366,9 +358,9 @@ pub fn Bool(comptime number: u63) type { return 1; } - pub fn encodeInto(self: Self, ctx: *AsyncContext) void { + pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { const value = if (self.data) [_]u8{1} else [_]u8{0}; - ctx.append(value[0..]); + try writer.write(value[0..]); } pub fn decodeFrom(self: *Self, bytes: []const u8) ParseError!usize { @@ -431,10 +423,10 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { return sum; } - pub fn encodeInto(self: Self, ctx: *AsyncContext) void { + pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { for (self.data) |item| { const wrapper = DataType{ .data = item }; - const result = wrapper.encodeInto(ctx); + wrapper.encodeInto(Writer, writer); } } From bf9205e845fe1d3a3c3ff73e890a8d02eb26e821 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Mon, 26 Aug 2019 08:21:58 -0500 Subject: [PATCH 6/9] Pushed IO into separate file --- src/io.zig | 29 ++++++++++++++++++++ src/main.zig | 18 ++++++++----- src/types.zig | 73 ++++++++++++++++----------------------------------- 3 files changed, 63 insertions(+), 57 deletions(-) create mode 100644 src/io.zig diff --git a/src/io.zig b/src/io.zig new file mode 100644 index 0000000..56cd9d7 --- /dev/null +++ b/src/io.zig @@ -0,0 +1,29 @@ +const std = @import("std"); + +pub const AutoAllocOutStream = struct { + list: std.ArrayList(u8), + + pub fn init(allocator: *std.mem.Allocator) AutoAllocOutStream { + return AutoAllocOutStream{ + .list = std.ArrayList(u8).init(allocator), + }; + } + + pub fn deinit(self: *AutoAllocOutStream) void { + return self.deinit(); + } + + pub fn write(self: *AutoAllocOutStream, bytes: []const u8) !void { + self.list.appendSlice(bytes) catch |err| switch (err) { + error.OutOfMemory => return error.NoSpaceLeft, + }; + } + + pub fn toSlice(self: *AutoAllocOutStream) []u8 { + return self.list.toSlice(); + } + + pub fn toOwnedSlice(self: *AutoAllocOutStream) []u8 { + return self.list.toOwnedSlice(); + } +}; diff --git a/src/main.zig b/src/main.zig index e1cb4a3..5363e88 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const testing = std.testing; const types = @import("types.zig"); +const io = @import("io.zig"); pub const Double = types.Double; pub const Float = types.Float; @@ -22,13 +23,16 @@ pub const String = types.String; pub const Repeated = types.Repeated; -pub fn encode(comptime T: type, item: T, writer: *types.BufferedWriter) !void { +pub fn encode(item: var, out_stream: var) !void { + const T = @typeOf(item); + const OutStream = @typeOf(out_stream.*); + 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(types.BufferedWriter, writer); - try @field(item, field.name).encodeInto(types.BufferedWriter, writer); + try field.field_type.field_meta.encodeInto(OutStream, out_stream); + try @field(item, field.name).encodeInto(OutStream, out_stream); } else { std.debug.warn("{} - unknown struct\n", field.name); } @@ -41,12 +45,12 @@ pub fn encode(comptime T: type, item: T, writer: *types.BufferedWriter) !void { } pub fn marshal(comptime T: type, allocator: *std.mem.Allocator, item: T) ![]u8 { - var writer = types.BufferedWriter.init(allocator); - errdefer writer.deinit(); + var out_stream = io.AutoAllocOutStream.init(allocator); + errdefer out_stream.deinit(); - try encode(T, item, &writer); + try encode(item, &out_stream); - return writer.toOwnedSlice(); + return out_stream.toOwnedSlice(); } pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []u8) !T { diff --git a/src/types.zig b/src/types.zig index 050001e..8c621b6 100644 --- a/src/types.zig +++ b/src/types.zig @@ -1,37 +1,10 @@ const std = @import("std"); const coder = @import("coder.zig"); +const io = @import("io.zig"); const testing = std.testing; const ParseError = coder.ParseError; -pub const BufferedWriter = struct { - list: std.ArrayList(u8), - - pub fn init(allocator: *std.mem.Allocator) BufferedWriter { - return BufferedWriter{ - .list = std.ArrayList(u8).init(allocator), - }; - } - - pub fn deinit(self: *BufferedWriter) void { - return self.deinit(); - } - - pub fn write(self: *BufferedWriter, bytes: []const u8) std.os.WriteError!void { - self.list.appendSlice(bytes) catch |err| switch (err) { - error.OutOfMemory => return error.NoSpaceLeft, - }; - } - - pub fn toSlice(self: *BufferedWriter) []u8 { - return self.list.toSlice(); - } - - pub fn toOwnedSlice(self: *BufferedWriter) []u8 { - return self.list.toOwnedSlice(); - } -}; - const WireType = enum(u3) { Varint = 0, _64bit = 1, @@ -52,9 +25,9 @@ pub const FieldMeta = struct { }; } - pub fn encodeInto(self: FieldMeta, comptime Writer: type, writer: *Writer) !void { + pub fn encodeInto(self: FieldMeta, comptime OutStream: type, out_stream: *OutStream) !void { const uint = (@intCast(u64, self.number) << 3) + @enumToInt(self.wire_type); - try coder.Uint64Coder.encodeInto(Writer, writer, uint); + try coder.Uint64Coder.encodeInto(OutStream, out_stream, uint); } pub fn decode(buffer: []const u8, len: *usize) ParseError!FieldMeta { @@ -123,8 +96,8 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in return Coder.encodeSize(@bitCast(Coder.primitive, self.data)); } - pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { - try Coder.encodeInto(Writer, writer, @bitCast(Coder.primitive, self.data)); + pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { + try Coder.encodeInto(OutStream, out_stream, @bitCast(Coder.primitive, self.data)); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -148,8 +121,8 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime return Coder.encodeSize(self.data); } - pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { - try Coder.encodeInto(Writer, writer, self.data); + pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { + try Coder.encodeInto(OutStream, out_stream, self.data); } pub fn decodeFrom(self: *Self, buffer: []const u8) ParseError!usize { @@ -163,25 +136,25 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime var rng = std.rand.DefaultPrng.init(0); fn testEncodeDecode(comptime T: type, base: T) !void { - var writer = BufferedWriter.init(std.heap.direct_allocator); + var out_stream = io.AutoAllocOutStream.init(std.heap.direct_allocator); - try base.encodeInto(BufferedWriter, &writer); - testing.expectEqual(base.encodeSize(), writer.toSlice().len); + try base.encodeInto(io.AutoAllocOutStream, &out_stream); + testing.expectEqual(base.encodeSize(), out_stream.toSlice().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFrom(writer.toSlice()); + const decoded_len = try decoded.decodeFrom(out_stream.toSlice()); testing.expectEqual(base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } fn testEncodeDecodeSlices(comptime T: type, base: T) !void { - var writer = BufferedWriter.init(std.heap.direct_allocator); + var out_stream = io.AutoAllocOutStream.init(std.heap.direct_allocator); - try base.encodeInto(BufferedWriter, &writer); - testing.expectEqual(base.encodeSize(), writer.toSlice().len); + try base.encodeInto(io.AutoAllocOutStream, &out_stream); + testing.expectEqual(base.encodeSize(), out_stream.toSlice().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFromAlloc(writer.toSlice(), std.heap.direct_allocator); + const decoded_len = try decoded.decodeFromAlloc(out_stream.toSlice(), std.heap.direct_allocator); testing.expectEqualSlices(u8, base.data, decoded.data); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -280,8 +253,8 @@ pub fn Bytes(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { - try coder.BytesCoder.encodeInto(Writer, writer, self.data); + pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { + try coder.BytesCoder.encodeInto(OutStream, out_stream, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -316,8 +289,8 @@ pub fn String(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.data); } - pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { - try coder.BytesCoder.encodeInto(Writer, writer, self.data); + pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { + try coder.BytesCoder.encodeInto(OutStream, out_stream, self.data); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { @@ -358,9 +331,9 @@ pub fn Bool(comptime number: u63) type { return 1; } - pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { + pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { const value = if (self.data) [_]u8{1} else [_]u8{0}; - try writer.write(value[0..]); + try out_stream.write(value[0..]); } pub fn decodeFrom(self: *Self, bytes: []const u8) ParseError!usize { @@ -423,10 +396,10 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { return sum; } - pub fn encodeInto(self: Self, comptime Writer: type, writer: *Writer) !void { + pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { for (self.data) |item| { const wrapper = DataType{ .data = item }; - wrapper.encodeInto(Writer, writer); + wrapper.encodeInto(OutStream, out_stream); } } From 12938dd187696d9cece02fba22a185884d8963b3 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Mon, 26 Aug 2019 08:24:33 -0500 Subject: [PATCH 7/9] Use .value over .data --- src/main.zig | 20 +++++------ src/types.zig | 94 +++++++++++++++++++++++++-------------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/main.zig b/src/main.zig index 5363e88..d88f1f3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -136,25 +136,25 @@ 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 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); + 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 8c621b6..933bc34 100644 --- a/src/types.zig +++ b/src/types.zig @@ -88,22 +88,22 @@ 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, comptime OutStream: type, out_stream: *OutStream) !void { - try Coder.encodeInto(OutStream, out_stream, @bitCast(Coder.primitive, self.data)); + try Coder.encodeInto(OutStream, 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); + self.value = @bitCast(TargetPrimitive, raw); return len; } }; @@ -113,22 +113,22 @@ 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, comptime OutStream: type, out_stream: *OutStream) !void { - try Coder.encodeInto(OutStream, out_stream, self.data); + try Coder.encodeInto(OutStream, 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); + self.value = @intCast(TargetPrimitive, raw); return len; } }; @@ -143,7 +143,7 @@ fn testEncodeDecode(comptime T: type, base: T) !void { var decoded: T = undefined; const decoded_len = try decoded.decodeFrom(out_stream.toSlice()); - testing.expectEqual(base.data, decoded.data); + testing.expectEqual(base.value, decoded.value); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -155,17 +155,17 @@ fn testEncodeDecodeSlices(comptime T: type, base: T) !void { var decoded: T = undefined; const decoded_len = try decoded.decodeFromAlloc(out_stream.toSlice(), std.heap.direct_allocator); - testing.expectEqualSlices(u8, base.data, decoded.data); + testing.expectEqualSlices(u8, base.value, decoded.value); testing.expectEqual(base.encodeSize(), decoded_len); } 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); } } @@ -210,21 +210,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); } } @@ -234,7 +234,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{ @@ -244,22 +244,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, comptime OutStream: type, out_stream: *OutStream) !void { - try coder.BytesCoder.encodeInto(OutStream, out_stream, self.data); + try coder.BytesCoder.encodeInto(OutStream, 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); + self.value = try coder.BytesCoder.decode(buffer, &len, allocator); self.allocator = allocator; return len; } @@ -270,7 +270,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{ @@ -280,23 +280,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, comptime OutStream: type, out_stream: *OutStream) !void { - try coder.BytesCoder.encodeInto(OutStream, out_stream, self.data); + try coder.BytesCoder.encodeInto(OutStream, out_stream, self.value); } pub fn decodeFromAlloc(self: *Self, buffer: []const u8, allocator: *std.mem.Allocator) ParseError!usize { // TODO: validate unicode var len: usize = undefined; - self.data = try coder.BytesCoder.decode(buffer, &len, allocator); + self.value = try coder.BytesCoder.decode(buffer, &len, allocator); self.allocator = allocator; return len; } @@ -310,7 +310,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); } } @@ -320,7 +320,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, @@ -332,7 +332,7 @@ pub fn Bool(comptime number: u63) type { } pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { - const value = if (self.data) [_]u8{1} else [_]u8{0}; + const value = if (self.value) [_]u8{1} else [_]u8{0}; try out_stream.write(value[0..]); } @@ -343,7 +343,7 @@ pub fn Bool(comptime number: u63) type { if (raw > 1) { return error.Overflow; } - self.data = raw != 0; + self.value = raw != 0; return len; } }; @@ -352,7 +352,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); } } @@ -361,44 +361,44 @@ 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, comptime OutStream: type, out_stream: *OutStream) !void { - for (self.data) |item| { - const wrapper = DataType{ .data = item }; + for (self.value) |item| { + const wrapper = ValueType{ .value = item }; wrapper.encodeInto(OutStream, out_stream); } } @@ -410,7 +410,7 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { } var base: T = undefined; const len = try base.decodeFrom(raw); - try self._decode_builder.?.append(base.data); + try self._decode_builder.?.append(base.value); return len; } @@ -419,11 +419,11 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { const header = try Uint64.decode(raw, &header_len); var items_len = usize(0); - while (items_len < header.data) { + while (items_len < header.value) { items_len += try self.decodeFromAlloc(raw[header_len + items_len ..], &len, allocator); } - if (items_len > header.data) { + if (items_len > header.value) { // Header listed length != items actual length return error.Overflow; } @@ -433,9 +433,9 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { 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; } } @@ -450,5 +450,5 @@ test "Repeated" { _ = try repeated_field.decodeFromAlloc(twelve[0..], std.heap.direct_allocator); _ = try repeated_field.decodeFromAlloc(hundred[0..], 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); } From 568d2223de255381b0ddd81311003a98652a7517 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Mon, 26 Aug 2019 16:31:58 -0500 Subject: [PATCH 8/9] Use std.io builtins --- src/coder.zig | 99 +++++++++++++++++++++++---------------------------- src/io.zig | 29 --------------- src/main.zig | 26 +++++--------- src/types.zig | 50 +++++++++++++------------- 4 files changed, 78 insertions(+), 126 deletions(-) delete mode 100644 src/io.zig diff --git a/src/coder.zig b/src/coder.zig index b8cb003..af8aa09 100644 --- a/src/coder.zig +++ b/src/coder.zig @@ -15,18 +15,12 @@ pub const Uint64Coder = struct { return std.math.max(divCeil(u64, bits, 7), 1); } - pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: u64) !void { - var buffer = [_]u8{0}; - - var i = usize(0); + pub fn encodeInto(out_stream: var, data: u64) !void { var value = data; - while (value >= 0x80) : (i += 1) { - buffer[0] = u8(0x80) + @truncate(u7, value); - try out_stream.write(buffer[0..]); - value >>= 7; + while (value >= 0x80) : (value >>= 7) { + try out_stream.writeByte(u8(0x80) + @truncate(u7, value)); } - buffer[0] = @truncate(u7, value); - try out_stream.write(buffer[0..]); + try out_stream.writeByte(@truncate(u7, value)); } pub fn decode(bytes: []const u8, len: *usize) ParseError!u64 { @@ -67,8 +61,8 @@ pub const Int64Coder = struct { return Uint64Coder.encodeSize(@bitCast(u64, data)); } - pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: i64) !void { - try Uint64Coder.encodeInto(OutStream, out_stream, @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 { @@ -77,12 +71,14 @@ pub const Int64Coder = struct { }; test "Int64Coder" { - var out1 = TestOutStream{}; - var out1 = TestOutStream{}; - - try Uint64Coder.encodeInto(TestOutStream, &out1, std.math.maxInt(u64)); - try Int64Coder.encodeInto(TestOutStream, &out2, -1); - testing.expectEqualSlices(u8, out1.slice(), out2.slice()); + 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..]); + + 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 { @@ -92,8 +88,8 @@ pub const Sint64Coder = struct { return Uint64Coder.encodeSize(@bitCast(u64, (data << 1) ^ (data >> 63))); } - pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: i64) !void { - try Uint64Coder.encodeInto(OutStream, out_stream, @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 { @@ -104,20 +100,26 @@ pub const Sint64Coder = struct { }; test "Sint64Coder" { - var out1 = TestOutStream{}; - var out2 = TestOutStream{}; - - try Uint64Coder.encodeInto(TestOutStream, &out1, 1); - try Sint64Coder.encodeInto(TestOutStream, &out2, -1); - testing.expectEqualSlices(u8, out1.slice(), out2.slice()); - - try Uint64Coder.encodeInto(TestOutStream, &out1, 4294967294); - try Sint64Coder.encodeInto(TestOutStream, &out2, 2147483647); - testing.expectEqualSlices(u8, out1.slice(), out2.slice()); - - try Uint64Coder.encodeInto(TestOutStream, &out1, 4294967295); - try Sint64Coder.encodeInto(TestOutStream, &out2, -2147483648); - testing.expectEqualSlices(u8, out1.slice(), out2.slice()); + 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..]); + + 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 { @@ -127,7 +129,7 @@ pub const Fixed64Coder = struct { return 8; } - pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: u64) !void { + 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..]); @@ -146,7 +148,7 @@ pub const Fixed32Coder = struct { return 4; } - pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: u32) !void { + 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..]); @@ -164,8 +166,8 @@ pub const BytesCoder = struct { return header_size + data.len; } - pub fn encodeInto(comptime OutStream: type, out_stream: *OutStream, data: []const u8) !void { - try Uint64Coder.encodeInto(OutStream, out_stream, data.len); + pub fn encodeInto(out_stream: var, data: []const u8) !void { + try Uint64Coder.encodeInto(out_stream, data.len); try out_stream.write(data); } @@ -184,30 +186,17 @@ pub const BytesCoder = struct { }; test "BytesCoder" { - var out_stream = TestOutStream{}; - try BytesCoder.encodeInto(TestOutStream, &out_stream, "testing"); + 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 }, - out_stream.slice(), + out.getWritten(), ); } fn divCeil(comptime T: type, numerator: T, denominator: T) T { return (numerator + denominator - 1) / denominator; } - -const TestOutStream = struct { - buffer: [1000]u8 = undefined, - cursor: usize = 0, - - fn write(self: *TestOutStream, bytes: []const u8) std.os.WriteError!void { - std.mem.copy(u8, self.buffer[self.cursor..], bytes); - self.cursor += bytes.len; - } - - fn slice(self: TestOutStream) []u8 { - return self.buffer[0..self.cursor]; - } -}; diff --git a/src/io.zig b/src/io.zig deleted file mode 100644 index 56cd9d7..0000000 --- a/src/io.zig +++ /dev/null @@ -1,29 +0,0 @@ -const std = @import("std"); - -pub const AutoAllocOutStream = struct { - list: std.ArrayList(u8), - - pub fn init(allocator: *std.mem.Allocator) AutoAllocOutStream { - return AutoAllocOutStream{ - .list = std.ArrayList(u8).init(allocator), - }; - } - - pub fn deinit(self: *AutoAllocOutStream) void { - return self.deinit(); - } - - pub fn write(self: *AutoAllocOutStream, bytes: []const u8) !void { - self.list.appendSlice(bytes) catch |err| switch (err) { - error.OutOfMemory => return error.NoSpaceLeft, - }; - } - - pub fn toSlice(self: *AutoAllocOutStream) []u8 { - return self.list.toSlice(); - } - - pub fn toOwnedSlice(self: *AutoAllocOutStream) []u8 { - return self.list.toOwnedSlice(); - } -}; diff --git a/src/main.zig b/src/main.zig index d88f1f3..7fd6d56 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const testing = std.testing; const types = @import("types.zig"); -const io = @import("io.zig"); pub const Double = types.Double; pub const Float = types.Float; @@ -23,16 +22,15 @@ pub const String = types.String; pub const Repeated = types.Repeated; -pub fn encode(item: var, out_stream: var) !void { +pub fn encodeInto(out_stream: var, item: var) !void { const T = @typeOf(item); - const OutStream = @typeOf(out_stream.*); 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(OutStream, out_stream); - try @field(item, field.name).encodeInto(OutStream, out_stream); + 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); } @@ -44,16 +42,7 @@ pub fn encode(item: var, out_stream: var) !void { } } -pub fn marshal(comptime T: type, allocator: *std.mem.Allocator, item: T) ![]u8 { - var out_stream = io.AutoAllocOutStream.init(allocator); - errdefer out_stream.deinit(); - - try encode(item, &out_stream); - - return out_stream.toOwnedSlice(); -} - -pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []u8) !T { +pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []const u8) !T { var result = init(T); errdefer deinit(T, &result); @@ -145,10 +134,11 @@ test "end-to-end" { 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(&out.stream, start); - var result = try unmarshal(Example, std.heap.direct_allocator, binary); + var result = try unmarshal(Example, std.heap.direct_allocator, out.getWritten()); 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); diff --git a/src/types.zig b/src/types.zig index 933bc34..ecee403 100644 --- a/src/types.zig +++ b/src/types.zig @@ -1,6 +1,5 @@ const std = @import("std"); const coder = @import("coder.zig"); -const io = @import("io.zig"); const testing = std.testing; const ParseError = coder.ParseError; @@ -25,9 +24,9 @@ pub const FieldMeta = struct { }; } - pub fn encodeInto(self: FieldMeta, comptime OutStream: type, out_stream: *OutStream) !void { + pub fn encodeInto(self: FieldMeta, out_stream: var) !void { const uint = (@intCast(u64, self.number) << 3) + @enumToInt(self.wire_type); - try coder.Uint64Coder.encodeInto(OutStream, out_stream, uint); + try coder.Uint64Coder.encodeInto(out_stream, uint); } pub fn decode(buffer: []const u8, len: *usize) ParseError!FieldMeta { @@ -96,8 +95,8 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in return Coder.encodeSize(@bitCast(Coder.primitive, self.value)); } - pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { - try Coder.encodeInto(OutStream, out_stream, @bitCast(Coder.primitive, self.value)); + 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 { @@ -121,8 +120,8 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime return Coder.encodeSize(self.value); } - pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { - try Coder.encodeInto(OutStream, out_stream, self.value); + 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 { @@ -136,25 +135,29 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime var rng = std.rand.DefaultPrng.init(0); fn testEncodeDecode(comptime T: type, base: T) !void { - var out_stream = io.AutoAllocOutStream.init(std.heap.direct_allocator); + var buffer: [1000]u8 = undefined; - try base.encodeInto(io.AutoAllocOutStream, &out_stream); - testing.expectEqual(base.encodeSize(), out_stream.toSlice().len); + var out = std.io.SliceOutStream.init(buffer[0..]); + + try base.encodeInto(&out.stream); + testing.expectEqual(base.encodeSize(), out.getWritten().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFrom(out_stream.toSlice()); + const decoded_len = try decoded.decodeFrom(out.getWritten()); testing.expectEqual(base.value, decoded.value); testing.expectEqual(base.encodeSize(), decoded_len); } fn testEncodeDecodeSlices(comptime T: type, base: T) !void { - var out_stream = io.AutoAllocOutStream.init(std.heap.direct_allocator); + var buffer: [1000]u8 = undefined; + + var out = std.io.SliceOutStream.init(buffer[0..]); - try base.encodeInto(io.AutoAllocOutStream, &out_stream); - testing.expectEqual(base.encodeSize(), out_stream.toSlice().len); + try base.encodeInto(&out.stream); + testing.expectEqual(base.encodeSize(), out.getWritten().len); var decoded: T = undefined; - const decoded_len = try decoded.decodeFromAlloc(out_stream.toSlice(), std.heap.direct_allocator); + const decoded_len = try decoded.decodeFromAlloc(out.getWritten(), std.heap.direct_allocator); testing.expectEqualSlices(u8, base.value, decoded.value); testing.expectEqual(base.encodeSize(), decoded_len); } @@ -253,8 +256,8 @@ pub fn Bytes(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.value); } - pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { - try coder.BytesCoder.encodeInto(OutStream, out_stream, self.value); + 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 { @@ -289,8 +292,8 @@ pub fn String(comptime number: u63) type { return coder.BytesCoder.encodeSize(self.value); } - pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { - try coder.BytesCoder.encodeInto(OutStream, out_stream, self.value); + 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 { @@ -331,9 +334,8 @@ pub fn Bool(comptime number: u63) type { return 1; } - pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { - const value = if (self.value) [_]u8{1} else [_]u8{0}; - try out_stream.write(value[0..]); + 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 { @@ -396,10 +398,10 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { return sum; } - pub fn encodeInto(self: Self, comptime OutStream: type, out_stream: *OutStream) !void { + pub fn encodeInto(self: Self, out_stream: var) !void { for (self.value) |item| { const wrapper = ValueType{ .value = item }; - wrapper.encodeInto(OutStream, out_stream); + wrapper.encodeInto(out_stream); } } From a2af096ee01d2b87866a51e5b67642c29222d25a Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Mon, 26 Aug 2019 17:12:28 -0500 Subject: [PATCH 9/9] Switch decodeFrom to use streaming --- src/coder.zig | 64 ++++++++++++++++++++---------------------------- src/main.zig | 27 +++++++++----------- src/types.zig | 68 ++++++++++++++++++++------------------------------- 3 files changed, 65 insertions(+), 94 deletions(-) diff --git a/src/coder.zig b/src/coder.zig index af8aa09..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; @@ -23,34 +17,33 @@ pub const Uint64Coder = struct { 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); } @@ -65,8 +58,8 @@ pub const Int64Coder = struct { 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)); } }; @@ -92,8 +85,8 @@ pub const Sint64Coder = struct { 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); } @@ -135,9 +128,8 @@ pub const Fixed64Coder = struct { 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); } }; @@ -154,9 +146,8 @@ pub const Fixed32Coder = struct { 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); } }; @@ -171,17 +162,14 @@ pub const BytesCoder = struct { 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; } }; diff --git a/src/main.zig b/src/main.zig index 7fd6d56..c684884 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,9 +22,7 @@ pub const String = types.String; pub const Repeated = types.Repeated; -pub fn encodeInto(out_stream: var, item: var) !void { - const T = @typeOf(item); - +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 => { @@ -42,24 +40,19 @@ pub fn encodeInto(out_stream: var, item: var) !void { } } -pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []const 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; - + 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; } @@ -69,6 +62,9 @@ pub fn unmarshal(comptime T: type, allocator: *std.mem.Allocator, bytes: []const }, } } + } else |err| switch (err) { + error.EndOfStream => {}, + else => return err, } inline for (@typeInfo(T).Struct.fields) |field, i| { @@ -136,9 +132,10 @@ test "end-to-end" { var buf: [1000]u8 = undefined; var out = std.io.SliceOutStream.init(buf[0..]); - try encodeInto(&out.stream, start); + try encodeInto(Example, start, &out.stream); - var result = try unmarshal(Example, std.heap.direct_allocator, out.getWritten()); + 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); diff --git a/src/types.zig b/src/types.zig index ecee403..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, @@ -29,8 +27,8 @@ pub const FieldMeta = struct { 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); } }; @@ -99,11 +97,9 @@ fn FromBitCast(comptime TargetPrimitive: type, comptime Coder: type, comptime in 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); + pub fn decodeFrom(self: *Self, in_stream: var) !void { + const raw = try Coder.decodeFrom(in_stream); self.value = @bitCast(TargetPrimitive, raw); - return len; } }; } @@ -124,11 +120,9 @@ fn FromVarintCast(comptime TargetPrimitive: type, comptime Coder: type, comptime 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); + pub fn decodeFrom(self: *Self, in_stream: var) !void { + const raw = try Coder.decodeFrom(in_stream); self.value = @intCast(TargetPrimitive, raw); - return len; } }; } @@ -142,10 +136,11 @@ fn testEncodeDecode(comptime T: type, base: T) !void { 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(out.getWritten()); + const decoded_len = try decoded.decodeFrom(&mem_in.stream); testing.expectEqual(base.value, decoded.value); - testing.expectEqual(base.encodeSize(), decoded_len); + testing.expectEqual(base.encodeSize(), mem_in.pos); } fn testEncodeDecodeSlices(comptime T: type, base: T) !void { @@ -156,10 +151,11 @@ fn testEncodeDecodeSlices(comptime T: type, base: T) !void { 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(out.getWritten(), std.heap.direct_allocator); + try decoded.decodeFromAlloc(&mem_in.stream, std.heap.direct_allocator); testing.expectEqualSlices(u8, base.value, decoded.value); - testing.expectEqual(base.encodeSize(), decoded_len); + testing.expectEqual(base.encodeSize(), mem_in.pos); } test "Var int" { @@ -260,11 +256,9 @@ pub fn Bytes(comptime number: u63) type { 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.value = 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; } }; } @@ -296,12 +290,10 @@ pub fn String(comptime number: u63) type { 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.value = try coder.BytesCoder.decode(buffer, &len, allocator); + self.value = try coder.BytesCoder.decodeFrom(in_stream, allocator); self.allocator = allocator; - return len; } }; } @@ -338,15 +330,13 @@ pub fn Bool(comptime number: u63) type { 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.value = raw != 0; - return len; } }; } @@ -405,32 +395,28 @@ pub fn Repeated(comptime number: u63, comptime Tfn: var) type { } } - 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 base.decodeFrom(in_stream); try self._decode_builder.?.append(base.value); - return len; } - 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.value) { - items_len += try self.decodeFromAlloc(raw[header_len + items_len ..], &len, allocator); + items_len += try self.decodeFromAlloc(in_stream, allocator); } 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 { @@ -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.value); }