lib/quic/src/varint.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const cursor = @import("cursor.zig");
 3 
 4 pub const EncodeError = cursor.WriteError;
 5 pub const DecodeError = cursor.ReadError;
 6 
 7 pub const Decoded = struct {
 8     value: u62,
 9     length: u4,
10 };
11 
12 pub fn encodedLength(value: u62) u4 {
13     if (value <= 63) return 1;
14     if (value <= 16_383) return 2;
15     if (value <= 1_073_741_823) return 4;
16     return 8;
17 }
18 
19 pub fn write(value: u62, output: *cursor.Write) EncodeError!u4 {
20     const length = encodedLength(value);
21     switch (length) {
22         1 => try output.byte(@intCast(value)),
23         2 => try output.int(u16, @as(u16, @intCast(value)) | 0x4000),
24         4 => try output.int(u32, @as(u32, @intCast(value)) | 0x8000_0000),
25         8 => try output.int(u64, @as(u64, value) | 0xc000_0000_0000_0000),
26         else => unreachable,
27     }
28     return length;
29 }
30 
31 pub fn encode(value: u62, out: []u8) EncodeError!u4 {
32     var output = cursor.Write.init(out);
33     return write(value, &output);
34 }
35 
36 /// Reads one varint from a read cursor and returns its value with the number of bytes it took, so a
37 /// codec takes the next varint out of a byte stream and leaves the cursor past it. The width comes
38 /// from the top two bits of the first byte. A value written in a wider form than it needs is
39 /// accepted and decoded to the same value, which RFC 9000 section 16 permits. The frame codec
40 /// rejects a non-minimal frame type. Too few bytes for the selected width give `Truncated`.
41 pub fn read(input: *cursor.Read) DecodeError!Decoded {
42     const first = try input.byte();
43     const length: u4 = @as(u4, 1) << @intCast(first >> 6);
44     const tail = try input.take(@as(usize, length) - 1);
45     var value: u64 = first & 0x3f;
46     for (0..7) |index| {
47         if (index >= tail.len) break;
48         value = (value << 8) | tail[index];
49     }
50     std.debug.assert(value <= std.math.maxInt(u62));
51     return .{ .value = @intCast(value), .length = length };
52 }
53 
54 /// Reads one varint from the front of a byte slice and returns its value with the number of bytes
55 /// it took, so a caller holding a plain byte slice calls it without building a cursor first. It
56 /// wraps the cursor form, so it accepts the same non-minimal encodings RFC 9000 section 16 permits,
57 /// and the frame codec keeps the minimality rule for frame type fields. Bytes after the varint are
58 /// left alone, and too few bytes give `Truncated`.
59 pub fn decode(bytes: []const u8) DecodeError!Decoded {
60     var input = cursor.Read.init(bytes);
61     return read(&input);
62 }
63 
64 test "RFC 9000 section 16 and Appendix A.1 varint known answers" {
65     const Vector = struct { bytes: []const u8, value: u62 };
66     const vectors = [_]Vector{
67         .{
68             .bytes = &.{ 0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c },
69             .value = 151_288_809_941_952_652,
70         },
71         .{ .bytes = &.{ 0x9d, 0x7f, 0x3e, 0x7d }, .value = 494_878_333 },
72         .{ .bytes = &.{ 0x7b, 0xbd }, .value = 15_293 },
73         .{ .bytes = &.{0x25}, .value = 37 },
74     };
75     for (vectors) |vector| {
76         const decoded = try decode(vector.bytes);
77         try std.testing.expectEqual(vector.value, decoded.value);
78         try std.testing.expectEqual(vector.bytes.len, decoded.length);
79         var encoded: [8]u8 = undefined;
80         const length = try encode(vector.value, &encoded);
81         try std.testing.expectEqualSlices(u8, vector.bytes, encoded[0..length]);
82     }
83 }
84 
85 test "RFC 9000 section 16 accepts permitted non-minimal varints" {
86     const decoded = try decode(&.{ 0x40, 0x25 });
87     try std.testing.expectEqual(@as(u62, 37), decoded.value);
88     try std.testing.expectEqual(@as(u4, 2), decoded.length);
89 }