summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2024-09-19 06:25:42 +0200
committerMitja Felicijan <mitja.felicijan@gmail.com>2024-09-19 06:25:42 +0200
commit7ceb2589d901a13b8a65df134b5325ef4c6fd6a9 (patch)
treee78dddd38460fee2905c205389ffad986e42a554
parent865ee53ab70b06a5f8e03b7cb1433e6b9e97cd35 (diff)
downloadprobe-7ceb2589d901a13b8a65df134b5325ef4c6fd6a9.tar.gz
Added TLV encoding example in Zig
-rw-r--r--README.md33
-rw-r--r--zig-tlv-encoding/Makefile2
-rw-r--r--zig-tlv-encoding/main.zig97
3 files changed, 116 insertions, 16 deletions
diff --git a/README.md b/README.md
index be5ffcf..490931c 100644
--- a/README.md
+++ b/README.md
@@ -16,22 +16,23 @@ running it.
> also written in the most explicit way and lacks massively when it
> comes to checking of errors etc.
-| Example | Compiler | What does it do? |
-|----------------------------------|------------|----------------------------------------------------------------|
-| [c-asm](./c-asm) | clang-17 | Calls a function written in ASM from C code. |
-| [c-embed](./c-embed) | clang-17 | Embedding external resources in compiled binary. |
-| [c-signals](./c-signals) | clang-17 | Uses SIGUSR1 and SIGUSR2 as IPC mechanism. |
-| [c-structs](./c-structs) | clang-17 | Saves and reads structs in/from binary files. |
-| [zig-c-interop](./zig-c-interop) | zig-0.11.0 | Uses functions written in C from Zig code. |
-| [zig-ppm](./zig-ppm) | zig-0.11.0 | Creates an image with random pixels in PPM image format. |
-| [zig-structs](./zig-structs) | zig-0.11.0 | Serialization of a struct into JSON and then reading it back. |
-| [zig-telnet](./zig-telnet) | zig-0.11.0 | Connects to Redis server like it is a basic telnet server. |
-| [zig-x11](./zig-x11) | zig-0.11.0 | Uses X11 to create a basic window without any bindings needed. |
-| [zig-http](./zig-http) | zig-0.11.0 | Basic example of a HTTP 1.1 server without any routing etc. |
-| [zig-x11-box](./zig-x11-box) | zig-0.11.0 | Move a box around with arrow keys with Xlib and Zig. |
-| [zig-kv-store](./zig-kv-store) | zig-0.11.0 | Simple Key-value store that mimics memcached written in Zig. |
-| [zig-wad](./zig-wad) | zig-0.11.0 | Reads doom.wad and extracts the identification header. |
-| [zig-os-props](./zig-os-props) | zig-0.11.0 | Detects properties of the target operating system. |
+| Example | Compiler | What does it do? |
+|----------------------------------------|------------|----------------------------------------------------------------|
+| [c-asm](./c-asm) | clang-17 | Calls a function written in ASM from C code. |
+| [c-embed](./c-embed) | clang-17 | Embedding external resources in compiled binary. |
+| [c-signals](./c-signals) | clang-17 | Uses SIGUSR1 and SIGUSR2 as IPC mechanism. |
+| [c-structs](./c-structs) | clang-17 | Saves and reads structs in/from binary files. |
+| [zig-c-interop](./zig-c-interop) | zig-0.11.0 | Uses functions written in C from Zig code. |
+| [zig-ppm](./zig-ppm) | zig-0.11.0 | Creates an image with random pixels in PPM image format. |
+| [zig-structs](./zig-structs) | zig-0.11.0 | Serialization of a struct into JSON and then reading it back. |
+| [zig-telnet](./zig-telnet) | zig-0.11.0 | Connects to Redis server like it is a basic telnet server. |
+| [zig-x11](./zig-x11) | zig-0.11.0 | Uses X11 to create a basic window without any bindings needed. |
+| [zig-http](./zig-http) | zig-0.11.0 | Basic example of a HTTP 1.1 server without any routing etc. |
+| [zig-x11-box](./zig-x11-box) | zig-0.11.0 | Move a box around with arrow keys with Xlib and Zig. |
+| [zig-kv-store](./zig-kv-store) | zig-0.11.0 | Simple Key-value store that mimics memcached written in Zig. |
+| [zig-wad](./zig-wad) | zig-0.11.0 | Reads doom.wad and extracts the identification header. |
+| [zig-os-props](./zig-os-props) | zig-0.11.0 | Detects properties of the target operating system. |
+| [zig-tlv-encoding](./zig-tlv-encoding) | zig-0.13.0 | Naive implementation of TLV encoding in Zig. |
## License
diff --git a/zig-tlv-encoding/Makefile b/zig-tlv-encoding/Makefile
new file mode 100644
index 0000000..98beea6
--- /dev/null
+++ b/zig-tlv-encoding/Makefile
@@ -0,0 +1,2 @@
+default:
+ zig test main.zig
diff --git a/zig-tlv-encoding/main.zig b/zig-tlv-encoding/main.zig
new file mode 100644
index 0000000..d7e2809
--- /dev/null
+++ b/zig-tlv-encoding/main.zig
@@ -0,0 +1,97 @@
+// TLV format: https://devopedia.org/tlv-format
+// Real world example: https://www.rfc-editor.org/rfc/rfc8609
+// Cool article: https://www.huy.rocks/everyday/12-11-2022-zig-using-zig-for-advent-of-code
+
+// Dummy TLV spec for encoding Linux devicei information
+//
+// Packet specification
+// 1byte ... Tag
+// 2bytes ... Length
+// unknown ... Value
+//
+// Available tags: { 0x00 = vendor_name, 0x01 = model_name }
+// Packet presented as a struct: Packet{ .vendor_name, .model_name }
+// This assumes that the max value length can be 255.
+//
+// Test data from `lsusb` (`lscpu`):
+// Bus 002 Device 003: ID 046d:086b Logitech, Inc. BRIO 4K Stream Edition
+// Bus 001 Device 009: ID 3434:0350 Keychron Keychron V5
+// Bus 001 Device 006: ID 2433:b200 ASETEK [NZXT Kraken X60]
+// Bus 001 Device 005: ID 046d:c548 Logitech, Inc. Logi Bolt Receiver
+
+const std = @import("std");
+
+const TLVTag = enum(u8) {
+ VENDOR_NAME = 0x00,
+ MODEL_NAME = 0x01,
+};
+
+const TLVPacket = struct {
+ tag: TLVTag,
+ length: u8,
+ value: std.ArrayList(u8),
+
+ pub fn printEncoded(self: *TLVPacket) void {
+ std.debug.print("\t", .{});
+ std.debug.print("{X:0>2}-", .{@intFromEnum(self.tag)});
+ std.debug.print("{X:0>2}-", .{self.length});
+
+ for (self.value.items) |val| {
+ std.debug.print("{X:0>2}", .{val});
+ }
+
+ std.debug.print("\n", .{});
+ }
+};
+
+const TLVPayload = struct {
+ packets: std.ArrayList(TLVPacket),
+ allocator: std.mem.Allocator,
+
+ pub fn init(allocator: std.mem.Allocator) TLVPayload {
+ return TLVPayload{
+ .packets = std.ArrayList(TLVPacket).init(allocator),
+ .allocator = allocator,
+ };
+ }
+
+ pub fn deinit(self: *TLVPayload) void {
+ for (self.packets.items) |*packet| {
+ packet.value.deinit();
+ }
+ self.packets.deinit();
+ }
+
+ pub fn append(self: *TLVPayload, tag: TLVTag, message: []const u8) !void {
+ var value = try std.ArrayList(u8).initCapacity(self.allocator, message.len);
+ try value.appendSlice(message);
+
+ try self.packets.append(.{
+ .tag = tag,
+ .length = @intCast(message.len),
+ .value = value,
+ });
+ }
+};
+
+test "Create TLV payload manually" {
+ var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ defer _ = gpa.deinit();
+
+ var payload = TLVPayload.init(gpa.allocator());
+ defer payload.deinit();
+
+ try payload.append(.VENDOR_NAME, "Logitech, Inc.");
+ try payload.append(.MODEL_NAME, "BRIO 4K Stream Edition");
+
+ std.debug.print("Capacity: {d}\n", .{payload.packets.capacity});
+
+ for (payload.packets.items) |*packet| {
+ std.debug.print("Tag: {}, Length: {d}\n", .{ packet.tag, packet.length });
+ packet.printEncoded();
+ }
+}
+
+pub fn main() void {
+ std.debug.print("Use `make test` or `zig test main.zig` instead.\n", .{});
+}