~caolan/zig-netstring

b990d5f0acf423869f89f70899cf4db788a2a8df — Caolan McMahon 6 months ago 74175ad
have .next() return a reader for the next netstring instead of bool
2 files changed, 19 insertions(+), 25 deletions(-)

M README.md
M src/netstring.zig
M README.md => README.md +1 -2
@@ 21,9 21,8 @@ pub fn main() !void {
    var parser = netstring.netStringParser(input.reader());

    // Repeat while the parser can find another netstring
    while (try parser.next()) {
    while (try parser.next()) |reader| {
        // Read the current netstring and print it
        const reader = parser.reader();
        const msg = try reader.readAllAlloc(std.heap.page_allocator, 4096);
        std.debug.print("{s}\n", .{msg});
    }

M src/netstring.zig => src/netstring.zig +18 -23
@@ 14,9 14,8 @@ pub fn NetStringParser(comptime ReaderType: type) type {
        const Error = ReaderType.Error;
        const Reader = std.io.Reader(*@This(), Error, read);

        /// Aligns reader at beginning of next netstring. Returns true if
        /// another netstring was found, false if not.
        pub fn next(self: *@This()) !bool {
        /// Moves input to beginning of next netstring and returns a Reader for it.
        pub fn next(self: *@This()) !?Reader {
            var r = self.child_reader.reader();
            // move stream to end of current netstring
            if (self.child_reader.bytes_read < self.end) {


@@ 27,16 26,16 @@ pub fn NetStringParser(comptime ReaderType: type) type {
            if (size_str) |s| {
                const size = try std.fmt.parseUnsigned(usize, s, 10);
                self.end = self.child_reader.bytes_read + size + 1;
                return true;
                return self.reader();
            } else {
                return false;
                return null;
            }
        }

        /// Reads bytes from the current netstring returning number of bytes read.
        /// If the number of bytes read is 0 it means the stream reached the end
        /// of the current netstring.
        pub fn read(self: *@This(), buf: []u8) Error!usize {
        fn read(self: *@This(), buf: []u8) Error!usize {
            // read until one byte before end because of tailing comma
            if (self.child_reader.bytes_read + 1 >= self.end) {
                return 0;


@@ 51,7 50,7 @@ pub fn NetStringParser(comptime ReaderType: type) type {
        /// Returns a std.io.Reader for the current netstring. Calling `next()`
        /// on this parser will move the reader onto the next netstring. This
        /// must be done before the first netstring can be read.
        pub fn reader(self: *@This()) Reader {
        fn reader(self: *@This()) Reader {
            return .{ .context = self };
        }
    };


@@ 66,77 65,73 @@ test "parse empty input stream" {
    var p = netStringParser(input.reader());
    var msg: [1024]u8 = undefined;
    try testing.expectEqual(@intCast(usize, 0), try p.reader().read(&msg));
    try testing.expectEqual(false, try p.next());
    try testing.expectEqual(try p.next(), null);
}

test "parse single netstring" {
    var input = std.io.fixedBufferStream("5:hello,");
    var p = netStringParser(input.reader());
    var msg: [1024]u8 = undefined;
    const r = p.reader();
    try testing.expectEqual(true, try p.next());
    const r = (try p.next()).?;
    const bytes = try r.read(&msg);
    try testing.expectEqual(@intCast(usize, 5), bytes);
    try testing.expectEqualSlices(u8, "hello", msg[0..bytes]);
    try testing.expectEqual(@intCast(usize, 0), try r.read(&msg));
    try testing.expectEqual(false, try p.next());
    try testing.expectEqual(try p.next(), null);
}

test "parse multiple netstrings" {
    var input = std.io.fixedBufferStream("5:hello,1:,,7: world!,");
    var p = netStringParser(input.reader());
    var msg: [1024]u8 = undefined;
    const r = p.reader();
    try testing.expectEqual(true, try p.next());
    {
        const r = (try p.next()).?;
        const bytes = try r.read(&msg);
        try testing.expectEqual(@intCast(usize, 5), bytes);
        try testing.expectEqualSlices(u8, "hello", msg[0..bytes]);
        try testing.expectEqual(@intCast(usize, 0), try r.read(&msg));
    }
    try testing.expectEqual(true, try p.next());
    {
        const r = (try p.next()).?;
        const bytes = try r.read(&msg);
        try testing.expectEqual(@intCast(usize, 1), bytes);
        try testing.expectEqualSlices(u8, ",", msg[0..bytes]);
        try testing.expectEqual(@intCast(usize, 0), try r.read(&msg));
    }
    try testing.expectEqual(true, try p.next());
    {
        const r = (try p.next()).?;
        const bytes = try r.read(&msg);
        try testing.expectEqual(@intCast(usize, 7), bytes);
        try testing.expectEqualSlices(u8, " world!", msg[0..bytes]);
        try testing.expectEqual(@intCast(usize, 0), try r.read(&msg));
    }
    try testing.expectEqual(false, try p.next());
    try testing.expectEqual(try p.next(), null);
}

test "calling next before completely reading current netstring" {
    var input = std.io.fixedBufferStream("3:foo,3:bar,");
    var p = netStringParser(input.reader());
    var msg: [1]u8 = undefined;
    const r = p.reader();
    try testing.expectEqual(true, try p.next());
    {
        const r = (try p.next()).?;
        const bytes = try r.read(&msg);
        try testing.expectEqual(@intCast(usize, 1), bytes);
        try testing.expectEqualSlices(u8, "f", msg[0..bytes]);
    }
    try testing.expectEqual(true, try p.next());
    {
        const r = (try p.next()).?;
        const bytes = try r.read(&msg);
        try testing.expectEqual(@intCast(usize, 1), bytes);
        try testing.expectEqualSlices(u8, "b", msg[0..bytes]);
        // attempt to keep reading past current netstring
        try testing.expectEqual(try p.next(), null);
        try testing.expectEqual(@intCast(usize, 0), try r.read(&msg));
    }
    try testing.expectEqual(false, try p.next());
    try testing.expectEqual(@intCast(usize, 0), try r.read(&msg));
}

test "invalid input" {
    var input = std.io.fixedBufferStream("hello");
    var p = netStringParser(input.reader());
    var msg: [1024]u8 = undefined;
    const r = p.reader();
    try testing.expectError(error.InvalidCharacter, p.next());
    try testing.expectEqual(@intCast(usize, 0), try r.read(&msg));
}