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 }