lib/reticulum/src/wire/flags.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const HeaderType = enum(u1) {
4 one = 0,
5 two = 1,
6 };
7
8 pub const TransportType = enum(u1) {
9 broadcast = 0,
10 transport = 1,
11 };
12
13 pub const DestinationType = enum(u2) {
14 single = 0,
15 group = 1,
16 plain = 2,
17 link = 3,
18 };
19
20 pub const PacketType = enum(u2) {
21 data = 0,
22 announce = 1,
23 link_request = 2,
24 proof = 3,
25 };
26
27 pub const Flags = struct {
28 ifac: u1,
29 header: HeaderType,
30 context: u1,
31 transport: TransportType,
32 destination: DestinationType,
33 packet: PacketType,
34
35 /// Packs the six flag fields into the byte that leads a packet header: the
36 /// access-code flag at bit 7, the header shape at bit 6, the context flag
37 /// at bit 5, whether the packet is carried for another node at bit 4, the
38 /// destination type at bits 3 and 2, and the packet type at bits 1 and 0,
39 /// for a caller building that header, following Reticulum@1.5.0
40 /// RNS/Packet.py:172-174.
41 pub fn encode(self: Flags) u8 {
42 return (@as(u8, self.ifac) << 7) |
43 (@as(u8, @backingInt(self.header)) << 6) |
44 (@as(u8, self.context) << 5) |
45 (@as(u8, @backingInt(self.transport)) << 4) |
46 (@as(u8, @backingInt(self.destination)) << 2) |
47 @as(u8, @backingInt(self.packet));
48 }
49
50 /// Reads the six flag fields back out of the first byte of an arriving
51 /// packet, at the bit positions the packing uses, following Reticulum@1.5.0
52 /// RNS/Packet.py:251-255. Every bit pattern names a value in its field, so
53 /// any byte decodes and encodes back to itself.
54 pub fn decode(byte: u8) Flags {
55 return .{
56 .ifac = @truncate(byte >> 7),
57 .header = @fromBackingInt(@intCast(@as(u1, @truncate(byte >> 6)))),
58 .context = @truncate(byte >> 5),
59 .transport = @fromBackingInt(@intCast(@as(u1, @truncate(byte >> 4)))),
60 .destination = @fromBackingInt(@intCast(@as(u2, @truncate(byte >> 2)))),
61 .packet = @fromBackingInt(@intCast(@as(u2, @truncate(byte)))),
62 };
63 }
64 };
65
66 test "Reticulum@1.5.0 RNS/Packet.py:172-174 packs each header type" {
67 const base = Flags{
68 .ifac = 0,
69 .header = .one,
70 .context = 0,
71 .transport = .broadcast,
72 .destination = .single,
73 .packet = .data,
74 };
75 try std.testing.expectEqual(@as(u8, 0x00), base.encode());
76 var header_two = base;
77 header_two.header = .two;
78 try std.testing.expectEqual(@as(u8, 0x40), header_two.encode());
79 }
80
81 test "Reticulum@1.5.0 RNS/Packet.py:172-174 packs each destination type" {
82 var value = Flags{
83 .ifac = 0,
84 .header = .one,
85 .context = 0,
86 .transport = .broadcast,
87 .destination = .single,
88 .packet = .data,
89 };
90 for ([_]u8{ 0x00, 0x04, 0x08, 0x0c }, 0..) |expected, raw| {
91 value.destination = @fromBackingInt(@intCast(@as(u2, @intCast(raw))));
92 try std.testing.expectEqual(expected, value.encode());
93 }
94 }
95
96 test "Reticulum@1.5.0 RNS/Packet.py:60-63,172 packs each packet type" {
97 var value = Flags{
98 .ifac = 0,
99 .header = .one,
100 .context = 0,
101 .transport = .broadcast,
102 .destination = .single,
103 .packet = .data,
104 };
105 for ([_]u8{ 0x00, 0x01, 0x02, 0x03 }, 0..) |expected, raw| {
106 value.packet = @fromBackingInt(@intCast(@as(u2, @intCast(raw))));
107 try std.testing.expectEqual(expected, value.encode());
108 }
109 }
110
111 test "Reticulum@1.5.0 RNS/Packet.py:251-255 preserves every flag bit" {
112 const decoded = Flags.decode(0xff);
113 try std.testing.expectEqual(@as(u1, 1), decoded.ifac);
114 try std.testing.expectEqual(HeaderType.two, decoded.header);
115 try std.testing.expectEqual(@as(u1, 1), decoded.context);
116 try std.testing.expectEqual(TransportType.transport, decoded.transport);
117 try std.testing.expectEqual(DestinationType.link, decoded.destination);
118 try std.testing.expectEqual(PacketType.proof, decoded.packet);
119 try std.testing.expectEqual(@as(u8, 0xff), decoded.encode());
120 }