lib/preserves/src/packed/constants.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 //! The tag bytes of the binary syntax, one per kind of value, plus the end marker. Code that writes
2 //! or checks encoded bytes by hand has to use the same bytes as the packed writer and reader.
3 const std = @import("std");
4
5 /// The tag byte that starts each encoded value, as an enum over `u8`, so code that writes encoded
6 /// bytes by hand names each tag through it. The packed reader rejects a byte outside these fourteen
7 /// values as `UnknownTag`.
8 pub const Tag = enum(u8) {
9 false_ = 0x80,
10 true_ = 0x81,
11 /// The byte 0x84, which closes a record, sequence, set or dictionary. At the start of a value,
12 /// the packed reader rejects it as `UnexpectedEndMarker`.
13 end = 0x84,
14 /// The byte 0x85, which starts an annotation in the binary syntax. The packed writer never
15 /// writes it, and the packed reader rejects it as `AnnotationNotSupported`.
16 annotation = 0x85,
17 /// The byte 0x86, which starts an embedded value. The packed writer writes it before an
18 /// embedded value's bytes, which come from its type's `encodePacked`. The packed reader rejects
19 /// it as `EmbeddedNotSupported`, so bytes holding an embedded value never decode.
20 embedded = 0x86,
21 /// The byte 0x87, which starts a double: a length of 8, then the double's eight bytes,
22 /// big-endian.
23 ieee754 = 0x87,
24 signed_integer = 0xb0,
25 string = 0xb1,
26 byte_string = 0xb2,
27 symbol = 0xb3,
28 record = 0xb4,
29 sequence = 0xb5,
30 set = 0xb6,
31 dictionary = 0xb7,
32
33 /// Returns the byte value of the tag, for code that writes a tag into a byte buffer.
34 pub fn byte(self: Tag) u8 {
35 return @backingInt(self);
36 }
37
38 /// Returns the tag whose byte is `b`, or null otherwise, so the packed reader calls it on each
39 /// tag byte for the kind of value that follows. `fromByte(t.byte())` returns `t` for every tag.
40 pub fn fromByte(b: u8) ?Tag {
41 return switch (b) {
42 0x80 => .false_,
43 0x81 => .true_,
44 0x84 => .end,
45 0x85 => .annotation,
46 0x86 => .embedded,
47 0x87 => .ieee754,
48 0xb0 => .signed_integer,
49 0xb1 => .string,
50 0xb2 => .byte_string,
51 0xb3 => .symbol,
52 0xb4 => .record,
53 0xb5 => .sequence,
54 0xb6 => .set,
55 0xb7 => .dictionary,
56 else => null,
57 };
58 }
59 };
60
61 test "Tag round-trips through byte" {
62 const all = [_]Tag{
63 .false_, .true_, .end, .annotation, .embedded, .ieee754,
64 .signed_integer, .string, .byte_string, .symbol, .record, .sequence,
65 .set, .dictionary,
66 };
67 for (all) |t| {
68 try std.testing.expectEqual(t, Tag.fromByte(t.byte()).?);
69 }
70 try std.testing.expectEqual(@as(?Tag, null), Tag.fromByte(0x00));
71 try std.testing.expectEqual(@as(?Tag, null), Tag.fromByte(0x82));
72 try std.testing.expectEqual(@as(?Tag, null), Tag.fromByte(0xb8));
73 }