Skip to documentation
SLOP

tiny.wayland.transport

Reference tiny.wayland transport

Defined in tiny.wayland.

API (13)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callstiny.waylandtransport
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callsTransportinitOwnedtransport.Storageattach
Static calls · unresolved targets: 1 · external targets: 2.
Called byCallsNo direct callsTransportinitOwnedtest sourcelib.wayland.src.transporttest: transport storage acquires all ...transport.Storagedeinit
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callsTransportinitOwnedtest sourcelib.wayland.src.transporttest: transport storage acquires all ...transport.Storageinit
Static calls · unresolved targets: 0 · external targets: 3.

Source: lib/wayland/src/root.zig:68

zig
pub const transport = @import("transport.zig");

Source: lib/wayland/src/transport.zig

zig
const std = @import("std");const sys = @import("sys");const wayland = @import("root.zig");pub const ReceiveStatus = enum {    data,    pending,    closed,};pub const Limits = wayland.stream.Limits;pub const Capacity = wayland.stream.Capacity;pub const CapacityError = wayland.stream.CapacityError;pub const StorageError = wayland.stream.StorageError;pub const Status = wayland.stream.Status;pub const default_byte_count = wayland.stream.default_byte_count;pub const default_descriptor_count = wayland.stream.default_descriptor_count;const test_capacity = Capacity.derive(.{}) catch unreachable;pub const Storage = struct {    inbox: wayland.stream.Inbox,    outbox: wayland.stream.Outbox,    pub fn init(        session_allocator: std.mem.Allocator,        capacity: Capacity,    ) std.mem.Allocator.Error!Storage {        var inbox = try wayland.stream.Inbox.init(session_allocator, capacity);        errdefer inbox.deinit();        return .{            .inbox = inbox,            .outbox = try wayland.stream.Outbox.init(session_allocator, capacity),        };    }    pub fn deinit(self: *Storage) void {        self.inbox.deinit();        self.outbox.deinit();        self.* = undefined;    }    pub fn attach(        self: *Storage,        owned_descriptor: sys.fd.Descriptor,    ) sys.fd.FlagError!Transport {        errdefer sys.fd.close(owned_descriptor);        try sys.fd.setCloseOnExec(owned_descriptor);        try sys.fd.setNonBlocking(owned_descriptor);        const transport: Transport = .{            .descriptor = owned_descriptor,            .inbox = self.inbox,            .outbox = self.outbox,        };        self.* = undefined;        return transport;    }};pub const Transport = struct {    descriptor: sys.fd.Descriptor,    inbox: wayland.stream.Inbox,    outbox: wayland.stream.Outbox,    pub fn initOwned(        session_allocator: std.mem.Allocator,        descriptor: sys.fd.Descriptor,        capacity: Capacity,    ) (sys.fd.FlagError || std.mem.Allocator.Error)!Transport {        var storage = Storage.init(session_allocator, capacity) catch |err| {            sys.fd.close(descriptor);            return err;        };        errdefer storage.deinit();        return storage.attach(descriptor);    }    pub fn deinit(self: *Transport) void {        self.inbox.deinit();        self.outbox.deinit();        sys.fd.close(self.descriptor);        self.* = undefined;    }    pub fn queueOwned(        self: *Transport,        object_id: u32,        opcode: u16,        payload: []const u8,        descriptors: []const sys.fd.Descriptor,    ) !void {        try self.outbox.queueOwned(object_id, opcode, payload, descriptors);    }    pub fn flush(self: *Transport) !wayland.stream.FlushStatus {        return self.outbox.flush(self.descriptor);    }    pub fn receive(self: *Transport) !ReceiveStatus {        const bytes = try self.inbox.prepareReceive(16 * 1024);        var descriptors: [sys.ancillary.maximum_descriptors]sys.fd.Descriptor = undefined;        const received = sys.ancillary.receive(self.descriptor, bytes, &descriptors) catch |err| switch (err) {            error.WouldBlock => return .pending,            else => return err,        };        if (received.byte_count == 0) {            std.debug.assert(received.descriptor_count == 0);            return .closed;        }        self.inbox.commitReceived(            received.byte_count,            descriptors[0..received.descriptor_count],        ) catch |err| {            closeAll(descriptors[0..received.descriptor_count]);            return err;        };        return .data;    }    pub fn status(self: *const Transport) Status {        const inbound = self.inbox.status();        const outbound = self.outbox.status();        return .{            .inbound_byte_capacity_rejection_count = inbound.inbound_byte_capacity_rejection_count,            .inbound_descriptor_capacity_rejection_count = inbound.inbound_descriptor_capacity_rejection_count,            .outbound_byte_capacity_rejection_count = outbound.outbound_byte_capacity_rejection_count,            .outbound_descriptor_capacity_rejection_count = outbound.outbound_descriptor_capacity_rejection_count,        };    }};fn closeAll(descriptors: []const sys.fd.Descriptor) void {    for (descriptors) |descriptor| sys.fd.close(descriptor);}test "transport storage acquires all four regions before attachment" {    const capacity = try Capacity.derive(.{        .inbound_byte_count = 8,        .inbound_descriptor_count = 1,        .outbound_byte_count = 8,        .outbound_descriptor_count = 1,    });    for (0..4) |fail_index| {        var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{            .fail_index = fail_index,        });        try std.testing.expectError(            error.OutOfMemory,            Storage.init(failing.allocator(), capacity),        );    }    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{        .fail_index = 4,    });    var storage = try Storage.init(failing.allocator(), capacity);    defer storage.deinit();    try std.testing.expectEqual(@as(usize, 4), failing.allocations);    try std.testing.expectEqual(@as(usize, 8), storage.inbox.bytes.len);    try std.testing.expectEqual(@as(usize, 1), storage.inbox.descriptors.len);    try std.testing.expectEqual(@as(usize, 8), storage.outbox.bytes.len);    try std.testing.expectEqual(@as(usize, 1), storage.outbox.descriptors.len);}test "owned descriptor closes when initial storage acquisition fails" {    if (comptime @import("builtin").os.tag != .linux) return error.SkipZigTest;    const sockets = sys.fd.socketPairUnixStream(.{ .close_on_exec = true }) catch |err| switch (err) {        error.UnsupportedPlatform => return error.SkipZigTest,        else => return err,    };    defer sys.fd.close(sockets[1]);    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{        .fail_index = 0,    });    try std.testing.expectError(        error.OutOfMemory,        Transport.initOwned(failing.allocator(), sockets[0], test_capacity),    );    try std.testing.expect(!sys.fd.isOpen(sockets[0]));}test "transport preserves descriptor order over a Unix stream" {    const sockets = sys.fd.socketPairUnixStream(.{ .close_on_exec = true }) catch |err| switch (err) {        error.UnsupportedPlatform => return error.SkipZigTest,        else => return err,    };    var sender = Transport.initOwned(std.testing.allocator, sockets[0], test_capacity) catch |err| {        sys.fd.close(sockets[1]);        return err;    };    defer sender.deinit();    var receiver = try Transport.initOwned(std.testing.allocator, sockets[1], test_capacity);    defer receiver.deinit();    const first_pipe = try sys.fd.pipeWithOptions(.{ .close_on_exec = true });    defer sys.fd.close(first_pipe[0]);    const second_pipe = try sys.fd.pipeWithOptions(.{ .close_on_exec = true });    defer sys.fd.close(second_pipe[0]);    var queued = false;    defer if (!queued) {        sys.fd.close(first_pipe[1]);        sys.fd.close(second_pipe[1]);    };    try sender.queueOwned(2, 5, &.{ 9, 8, 7, 6 }, &.{ first_pipe[1], second_pipe[1] });    queued = true;    try std.testing.expectEqual(wayland.stream.FlushStatus.drained, try sender.flush());    try std.testing.expectEqual(ReceiveStatus.data, try receiver.receive());    const frame = (try receiver.inbox.peek()).?;    try std.testing.expectEqual(@as(u32, 2), frame.header.object_id);    try std.testing.expectEqual(@as(u16, 5), frame.header.opcode);    try std.testing.expectEqualSlices(u8, &.{ 9, 8, 7, 6 }, frame.payload);    const received_first = receiver.inbox.takeDescriptor().?;    defer sys.fd.close(received_first);    const received_second = receiver.inbox.takeDescriptor().?;    defer sys.fd.close(received_second);    try std.testing.expect(receiver.inbox.takeDescriptor() == null);    try std.testing.expectEqual(@as(usize, 1), try sys.fd.write(received_first, "a"));    try std.testing.expectEqual(@as(usize, 1), try sys.fd.write(received_second, "b"));    var byte: [1]u8 = undefined;    try std.testing.expectEqual(@as(usize, 1), try sys.fd.read(first_pipe[0], &byte));    try std.testing.expectEqual(@as(u8, 'a'), byte[0]);    try std.testing.expectEqual(@as(usize, 1), try sys.fd.read(second_pipe[0], &byte));    try std.testing.expectEqual(@as(u8, 'b'), byte[0]);    try receiver.inbox.consume();}test "received descriptors queue independently of fragmented bytes" {    const sockets = sys.fd.socketPairUnixStream(.{ .close_on_exec = true }) catch |err| switch (err) {        error.UnsupportedPlatform => return error.SkipZigTest,        else => return err,    };    defer sys.fd.close(sockets[0]);    var receiver = Transport.initOwned(std.testing.allocator, sockets[1], test_capacity) catch |err| {        return err;    };    defer receiver.deinit();    const pipe = try sys.fd.pipeWithOptions(.{ .close_on_exec = true });    defer sys.fd.close(pipe[0]);    defer sys.fd.close(pipe[1]);    const header = try wayland.wire.Header.init(2, 1, 4);    var message: [12]u8 = undefined;    try header.encode(message[0..8]);    @memcpy(message[8..], &[_]u8{ 1, 2, 3, 4 });    try std.testing.expectEqual(@as(usize, 1), try sys.ancillary.send(sockets[0], message[0..1], &.{pipe[1]}));    try std.testing.expectEqual(ReceiveStatus.data, try receiver.receive());    try std.testing.expect((try receiver.inbox.peek()) == null);    try std.testing.expectEqual(@as(usize, 1), receiver.inbox.queuedDescriptorCount());    try std.testing.expectEqual(message.len - 1, try sys.fd.write(sockets[0], message[1..]));    try std.testing.expectEqual(ReceiveStatus.data, try receiver.receive());    try std.testing.expectEqual(header, (try receiver.inbox.peek()).?.header);    const received = receiver.inbox.takeDescriptor().?;    sys.fd.close(received);}test "inbound byte max plus one preserves the full retained prefix" {    const sockets = sys.fd.socketPairUnixStream(.{ .close_on_exec = true }) catch |err| switch (err) {        error.UnsupportedPlatform => return error.SkipZigTest,        else => return err,    };    var sender = Transport.initOwned(std.testing.allocator, sockets[0], test_capacity) catch |err| {        sys.fd.close(sockets[1]);        return err;    };    defer sender.deinit();    const receiver_capacity = try Capacity.derive(.{        .inbound_byte_count = wayland.wire.header_size,    });    var receiver = try Transport.initOwned(        std.testing.allocator,        sockets[1],        receiver_capacity,    );    defer receiver.deinit();    try sender.queueOwned(2, 1, &.{ 1, 2, 3, 4 }, &.{});    try std.testing.expectEqual(wayland.stream.FlushStatus.drained, try sender.flush());    try std.testing.expectEqual(ReceiveStatus.data, try receiver.receive());    try std.testing.expectEqual(        @as(usize, wayland.wire.header_size),        receiver.inbox.queuedByteCount(),    );    var retained: [wayland.wire.header_size]u8 = undefined;    @memcpy(&retained, receiver.inbox.bytes[0..receiver.inbox.byte_count]);    try std.testing.expectError(error.InboundByteCapacityExceeded, receiver.receive());    try std.testing.expectEqualSlices(        u8,        &retained,        receiver.inbox.bytes[0..receiver.inbox.byte_count],    );    try std.testing.expectEqual(        @as(u64, 1),        receiver.status().inbound_byte_capacity_rejection_count,    );}test "inbound descriptor max plus one closes the rejected foreign batch" {    const sockets = sys.fd.socketPairUnixStream(.{ .close_on_exec = true }) catch |err| switch (err) {        error.UnsupportedPlatform => return error.SkipZigTest,        else => return err,    };    const sender_capacity = try Capacity.derive(.{ .outbound_descriptor_count = 2 });    var sender = Transport.initOwned(std.testing.allocator, sockets[0], sender_capacity) catch |err| {        sys.fd.close(sockets[1]);        return err;    };    defer sender.deinit();    const receiver_capacity = try Capacity.derive(.{ .inbound_descriptor_count = 1 });    var receiver = try Transport.initOwned(        std.testing.allocator,        sockets[1],        receiver_capacity,    );    defer receiver.deinit();    const first_pipe = try sys.fd.pipeWithOptions(.{ .close_on_exec = true });    defer sys.fd.close(first_pipe[0]);    const second_pipe = try sys.fd.pipeWithOptions(.{ .close_on_exec = true });    defer sys.fd.close(second_pipe[0]);    try sender.queueOwned(2, 1, &.{}, &.{ first_pipe[1], second_pipe[1] });    try std.testing.expectEqual(wayland.stream.FlushStatus.drained, try sender.flush());    try std.testing.expectError(        error.InboundDescriptorCapacityExceeded,        receiver.receive(),    );    try std.testing.expectEqual(@as(usize, 0), receiver.inbox.queuedByteCount());    try std.testing.expectEqual(@as(usize, 0), receiver.inbox.queuedDescriptorCount());    try std.testing.expectEqual(        @as(u64, 1),        receiver.status().inbound_descriptor_capacity_rejection_count,    );    var byte: [1]u8 = undefined;    try std.testing.expectEqual(@as(usize, 0), try sys.fd.read(first_pipe[0], &byte));    try std.testing.expectEqual(@as(usize, 0), try sys.fd.read(second_pipe[0], &byte));}

Audit

Definitions6
Public names6
Members5
Version26.7.0
Revisiondaab053ee433