Added TLV encoding example in Zig

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2024-09-19 06:25:42 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2024-09-19 06:25:42 +0200
Commit 7ceb2589d901a13b8a65df134b5325ef4c6fd6a9 (patch)
-rw-r--r-- README.md 33
-rw-r--r-- zig-tlv-encoding/Makefile 2
-rw-r--r-- zig-tlv-encoding/main.zig 97
3 files changed, 116 insertions, 16 deletions
diff --git a/README.md b/README.md
...
16
> also written in the most explicit way and lacks massively when it
16
> also written in the most explicit way and lacks massively when it
17
> comes to checking of errors etc.
17
> comes to checking of errors etc.
18
  
18
  
19
| Example                          | Compiler   | What does it do?                                               |
19
| Example                                | Compiler   | What does it do?                                               |
20
|----------------------------------|------------|----------------------------------------------------------------|
20
|----------------------------------------|------------|----------------------------------------------------------------|
21
| [c-asm](./c-asm)                 | clang-17   | Calls a function written in ASM from C code.                   |
21
| [c-asm](./c-asm)                       | clang-17   | Calls a function written in ASM from C code.                   |
22
| [c-embed](./c-embed)             | clang-17   | Embedding external resources in compiled binary.               |
22
| [c-embed](./c-embed)                   | clang-17   | Embedding external resources in compiled binary.               |
23
| [c-signals](./c-signals)         | clang-17   | Uses SIGUSR1 and SIGUSR2 as IPC mechanism.                     |
23
| [c-signals](./c-signals)               | clang-17   | Uses SIGUSR1 and SIGUSR2 as IPC mechanism.                     |
24
| [c-structs](./c-structs)         | clang-17   | Saves and reads structs in/from binary files.                  |
24
| [c-structs](./c-structs)               | clang-17   | Saves and reads structs in/from binary files.                  |
25
| [zig-c-interop](./zig-c-interop) | zig-0.11.0 | Uses functions written in C from Zig code.                     |
25
| [zig-c-interop](./zig-c-interop)       | zig-0.11.0 | Uses functions written in C from Zig code.                     |
26
| [zig-ppm](./zig-ppm)             | zig-0.11.0 | Creates an image with random pixels in PPM image format.       |
26
| [zig-ppm](./zig-ppm)                   | zig-0.11.0 | Creates an image with random pixels in PPM image format.       |
27
| [zig-structs](./zig-structs)     | zig-0.11.0 | Serialization of a struct into JSON and then reading it back.  |
27
| [zig-structs](./zig-structs)           | zig-0.11.0 | Serialization of a struct into JSON and then reading it back.  |
28
| [zig-telnet](./zig-telnet)       | zig-0.11.0 | Connects to Redis server like it is a basic telnet server.     |
28
| [zig-telnet](./zig-telnet)             | zig-0.11.0 | Connects to Redis server like it is a basic telnet server.     |
29
| [zig-x11](./zig-x11)             | zig-0.11.0 | Uses X11 to create a basic window without any bindings needed. |
29
| [zig-x11](./zig-x11)                   | zig-0.11.0 | Uses X11 to create a basic window without any bindings needed. |
30
| [zig-http](./zig-http)           | zig-0.11.0 | Basic example of a HTTP 1.1 server without any routing etc.    |
30
| [zig-http](./zig-http)                 | zig-0.11.0 | Basic example of a HTTP 1.1 server without any routing etc.    |
31
| [zig-x11-box](./zig-x11-box)     | zig-0.11.0 | Move a box around with arrow keys with Xlib and Zig.           |
31
| [zig-x11-box](./zig-x11-box)           | zig-0.11.0 | Move a box around with arrow keys with Xlib and Zig.           |
32
| [zig-kv-store](./zig-kv-store)   | zig-0.11.0 | Simple Key-value store that mimics memcached written in Zig.   |
32
| [zig-kv-store](./zig-kv-store)         | zig-0.11.0 | Simple Key-value store that mimics memcached written in Zig.   |
33
| [zig-wad](./zig-wad)             | zig-0.11.0 | Reads doom.wad and extracts the identification header.         |
33
| [zig-wad](./zig-wad)                   | zig-0.11.0 | Reads doom.wad and extracts the identification header.         |
34
| [zig-os-props](./zig-os-props)   | zig-0.11.0 | Detects properties of the target operating system.             |
34
| [zig-os-props](./zig-os-props)         | zig-0.11.0 | Detects properties of the target operating system.             |
  
35
| [zig-tlv-encoding](./zig-tlv-encoding) | zig-0.13.0 | Naive implementation of TLV encoding in Zig.                   |
35
  
36
  
36
## License
37
## License
37
  
38
  
...
diff --git a/zig-tlv-encoding/Makefile b/zig-tlv-encoding/Makefile
  
1
default:
  
2
	zig test main.zig
diff --git a/zig-tlv-encoding/main.zig b/zig-tlv-encoding/main.zig
  
1
// TLV format: https://devopedia.org/tlv-format
  
2
// Real world example: https://www.rfc-editor.org/rfc/rfc8609
  
3
// Cool article: https://www.huy.rocks/everyday/12-11-2022-zig-using-zig-for-advent-of-code
  
4
  
  
5
// Dummy TLV spec for encoding Linux devicei information
  
6
//
  
7
// Packet specification
  
8
//   1byte    ... Tag
  
9
//   2bytes   ... Length
  
10
//   unknown  ... Value
  
11
//
  
12
// Available tags: { 0x00 = vendor_name, 0x01 = model_name }
  
13
// Packet presented as a struct: Packet{ .vendor_name, .model_name }
  
14
// This assumes that the max value length can be 255.
  
15
//
  
16
// Test data from `lsusb` (`lscpu`):
  
17
// Bus 002 Device 003: ID 046d:086b Logitech, Inc. BRIO 4K Stream Edition
  
18
// Bus 001 Device 009: ID 3434:0350 Keychron Keychron V5
  
19
// Bus 001 Device 006: ID 2433:b200 ASETEK [NZXT Kraken X60]
  
20
// Bus 001 Device 005: ID 046d:c548 Logitech, Inc. Logi Bolt Receiver
  
21
  
  
22
const std = @import("std");
  
23
  
  
24
const TLVTag = enum(u8) {
  
25
    VENDOR_NAME = 0x00,
  
26
    MODEL_NAME = 0x01,
  
27
};
  
28
  
  
29
const TLVPacket = struct {
  
30
    tag: TLVTag,
  
31
    length: u8,
  
32
    value: std.ArrayList(u8),
  
33
  
  
34
    pub fn printEncoded(self: *TLVPacket) void {
  
35
        std.debug.print("\t", .{});
  
36
        std.debug.print("{X:0>2}-", .{@intFromEnum(self.tag)});
  
37
        std.debug.print("{X:0>2}-", .{self.length});
  
38
  
  
39
        for (self.value.items) |val| {
  
40
            std.debug.print("{X:0>2}", .{val});
  
41
        }
  
42
  
  
43
        std.debug.print("\n", .{});
  
44
    }
  
45
};
  
46
  
  
47
const TLVPayload = struct {
  
48
    packets: std.ArrayList(TLVPacket),
  
49
    allocator: std.mem.Allocator,
  
50
  
  
51
    pub fn init(allocator: std.mem.Allocator) TLVPayload {
  
52
        return TLVPayload{
  
53
            .packets = std.ArrayList(TLVPacket).init(allocator),
  
54
            .allocator = allocator,
  
55
        };
  
56
    }
  
57
  
  
58
    pub fn deinit(self: *TLVPayload) void {
  
59
        for (self.packets.items) |*packet| {
  
60
            packet.value.deinit();
  
61
        }
  
62
        self.packets.deinit();
  
63
    }
  
64
  
  
65
    pub fn append(self: *TLVPayload, tag: TLVTag, message: []const u8) !void {
  
66
        var value = try std.ArrayList(u8).initCapacity(self.allocator, message.len);
  
67
        try value.appendSlice(message);
  
68
  
  
69
        try self.packets.append(.{
  
70
            .tag = tag,
  
71
            .length = @intCast(message.len),
  
72
            .value = value,
  
73
        });
  
74
    }
  
75
};
  
76
  
  
77
test "Create TLV payload manually" {
  
78
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
  
79
    defer _ = gpa.deinit();
  
80
  
  
81
    var payload = TLVPayload.init(gpa.allocator());
  
82
    defer payload.deinit();
  
83
  
  
84
    try payload.append(.VENDOR_NAME, "Logitech, Inc.");
  
85
    try payload.append(.MODEL_NAME, "BRIO 4K Stream Edition");
  
86
  
  
87
    std.debug.print("Capacity: {d}\n", .{payload.packets.capacity});
  
88
  
  
89
    for (payload.packets.items) |*packet| {
  
90
        std.debug.print("Tag: {}, Length: {d}\n", .{ packet.tag, packet.length });
  
91
        packet.printEncoded();
  
92
    }
  
93
}
  
94
  
  
95
pub fn main() void {
  
96
    std.debug.print("Use `make test` or `zig test main.zig` instead.\n", .{});
  
97
}