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 }