state: State,
chunk_len: u64,
pub const init: ChunkParser = .{
.state = .head_size,
.chunk_len = 0,
};
pub const State = enum {
head_size,
head_ext,
head_r,
data,
data_suffix,
data_suffix_r,
invalid,
};
pub fn feed(p: *ChunkParser, bytes: []const u8) usize {
for (bytes, 0..) |c, i| switch (p.state) {
.data_suffix => switch (c) {
'\r' => p.state = .data_suffix_r,
'\n' => p.state = .head_size,
else => {
p.state = .invalid;
return i;
},
},
.data_suffix_r => switch (c) {
'\n' => p.state = .head_size,
else => {
p.state = .invalid;
return i;
},
},
.head_size => {
const digit = switch (c) {
'0'...'9' => |b| b - '0',
'A'...'Z' => |b| b - 'A' + 10,
'a'...'z' => |b| b - 'a' + 10,
'\r' => {
p.state = .head_r;
continue;
},
'\n' => {
p.state = .data;
return i + 1;
},
else => {
p.state = .head_ext;
continue;
},
};
const new_len = p.chunk_len *% 16 +% digit;
if (new_len <= p.chunk_len and p.chunk_len != 0) {
p.state = .invalid;
return i;
}
p.chunk_len = new_len;
},
.head_ext => switch (c) {
'\r' => p.state = .head_r,
'\n' => {
p.state = .data;
return i + 1;
},
else => continue,
},
.head_r => switch (c) {
'\n' => {
p.state = .data;
return i + 1;
},
else => {
p.state = .invalid;
return i;
},
},
.data => unreachable,
.invalid => unreachable,
};
return bytes.len;
}
const ChunkParser = @This();
const std = @import("std");
test feed {
const testing = std.testing;
const data = "Ff\r\nf0f000 ; ext\n0\r\nffffffffffffffffffffffffffffffffffffffff\r\n";
var p = init;
const first = p.feed(data[0..]);
try testing.expectEqual(@as(u32, 4), first);
try testing.expectEqual(@as(u64, 0xff), p.chunk_len);
try testing.expectEqual(.data, p.state);
p = init;
const second = p.feed(data[first..]);
try testing.expectEqual(@as(u32, 13), second);
try testing.expectEqual(@as(u64, 0xf0f000), p.chunk_len);
try testing.expectEqual(.data, p.state);
p = init;
const third = p.feed(data[first + second ..]);
try testing.expectEqual(@as(u32, 3), third);
try testing.expectEqual(@as(u64, 0), p.chunk_len);
try testing.expectEqual(.data, p.state);
p = init;
const fourth = p.feed(data[first + second + third ..]);
try testing.expectEqual(@as(u32, 16), fourth);
try testing.expectEqual(@as(u64, 0xffffffffffffffff), p.chunk_len);
try testing.expectEqual(.invalid, p.state);
}