Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

MemoryMappedInput

Reusable and recoverable input.

Has a 32-bit limit on the input length. This has the nice side effect that u32 can be used in most placed in fuzzer with the last @sizeOf(abi.MmapInputHeader) values reserved.

fuzzer.MemoryMappedInput
const MemoryMappedInput = struct

File

lib/fuzzer.zig:2321

Code

const MemoryMappedInput = struct {
    const Header = abi.MmapInputHeader;

    len: u32,
    /// Directly accessing `memory` is unsafe, use either `inputSlice` or `writeSlice`.
    mmap: Io.File.MemoryMap,
    in_i: u32,

    /// `file` becomes owned by the returned `MemoryMappedInput`
    pub fn init(file: Io.File, instance_id: u32, in_i: u32) MemoryMappedInput {
        var size = file.length(io) catch |e|
            panic("failed to get length of 'in{x}': {t}", .{ in_i, e });
        if (size < std.heap.page_size_max) {
            size = std.heap.page_size_max;
            file.setLength(io, size) catch |e|
                panic("failed to resize 'in{x}': {t}", .{ in_i, e });
        }
        const map = file.createMemoryMap(io, .{ .len = size }) catch |e|
            panic("failed to memmap input file 'in{x}': {t}", .{ in_i, e });
        @as(*volatile Header, @ptrCast(map.memory)).* = .{
            .pc_digest = mem.nativeToLittle(u64, exec.pc_digest),
            .instance_id = mem.nativeToLittle(u32, instance_id),
            .test_i = 0,
            .len = 0,
        };
        return .{
            .len = 0,
            .mmap = map,
            .in_i = in_i,
        };
    }

    pub fn deinit(l: *MemoryMappedInput) void {
        const f = l.mmap.file;
        l.mmap.write(io) catch |e| panic("failed to write memory map of 'in{x}': {t}", .{ l.in_i, e });
        l.mmap.destroy(io);
        f.close(io);
        l.* = undefined;
    }

    /// Modify the array so that it can hold at least `additional_count` **more** items.
    ///
    /// Invalidates element pointers if additional memory is needed.
    pub fn ensureUnusedCapacity(l: *MemoryMappedInput, additional_count: usize) void {
        return l.ensureSize(@sizeOf(Header) + l.len + additional_count);
    }

    fn ensureSize(l: *MemoryMappedInput, min_capacity: usize) void {
        if (l.mmap.memory.len < min_capacity) {
            @branchHint(.unlikely);

            const max_capacity = 1 << 32; // The size of the header is not added
            // in order to keep the capacity page aligned and to allow those values to
            // reserved for other places.
            if (min_capacity > max_capacity) @panic("too much smith data requested");

            const new_capacity = @min(growCapacity(min_capacity), max_capacity);
            l.mmap.file.setLength(io, new_capacity) catch |e|
                panic("failed to resize 'in{x}': {t}", .{ l.in_i, e });
            l.mmap.setLength(io, new_capacity) catch |se| switch (se) {
                error.OperationUnsupported => {
                    const f = l.mmap.file;
                    l.mmap.destroy(io);
                    l.mmap = f.createMemoryMap(io, .{ .len = new_capacity }) catch |e|
                        panic("failed to memory map 'in{x}': {t}", .{ l.in_i, e });
                },
                else => panic("failed to resize memory map of 'in{x}': {t}", .{ l.in_i, se }),
            };
        }
    }

    // Only writing has side effects, so volatile is not needed
    pub fn inputSlice(l: *MemoryMappedInput) []const u8 {
        return l.mmap.memory[@sizeOf(Header)..][0..l.len];
    }

    // Writing has side effectsd, so volatile is necessary
    pub fn writeSlice(l: *MemoryMappedInput) []volatile u8 {
        return l.mmap.memory;
    }

    fn writeLen(l: *MemoryMappedInput) void {
        l.writeSlice()[@offsetOf(Header, "len")..][0..4].* =
            @bitCast(mem.nativeToLittle(u32, l.len));
    }

    pub fn setTest(l: *MemoryMappedInput, i: u32) void {
        l.writeSlice()[@offsetOf(Header, "test_i")..][0..4].* =
            @bitCast(mem.nativeToLittle(u32, i));
    }

    /// Invalidates all element pointers.
    pub fn clearRetainingCapacity(l: *MemoryMappedInput) void {
        l.len = 0;
        l.writeLen();
    }

    /// Append the slice of items to the list.
    ///
    /// Invalidates item pointers if more space is required.
    pub fn appendSlice(l: *MemoryMappedInput, items: []const u8) void {
        l.ensureUnusedCapacity(items.len);
        @memcpy(l.writeSlice()[@sizeOf(Header) + l.len ..][0..items.len], items);
        l.len += @as(u32, @intCast(items.len));
        l.writeLen();
    }

    /// Append the little-endian integer to the list.
    ///
    /// Invalidates item pointers if more space is required.
    pub fn appendLittleInt(l: *MemoryMappedInput, T: type, x: T) void {
        l.ensureUnusedCapacity(@sizeOf(T));
        l.writeSlice()[@sizeOf(Header) + l.len ..][0..@sizeOf(T)].* =
            @bitCast(mem.nativeToLittle(T, x));
        l.len += @sizeOf(T);
        l.writeLen();
    }

    /// Called when memory growth is necessary. Returns a capacity larger than
    /// minimum that grows super-linearly.
    fn growCapacity(minimum: usize) usize {
        return mem.alignForward(
            usize,
            minimum +| (minimum / 2 + std.heap.page_size_max),
            std.heap.page_size_max,
        );
    }
}