lib/quic/src/connection/crypto.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const Reassembler = struct {
4 bytes: []u8,
5 present: []u8,
6 next_offset: u62 = 0,
7
8 pub const ReceiveError = error{ BufferExceeded, ConflictingData };
9
10 pub fn init(bytes: []u8, present: []u8) Reassembler {
11 std.debug.assert(bytes.len == present.len);
12 @memset(bytes, 0);
13 @memset(present, 0);
14 return .{ .bytes = bytes, .present = present };
15 }
16
17 pub fn receive(
18 self: *Reassembler,
19 offset: u62,
20 data: []const u8,
21 ) ReceiveError!void {
22 const start = std.math.cast(usize, offset) orelse return error.BufferExceeded;
23 if (start > self.bytes.len) return error.BufferExceeded;
24 if (data.len > self.bytes.len - start) return error.BufferExceeded;
25 for (data, 0..) |byte, index| {
26 const target = start + index;
27 if (self.present[target] != 0 and self.bytes[target] != byte) {
28 return error.ConflictingData;
29 }
30 self.bytes[target] = byte;
31 self.present[target] = 1;
32 }
33 }
34
35 pub fn contiguous(self: *const Reassembler) []const u8 {
36 const start = std.math.cast(usize, self.next_offset) orelse return &.{};
37 if (start >= self.bytes.len) return &.{};
38 var end = start;
39 for (start..self.bytes.len) |index| {
40 if (self.present[index] == 0) break;
41 end += 1;
42 }
43 return self.bytes[start..end];
44 }
45
46 pub fn consume(self: *Reassembler, length: usize) void {
47 const available = self.contiguous();
48 std.debug.assert(length <= available.len);
49 self.next_offset += @intCast(length);
50 }
51 };
52
53 test "RFC 9000 section 19.6 CRYPTO reassembly tolerates bounded gaps" {
54 var bytes: [8]u8 = undefined;
55 var present: [8]u8 = undefined;
56 var stream = Reassembler.init(&bytes, &present);
57 try stream.receive(4, "ef");
58 try std.testing.expectEqual(@as(usize, 0), stream.contiguous().len);
59 try stream.receive(0, "abcd");
60 try std.testing.expectEqualStrings("abcdef", stream.contiguous());
61 stream.consume(6);
62 try std.testing.expectEqual(@as(u62, 6), stream.next_offset);
63 }
64
65 test "RFC 9000 section 19.6 CRYPTO reassembly maximum and maximum plus one" {
66 var bytes: [4]u8 = undefined;
67 var present: [4]u8 = undefined;
68 var stream = Reassembler.init(&bytes, &present);
69 try stream.receive(0, "full");
70 try std.testing.expectError(error.BufferExceeded, stream.receive(4, "x"));
71 }