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.

PaxIterator

tar.PaxIterator
pub const PaxIterator = struct

File

lib/std/tar.zig:504

Code

pub const PaxIterator = struct {
    size: usize, // cumulative size of all pax attributes
    reader: *Io.Reader,

    const Self = @This();

    const Attribute = struct {
        kind: PaxAttributeKind,
        len: usize, // length of the attribute value
        reader: *Io.Reader, // reader positioned at value start

        // Copies pax attribute value into destination buffer.
        // Must be called with destination buffer of size at least Attribute.len.
        pub fn value(self: Attribute, dst: []u8) ![]const u8 {
            if (self.len > dst.len) return error.TarInsufficientBuffer;
            // assert(self.len <= dst.len);
            const buf = dst[0..self.len];
            const n = try self.reader.readSliceShort(buf);
            if (n < self.len) return error.UnexpectedEndOfStream;
            try validateAttributeEnding(self.reader);
            if (hasNull(buf)) return error.PaxNullInValue;
            return buf;
        }
    };

    // Iterates over pax attributes. Returns known only known attributes.
    // Caller has to call value in Attribute, to advance reader across value.
    pub fn next(self: *Self) !?Attribute {
        // Pax extended header consists of one or more attributes, each constructed as follows:
        // "%d %s=%s\n", <length>, <keyword>, <value>
        while (self.size > 0) {
            const length_buf = try self.reader.takeSentinel(' ');
            const length = try std.fmt.parseInt(usize, length_buf, 10); // record length in bytes

            const keyword = try self.reader.takeSentinel('=');
            if (hasNull(keyword)) return error.PaxNullInKeyword;

            // calculate value_len
            const value_start = length_buf.len + keyword.len + 2; // 2 separators
            if (length < value_start + 1 or self.size < length) return error.UnexpectedEndOfStream;
            const value_len = length - value_start - 1; // \n separator at end
            self.size -= length;

            const kind: PaxAttributeKind = if (eql(keyword, "path"))
                .path
            else if (eql(keyword, "linkpath"))
                .linkpath
            else if (eql(keyword, "size"))
                .size
            else {
                try self.reader.discardAll(value_len);
                try validateAttributeEnding(self.reader);
                continue;
            };
            if (kind == .size and value_len > pax_max_size_attr_len) {
                return error.PaxSizeAttrOverflow;
            }
            return .{
                .kind = kind,
                .len = value_len,
                .reader = self.reader,
            };
        }

        return null;
    }

    fn eql(a: []const u8, b: []const u8) bool {
        return std.mem.eql(u8, a, b);
    }

    fn hasNull(str: []const u8) bool {
        return (std.mem.findScalar(u8, str, 0)) != null;
    }

    // Checks that each record ends with new line.
    fn validateAttributeEnding(reader: *Io.Reader) !void {
        if (try reader.takeByte() != '\n') return error.PaxInvalidAttributeEnd;
    }
}