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.

Decoder

An abstraction to ensure that protocol-parsing code does not perform an out-of-bounds read.

tls.Decoder
pub const Decoder = struct

File

lib/std/crypto/tls.zig:634

Code

pub const Decoder = struct {
    buf: []u8,
    /// Points to the next byte in buffer that will be decoded.
    idx: usize = 0,
    /// Up to this point in `buf` we have already checked that `cap` is greater than it.
    our_end: usize = 0,
    /// Beyond this point in `buf` is extra tag-along bytes beyond the amount we
    /// requested with `readAtLeast`.
    their_end: usize = 0,
    /// Points to the end within buffer that has been filled. Beyond this point
    /// in buf is undefined bytes.
    cap: usize = 0,
    /// Debug helper to prevent illegal calls to read functions.
    disable_reads: bool = false,

    pub fn fromTheirSlice(buf: []u8) Decoder {
        return .{
            .buf = buf,
            .their_end = buf.len,
            .cap = buf.len,
            .disable_reads = true,
        };
    }

    /// Use this function to increase `their_end`.
    pub fn readAtLeast(d: *Decoder, stream: *std.Io.Reader, their_amt: usize) !void {
        assert(!d.disable_reads);
        const existing_amt = d.cap - d.idx;
        d.their_end = d.idx + their_amt;
        if (their_amt <= existing_amt) return;
        const request_amt = their_amt - existing_amt;
        const dest = d.buf[d.cap..];
        if (request_amt > dest.len) return error.TlsRecordOverflow;
        stream.readSlice(dest[0..request_amt]) catch |err| switch (err) {
            error.EndOfStream => return error.TlsConnectionTruncated,
            error.ReadFailed => |e| return e,
        };
        d.cap += request_amt;
    }

    /// Same as `readAtLeast` but also increases `our_end` by exactly `our_amt`.
    /// Use when `our_amt` is calculated by us, not by them.
    pub fn readAtLeastOurAmt(d: *Decoder, stream: *std.Io.Reader, our_amt: usize) !void {
        assert(!d.disable_reads);
        try readAtLeast(d, stream, our_amt);
        d.our_end = d.idx + our_amt;
    }

    /// Use this function to increase `our_end`.
    /// This should always be called with an amount provided by us, not them.
    pub fn ensure(d: *Decoder, amt: usize) !void {
        d.our_end = @max(d.idx + amt, d.our_end);
        if (d.our_end > d.their_end) return error.TlsDecodeError;
    }

    /// Use this function to increase `idx`.
    pub fn decode(d: *Decoder, comptime T: type) T {
        switch (@typeInfo(T)) {
            .int => |info| switch (info.bits) {
                8 => {
                    skip(d, 1);
                    return d.buf[d.idx - 1];
                },
                16 => {
                    skip(d, 2);
                    const b0: u16 = d.buf[d.idx - 2];
                    const b1: u16 = d.buf[d.idx - 1];
                    return (b0 << 8) | b1;
                },
                24 => {
                    skip(d, 3);
                    const b0: u24 = d.buf[d.idx - 3];
                    const b1: u24 = d.buf[d.idx - 2];
                    const b2: u24 = d.buf[d.idx - 1];
                    return (b0 << 16) | (b1 << 8) | b2;
                },
                else => @compileError("unsupported int type: " ++ @typeName(T)),
            },
            .@"enum" => |info| {
                if (info.mode == .exhaustive) @compileError("exhaustive enum cannot be used");
                return @fromBackingInt(@intCast(d.decode(info.tag_type)));
            },
            else => @compileError("unsupported type: " ++ @typeName(T)),
        }
    }

    /// Use this function to increase `idx`.
    pub fn array(d: *Decoder, comptime len: usize) *[len]u8 {
        skip(d, len);
        return d.buf[d.idx - len ..][0..len];
    }

    /// Use this function to increase `idx`.
    pub fn slice(d: *Decoder, len: usize) []u8 {
        skip(d, len);
        return d.buf[d.idx - len ..][0..len];
    }

    /// Use this function to increase `idx`.
    pub fn skip(d: *Decoder, amt: usize) void {
        d.idx += amt;
        assert(d.idx <= d.our_end); // insufficient ensured bytes
    }

    pub fn eof(d: Decoder) bool {
        assert(d.our_end <= d.their_end);
        assert(d.idx <= d.our_end);
        return d.idx == d.their_end;
    }

    /// Provide the length they claim, and receive a sub-decoder specific to that slice.
    /// The parent decoder is advanced to the end.
    pub fn sub(d: *Decoder, their_len: usize) !Decoder {
        const end = d.idx + their_len;
        if (end > d.their_end) return error.TlsDecodeError;
        const sub_buf = d.buf[d.idx..end];
        d.idx = end;
        d.our_end = end;
        return fromTheirSlice(sub_buf);
    }

    pub fn rest(d: Decoder) []u8 {
        return d.buf[d.idx..d.cap];
    }
}