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 }