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.

Raw

Does not compress data

Compress.Raw
pub const Raw = struct

File

lib/std/compress/flate/Compress.zig:1598

Code

pub const Raw = struct {
    /// After `finish` is called, all vtable calls with result in `error.WriteFailed`.
    writer: Writer,
    output: *Writer,
    hasher: flate.Container.Hasher,

    const max_block_size: u16 = 65535;
    const full_header: [5]u8 = .{
        BlockHeader.int(.{ .final = false, .kind = .stored }),
        255,
        255,
        0,
        0,
    };

    /// While there is no minimum buffer size, it is recommended
    /// to be at least `flate.max_window_len` for optimal output.
    pub fn init(output: *Writer, buffer: []u8, container: flate.Container) Writer.Error!Raw {
        try output.writeAll(container.header());
        return .{
            .writer = .{
                .buffer = buffer,
                .vtable = &.{
                    .drain = Raw.drain,
                    .flush = Raw.flush,
                    .rebase = Raw.rebase,
                },
            },
            .output = output,
            .hasher = .init(container),
        };
    }

    fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
        errdefer w.* = .failing;
        const r: *Raw = @fieldParentPtr("writer", w);
        const min_block = @min(w.buffer.len, max_block_size);
        const pattern = data[data.len - 1];
        var partial_header: [5]u8 = undefined;

        var vecs: [16][]const u8 = undefined;
        var vecs_n: usize = 0;
        const data_bytes = Writer.countSplat(data, splat);
        const total_bytes = w.end + data_bytes;
        var rem_bytes = total_bytes;
        var rem_splat = splat;
        var rem_data = data;
        var rem_data_elem: []const u8 = w.buffered();

        assert(rem_bytes > min_block);
        while (rem_bytes > min_block) { // not >= to allow `min_block` blocks to be marked as final
            // also, it handles the case of `min_block` being zero (no buffer)
            const block_size: u16 = @min(rem_bytes, max_block_size);
            rem_bytes -= block_size;

            if (vecs_n == vecs.len) {
                try r.output.writeVecAll(&vecs);
                vecs_n = 0;
            }
            vecs[vecs_n] = if (block_size == 65535)
                &full_header
            else header: {
                partial_header[0] = BlockHeader.int(.{ .final = false, .kind = .stored });
                mem.writeInt(u16, partial_header[1..3], block_size, .little);
                mem.writeInt(u16, partial_header[3..5], ~block_size, .little);
                break :header &partial_header;
            };
            vecs_n += 1;

            var block_limit: Io.Limit = .limited(block_size);
            while (true) {
                if (vecs_n == vecs.len) {
                    try r.output.writeVecAll(&vecs);
                    vecs_n = 0;
                }

                const vec = block_limit.sliceConst(rem_data_elem);
                vecs[vecs_n] = vec;
                vecs_n += 1;
                r.hasher.update(vec);

                const is_pattern = rem_splat != splat and vec.len == pattern.len;
                if (is_pattern) assert(pattern.len != 0); // exceeded countSplat

                if (!is_pattern or rem_splat == 0 or pattern.len > @backingInt(block_limit) / 2) {
                    rem_data_elem = rem_data_elem[vec.len..];
                    block_limit = block_limit.subtract(vec.len).?;

                    if (rem_data_elem.len == 0) {
                        rem_data_elem = rem_data[0];
                        if (rem_data.len != 1) {
                            rem_data = rem_data[1..];
                        } else if (rem_splat != 0) {
                            rem_splat -= 1;
                        } else {
                            // All of `data` has been consumed.
                            assert(block_limit == .nothing);
                            assert(rem_bytes == 0);
                            // Since `rem_bytes` and `block_limit` are zero, these won't be used.
                            rem_data = undefined;
                            rem_data_elem = undefined;
                            rem_splat = undefined;
                        }
                    }
                    if (block_limit == .nothing) break;
                } else {
                    const out_splat = @backingInt(block_limit) / pattern.len;
                    assert(out_splat >= 2);

                    try r.output.writeSplatAll(vecs[0..vecs_n], out_splat);
                    for (1..out_splat) |_| r.hasher.update(vec);

                    vecs_n = 0;
                    block_limit = block_limit.subtract(pattern.len * out_splat).?;
                    if (rem_splat >= out_splat) {
                        // `out_splat` contains `rem_data`, however one more needs subtracted
                        // anyways since the next pattern is also being taken.
                        rem_splat -= out_splat;
                    } else {
                        // All of `data` has been consumed.
                        assert(block_limit == .nothing);
                        assert(rem_bytes == 0);
                        // Since `rem_bytes` and `block_limit` are zero, these won't be used.
                        rem_data = undefined;
                        rem_data_elem = undefined;
                        rem_splat = undefined;
                    }
                    if (block_limit == .nothing) break;
                }
            }
        }

        if (vecs_n != 0) { // can be the case if a splat was sent
            try r.output.writeVecAll(vecs[0..vecs_n]);
        }

        if (rem_bytes > data_bytes) {
            assert(rem_bytes - data_bytes == rem_data_elem.len);
            assert(&rem_data_elem[0] == &w.buffer[total_bytes - rem_bytes]);
        }
        return w.consume(total_bytes - rem_bytes);
    }

    fn flush(w: *Writer) Writer.Error!void {
        errdefer w.* = .failing;
        try Raw.rebaseInner(w, 0, w.buffer.len, false);
    }

    pub fn finish(r: *Raw) Writer.Error!void {
        defer r.writer = .failing;
        try Raw.rebaseInner(&r.writer, 0, r.writer.buffer.len, true);
        // The footer is written in `rebaseInner` as part of the write vector
    }

    fn rebase(w: *Writer, preserve: usize, capacity: usize) Writer.Error!void {
        errdefer w.* = .failing;
        try Raw.rebaseInner(w, preserve, capacity, false);
    }

    fn rebaseInner(w: *Writer, preserve: usize, capacity: usize, eos: bool) Writer.Error!void {
        const r: *Raw = @fieldParentPtr("writer", w);
        assert(preserve + capacity <= w.buffer.len);
        if (eos) assert(capacity == w.buffer.len);

        var partial_header: [5]u8 = undefined;
        var footer_buf: [8]u8 = undefined;
        const preserved = @min(w.end, preserve);
        var remaining = w.buffer[0 .. w.end - preserved];

        var vecs: [16][]const u8 = undefined;
        var vecs_n: usize = 0;
        while (remaining.len > max_block_size) { // not >= so there is always a block down below
            if (vecs_n == vecs.len) {
                try r.output.writeVecAll(&vecs);
                vecs_n = 0;
            }
            vecs[vecs_n + 0] = &full_header;
            vecs[vecs_n + 1] = remaining[0..max_block_size];
            r.hasher.update(vecs[vecs_n + 1]);
            vecs_n += 2;
            remaining = remaining[max_block_size..];
        }

        // eos check required for empty block
        if (w.buffer.len - (remaining.len + preserved) < capacity or eos) {
            // A partial write is necessary to reclaim enough buffer space
            const block_size: u16 = @intCast(remaining.len);
            partial_header[0] = BlockHeader.int(.{ .final = eos, .kind = .stored });
            mem.writeInt(u16, partial_header[1..3], block_size, .little);
            mem.writeInt(u16, partial_header[3..5], ~block_size, .little);

            if (vecs_n == vecs.len) {
                try r.output.writeVecAll(&vecs);
                vecs_n = 0;
            }
            vecs[vecs_n + 0] = &partial_header;
            vecs[vecs_n + 1] = remaining[0..block_size];
            r.hasher.update(vecs[vecs_n + 1]);
            vecs_n += 2;
            remaining = remaining[block_size..];
            assert(remaining.len == 0);

            if (eos and r.hasher != .raw) {
                // the footer is done here instead of `flush` so it can be included in the vector
                var footer_w: Writer = .fixed(&footer_buf);
                r.hasher.writeFooter(&footer_w) catch unreachable;
                assert(footer_w.end != 0);

                if (vecs_n == vecs.len) {
                    try r.output.writeVecAll(&vecs);
                    return r.output.writeAll(footer_w.buffered());
                } else {
                    vecs[vecs_n] = footer_w.buffered();
                    vecs_n += 1;
                }
            }
        }

        try r.output.writeVecAll(vecs[0..vecs_n]);
        _ = w.consume(w.end - preserved - remaining.len);
    }
}