lib/wayland/src/wire.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const builtin = @import("builtin");
3
4 pub const header_size = 8;
5 pub const maximum_message_size = std.math.maxInt(u16) & ~@as(u16, 3);
6
7 pub const Error = error{
8 IncompleteHeader,
9 InvalidObject,
10 InvalidSize,
11 MessageTooLarge,
12 };
13
14 pub const Header = struct {
15 object_id: u32,
16 opcode: u16,
17 size: u16,
18
19 pub fn init(object_id: u32, opcode: u16, payload_size: usize) Error!Header {
20 if (object_id == 0) return error.InvalidObject;
21 if (payload_size % 4 != 0) return error.InvalidSize;
22 if (payload_size > maximum_message_size - header_size) return error.MessageTooLarge;
23 const size = header_size + payload_size;
24 return .{
25 .object_id = object_id,
26 .opcode = opcode,
27 .size = @intCast(size),
28 };
29 }
30
31 pub fn encode(self: Header, destination: *[header_size]u8) Error!void {
32 try self.validate();
33 const second = @as(u32, self.size) << 16 | self.opcode;
34 std.mem.writeInt(u32, destination[0..4], self.object_id, builtin.cpu.arch.endian());
35 std.mem.writeInt(u32, destination[4..8], second, builtin.cpu.arch.endian());
36 }
37
38 pub fn validate(self: Header) Error!void {
39 if (self.object_id == 0) return error.InvalidObject;
40 if (self.size < header_size or self.size % 4 != 0) return error.InvalidSize;
41 }
42 };
43
44 pub fn decode(bytes: []const u8) Error!Header {
45 if (bytes.len < header_size) return error.IncompleteHeader;
46 const second = std.mem.readInt(u32, bytes[4..8], builtin.cpu.arch.endian());
47 const header: Header = .{
48 .object_id = std.mem.readInt(u32, bytes[0..4], builtin.cpu.arch.endian()),
49 .opcode = @truncate(second),
50 .size = @intCast(second >> 16),
51 };
52 try header.validate();
53 return header;
54 }
55
56 test "Wayland header round trips in host byte order" {
57 const expected: Header = .{ .object_id = 0x1234, .opcode = 7, .size = 20 };
58 var encoded: [header_size]u8 = undefined;
59 try expected.encode(&encoded);
60 try std.testing.expectEqual(expected, try decode(&encoded));
61 }
62
63 test "Wayland header rejects invalid object and size fields" {
64 try std.testing.expectError(error.InvalidObject, Header.init(0, 0, 0));
65 try std.testing.expectError(error.InvalidSize, Header.init(1, 0, 2));
66 try std.testing.expectError(error.MessageTooLarge, Header.init(1, 0, maximum_message_size));
67
68 var encoded: [header_size]u8 = @splat(0);
69 std.mem.writeInt(u32, encoded[0..4], 1, builtin.cpu.arch.endian());
70 std.mem.writeInt(u32, encoded[4..8], @as(u32, 6) << 16, builtin.cpu.arch.endian());
71 try std.testing.expectError(error.InvalidSize, decode(&encoded));
72 }