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 }