struct ChunkParser [src]
Alias for std.http.ChunkParser
Parser for transfer-encoding: chunked.
Fields
state: State
chunk_len: u64
Members
Source
//! Parser for transfer-encoding: chunked.
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,
};
/// Returns the number of bytes consumed by the chunk size. This is always
/// less than or equal to `bytes.len`.
///
/// After this function returns, `chunk_len` will contain the parsed chunk size
/// in bytes when `state` is `data`. Alternately, `state` may become `invalid`,
/// indicating a syntax error in the input stream.
///
/// If the amount returned is less than `bytes.len`, the parser is in the
/// `chunk_data` state and the first byte of the chunk is at `bytes[result]`.
///
/// Asserts `state` is neither `data` nor `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);
}