lib/quic/src/connection/stream/id.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 
 3 /// The number that names one QUIC stream serves as the type for every stream argument in the public
 4 /// API. The value is sixty-two bits wide and unsigned.
 5 pub const StreamId = u62;
 6 
 7 /// Which of the two endpoints opened a stream. `admitStream` compares it against this endpoint's
 8 /// own role to decide whether an arriving frame names a stream this endpoint opened.
 9 pub const Initiator = enum { client, server };
10 
11 /// Whether one endpoint or both may send on a stream. This connection carries one bidirectional
12 /// stream, so a unidirectional identifier closes the connection.
13 pub const Direction = enum { bidirectional, unidirectional };
14 
15 /// Reads the opener out of the identifier's lowest bit, so `admitStream` calls this function first
16 /// because a frame for a stream this endpoint should have opened itself is a state error. A zero
17 /// there names the client and a one names the server.
18 pub fn initiator(id: StreamId) Initiator {
19     return if (id & 0x01 == 0) .client else .server;
20 }
21 
22 /// Reads out of the identifier's second bit whether both sides may send, so `admitStream` rejects a
23 /// unidirectional stream outright. A zero there names a stream both sides send on, and a one names
24 /// a stream only its opener sends on.
25 pub fn direction(id: StreamId) Direction {
26     return if (id & 0x02 == 0) .bidirectional else .unidirectional;
27 }
28 
29 /// Counts the streams of the same opener and kind that come before this one, so `admitStream`
30 /// compares the sequence against the advertised stream limit. The sequence is the identifier
31 /// shifted past its two lowest bits.
32 pub fn sequence(id: StreamId) u60 {
33     return @intCast(id >> 2);
34 }
35 
36 test "RFC 9000 section 2.1 stream ID low bits name initiator and direction" {
37     const Case = struct { id: StreamId, from: Initiator, way: Direction, sequence: u60 };
38     const cases = [_]Case{
39         .{ .id = 0, .from = .client, .way = .bidirectional, .sequence = 0 },
40         .{ .id = 1, .from = .server, .way = .bidirectional, .sequence = 0 },
41         .{ .id = 2, .from = .client, .way = .unidirectional, .sequence = 0 },
42         .{ .id = 3, .from = .server, .way = .unidirectional, .sequence = 0 },
43         .{ .id = 4, .from = .client, .way = .bidirectional, .sequence = 1 },
44         .{
45             .id = std.math.maxInt(u62),
46             .from = .server,
47             .way = .unidirectional,
48             .sequence = std.math.maxInt(u60),
49         },
50     };
51     for (cases) |case| {
52         try std.testing.expectEqual(case.from, initiator(case.id));
53         try std.testing.expectEqual(case.way, direction(case.id));
54         try std.testing.expectEqual(case.sequence, sequence(case.id));
55     }
56 }