// Generated by re2zig
// re2zig $INPUT -o $OUTPUT -cf --recursive-functions -Wno-nondeterministic-tags

const std = @import("std");

// Use a small buffer to cover the case when a lexeme doesn't fit.
// In real world use a larger buffer.
const bufsize = 100;
const none = std.math.maxInt(usize);
const mtag_root = none - 1;

const yycmedia_type: u32 = 1;
const yycheader: u32 = 23;


const State = struct {
    allocator: std.mem.Allocator,
    file: *std.io.Reader,
    yyinput: [bufsize + 1]u8,
    yycursor: usize,
    yymarker: usize,
    yylimit: usize,
    token: usize,
    yycond: u32,
    yystate: i32,
    trie: *std.ArrayList(MtagElem),
    yyt1: usize,
yyt2: usize,

    yytm1: usize,
yytm10: usize,
yytm2: usize,
yytm3: usize,
yytm4: usize,
yytm5: usize,
yytm6: usize,
yytm7: usize,
yytm8: usize,
yytm9: usize,

    l1: usize,
    l2: usize,
    f1: usize,
    f2: usize,
    p1: usize,
    p2: usize,
    p3: usize,
    p4: usize,
    yyaccept: u32,
};

const Status = enum {
    end,
    ready,
    waiting,
    bad_packet,
    big_packet,
};

// An m-tag tree is a way to store histories with an O(1) copy operation.
// Histories naturally form a tree, as they have common start and fork at some
// point. The tree is stored as an array of pairs (tag value, link to parent).
// An m-tag is represented with a single link in the tree (array index).
const MtagElem = struct {
    elem: usize, // tag value
    pred: usize, // index of the predecessor node or root
};

// Append a single value to an m-tag history.
fn add_mtag(st: *State, mtag: usize, value: usize) !usize {
    try st.trie.append(st.allocator, MtagElem{.elem = value, .pred = mtag});
    return st.trie.items.len - 1;
}

// Recursively unwind tag histories and collect version components.
fn unwind(st: *State, x: usize, y: usize) !std.ArrayList([]const u8) {
    // Reached the root of the m-tag tree, stop recursion.
    if (x == mtag_root and y == mtag_root) {
        return try std.ArrayList([]const u8).initCapacity(st.allocator, 0);
    }

    // Unwind history further.
    var ss = try unwind(st, st.trie.items[x].pred, st.trie.items[y].pred);

    // Get tag values. Tag histories must have equal length.
    std.debug.assert(x != mtag_root and y != mtag_root);
    const ex = st.trie.items[x].elem;
    const ey = st.trie.items[y].elem;

    if (ex != none and ey != none) {
        // Both tags are valid string indices, extract component.
        const s = try std.mem.Allocator.dupe(st.allocator, u8, st.yyinput[ex..ey]);
        try ss.append(st.allocator, s);
    } else {
        // Both tags are none (this corresponds to zero repetitions).
        std.debug.assert(ex == none and ey == none);
    }

    return ss;
}

fn s2n(str: []const u8) u32 { // convert a pre-parsed string to a number
    var n: u32 = 0;
    for (str) |c| { n = n * 10 + (c - 48); }
    return n;
}

fn fill(st: *State) Status {
    const used = st.yylimit - st.token;
    const free = bufsize - used;

    // Error: lexeme too long. In real life can reallocate a larger buffer.
    if (free < 1) return Status.big_packet;

    // Shift buffer contents (discard everything up to the current token).
    std.mem.copyBackwards(u8, st.yyinput[0..used], st.yyinput[st.token..st.yylimit]);
    st.yycursor -= st.token;
    st.yymarker = @subWithOverflow(st.yymarker, st.token)[0];
    st.yylimit -= st.token;
    // Tag variables need to be shifted like other input positions. The check
    // for NONE is only needed if some tags are nested inside of alternative or
    // repetition, so that they can have NONE value.
    if (st.yyt1 != none) st.yyt1 = @subWithOverflow(st.yyt1, st.token)[0];
if (st.yyt2 != none) st.yyt2 = @subWithOverflow(st.yyt2, st.token)[0];

    st.token = 0;

    // Fill free space at the end of buffer with new data from file.
    st.yylimit += st.file.readSliceShort(st.yyinput[st.yylimit..bufsize]) catch 0;
    st.yyinput[st.yylimit] = 0; // append sentinel symbol

    return Status.ready;
}


fn yy1(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x2E,
        0x30...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => {
            st.yyt1 = st.yycursor;
            st.yycursor += 1;
            return yy4(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 0;
                return Status.waiting;
            } else {
                st.yycursor += 1;
                return yy2(st);
            }
        },
    }
}

fn yy2(st: *State) Status {
    return yy3(st);
}

fn yy3(st: *State) Status {
    st.yystate = -1;
    return Status.bad_packet;
}

fn yy4(st: *State) Status {
    st.yymarker = st.yycursor;
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => { return yy6(st, yych); },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 1;
                return Status.waiting;
            } else {
                return yy3(st);
            }
        },
    }
}

fn yy5(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    return yy6(st, yych);
}

fn yy6(st: *State, yych: u8) Status {
    switch (yych) {
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x2E,
        0x30...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => {
            st.yycursor += 1;
            return yy5(st);
        },
        0x2F => {
            st.yycursor += 1;
            return yy8(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 2;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy7(st: *State) Status {
    st.yycursor = st.yymarker;
    return yy3(st);
}

fn yy8(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x09,
        0x0D,
        0x20,
        0x3B => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 3;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
        else => { return yy10(st, yych); },
    }
}

fn yy9(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    return yy10(st, yych);
}

fn yy10(st: *State, yych: u8) Status {
    switch (yych) {
        0x09,
        0x20 => {
            st.yytm6 = st.yytm10;
            st.yytm6 = add_mtag(st, st.yytm6, none) catch none;
            st.yytm5 = st.yytm9;
            st.yytm5 = add_mtag(st, st.yytm5, none) catch none;
            st.yytm4 = st.yytm8;
            st.yytm4 = add_mtag(st, st.yytm4, none) catch none;
            st.yytm3 = st.yytm7;
            st.yytm3 = add_mtag(st, st.yytm3, none) catch none;
            st.yyt2 = st.yycursor;
            st.yycursor += 1;
            return yy11(st);
        },
        0x0D => {
            st.yytm6 = st.yytm10;
            st.yytm6 = add_mtag(st, st.yytm6, none) catch none;
            st.yytm5 = st.yytm9;
            st.yytm5 = add_mtag(st, st.yytm5, none) catch none;
            st.yytm4 = st.yytm8;
            st.yytm4 = add_mtag(st, st.yytm4, none) catch none;
            st.yytm3 = st.yytm7;
            st.yytm3 = add_mtag(st, st.yytm3, none) catch none;
            st.yyt2 = st.yycursor;
            st.yycursor += 1;
            return yy12(st);
        },
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x2E,
        0x30...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => {
            st.yycursor += 1;
            return yy9(st);
        },
        0x3B => {
            st.yyt2 = st.yycursor;
            st.yycursor += 1;
            return yy13(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 4;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy11(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yycursor += 1;
            return yy11(st);
        },
        0x0D => {
            st.yycursor += 1;
            return yy12(st);
        },
        0x3B => {
            st.yycursor += 1;
            return yy13(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 5;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy12(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x0A => {
            st.yycursor += 1;
            return yy14(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 6;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy13(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yycursor += 1;
            return yy13(st);
        },
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x2E,
        0x30...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => {
            st.yytm7 = add_mtag(st, st.yytm7, st.yycursor) catch none;
            st.yycursor += 1;
            return yy15(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 7;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy14(st: *State) Status {
    st.l1 = st.yyt1;
    st.l2 = st.yyt2;
    st.p1 = st.yytm3;
    st.p2 = st.yytm4;
    st.p3 = st.yytm5;
    st.p4 = st.yytm6;
    st.yystate = -1;
    
        const mt = st.yyinput[st.l1..st.l2];
        std.log.debug("media type: {s}", .{mt});

        const pnames = unwind(st, st.p1, st.p2) catch null;
        std.log.debug("pnames: {any}", .{pnames});

        const pvals = unwind(st, st.p3, st.p4) catch null;
        std.log.debug("pvals: {any}", .{pvals});

        st.token = st.yycursor;
        return lex(st);

}

fn yy15(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x2E,
        0x30...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => {
            st.yycursor += 1;
            return yy15(st);
        },
        0x3D => {
            st.yytm8 = add_mtag(st, st.yytm8, st.yycursor) catch none;
            st.yycursor += 1;
            return yy16(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 8;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy16(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x2E,
        0x30...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => {
            st.yytm9 = add_mtag(st, st.yytm9, st.yycursor) catch none;
            st.yycursor += 1;
            return yy17(st);
        },
        0x22 => {
            st.yytm9 = add_mtag(st, st.yytm9, st.yycursor) catch none;
            st.yycursor += 1;
            return yy18(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 9;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy17(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yytm10 = add_mtag(st, st.yytm10, st.yycursor) catch none;
            st.yycursor += 1;
            return yy19(st);
        },
        0x0D => {
            st.yytm3 = st.yytm7;
            st.yytm4 = st.yytm8;
            st.yytm5 = st.yytm9;
            st.yytm6 = st.yytm10;
            st.yytm6 = add_mtag(st, st.yytm6, st.yycursor) catch none;
            st.yycursor += 1;
            return yy12(st);
        },
        0x21,
        0x23...0x27,
        0x2A...0x2B,
        0x2D...0x2E,
        0x30...0x39,
        0x41...0x5A,
        0x5E...0x7A,
        0x7C,
        0x7E => {
            st.yycursor += 1;
            return yy17(st);
        },
        0x3B => {
            st.yytm10 = add_mtag(st, st.yytm10, st.yycursor) catch none;
            st.yycursor += 1;
            return yy13(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 10;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy18(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x1F,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 11;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
        0x22 => {
            st.yycursor += 1;
            return yy20(st);
        },
        0x5C => {
            st.yycursor += 1;
            return yy21(st);
        },
        else => {
            st.yycursor += 1;
            return yy18(st);
        },
    }
}

fn yy19(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yycursor += 1;
            return yy19(st);
        },
        0x0D => {
            st.yytm3 = st.yytm7;
            st.yytm4 = st.yytm8;
            st.yytm5 = st.yytm9;
            st.yytm6 = st.yytm10;
            st.yycursor += 1;
            return yy12(st);
        },
        0x3B => {
            st.yycursor += 1;
            return yy13(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 12;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy20(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yytm10 = add_mtag(st, st.yytm10, st.yycursor) catch none;
            st.yycursor += 1;
            return yy19(st);
        },
        0x0D => {
            st.yytm3 = st.yytm7;
            st.yytm4 = st.yytm8;
            st.yytm5 = st.yytm9;
            st.yytm6 = st.yytm10;
            st.yytm6 = add_mtag(st, st.yytm6, st.yycursor) catch none;
            st.yycursor += 1;
            return yy12(st);
        },
        0x3B => {
            st.yytm10 = add_mtag(st, st.yytm10, st.yycursor) catch none;
            st.yycursor += 1;
            return yy13(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 13;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
    }
}

fn yy21(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 14;
                return Status.waiting;
            } else {
                return yy7(st);
            }
        },
        else => {
            st.yycursor += 1;
            return yy18(st);
        },
    }
}

fn yy22(st: *State) Status {
    st.yystate = -1;
    return Status.end;
}

fn yyfnmedia_type(st: *State) Status {
    return yy1(st);
}

fn yy23(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 15;
                return Status.waiting;
            } else {
                st.yycursor += 1;
                return yy24(st);
            }
        },
        0x0D => {
            st.yytm3 = add_mtag(st, st.yytm3, st.yycursor) catch none;
            st.yycursor += 1;
            return yy26(st);
        },
        else => {
            st.yycursor += 1;
            return yy27(st);
        },
    }
}

fn yy24(st: *State) Status {
    return yy25(st);
}

fn yy25(st: *State) Status {
    st.yystate = -1;
    return Status.bad_packet;
}

fn yy26(st: *State) Status {
    st.yyaccept = 0;
    st.yymarker = st.yycursor;
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x0A => {
            st.yycursor += 1;
            return yy28(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 16;
                return Status.waiting;
            } else {
                return yy25(st);
            }
        },
    }
}

fn yy27(st: *State) Status {
    st.yyaccept = 0;
    st.yymarker = st.yycursor;
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 17;
                return Status.waiting;
            } else {
                return yy25(st);
            }
        },
        0x09 => {
            st.yycursor += 1;
            return yy30(st);
        },
        0x0D => {
            st.yytm3 = add_mtag(st, st.yytm3, st.yycursor) catch none;
            st.yycursor += 1;
            return yy31(st);
        },
        else => {
            st.yycursor += 1;
            return yy32(st);
        },
    }
}

fn yy28(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yycursor += 1;
            return yy33(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 18;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
    }
}

fn yy29(st: *State) Status {
    st.yycursor = st.yymarker;
    if (st.yyaccept == 0) { return yy25(st); }
    else  {return yy38(st); }
}

fn yy30(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 19;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
        0x09 => {
            st.yycursor += 1;
            return yy30(st);
        },
        0x20 => {
            st.yycursor += 1;
            return yy32(st);
        },
        else => {
            st.yycursor += 1;
            return yy34(st);
        },
    }
}

fn yy31(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x0A => {
            st.yycursor += 1;
            return yy28(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 20;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
    }
}

fn yy32(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 21;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
        0x09 => {
            st.yycursor += 1;
            return yy30(st);
        },
        0x0D => {
            st.yytm3 = add_mtag(st, st.yytm3, st.yycursor) catch none;
            st.yycursor += 1;
            return yy31(st);
        },
        else => {
            st.yycursor += 1;
            return yy32(st);
        },
    }
}

fn yy33(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 22;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
        0x09,
        0x20 => {
            st.yycursor += 1;
            return yy33(st);
        },
        0x0D => {
            st.yytm5 = add_mtag(st, st.yytm5, st.yycursor) catch none;
            st.yytm4 = st.yytm3;
            st.yytm4 = add_mtag(st, st.yytm4, st.yycursor) catch none;
            st.yycursor += 1;
            return yy35(st);
        },
        else => {
            st.yytm5 = add_mtag(st, st.yytm5, st.yycursor) catch none;
            st.yycursor += 1;
            return yy36(st);
        },
    }
}

fn yy34(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 23;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
        0x0D => {
            st.yytm3 = add_mtag(st, st.yytm3, st.yycursor) catch none;
            st.yycursor += 1;
            return yy31(st);
        },
        else => {
            st.yycursor += 1;
            return yy32(st);
        },
    }
}

fn yy35(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x0A => {
            st.yycursor += 1;
            return yy37(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 24;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
    }
}

fn yy36(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 25;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
        0x09 => {
            st.yycursor += 1;
            return yy39(st);
        },
        0x0D => {
            st.yytm4 = st.yytm3;
            st.yytm4 = add_mtag(st, st.yytm4, st.yycursor) catch none;
            st.yycursor += 1;
            return yy35(st);
        },
        else => {
            st.yycursor += 1;
            return yy36(st);
        },
    }
}

fn yy37(st: *State) Status {
    st.yyaccept = 1;
    st.yymarker = st.yycursor;
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yytm1 = st.yytm3;
            st.yytm2 = st.yytm5;
            st.yytm3 = st.yytm4;
            st.yycursor += 1;
            return yy33(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 26;
                return Status.waiting;
            } else {
                st.yytm1 = st.yytm3;
                st.yytm2 = st.yytm5;
                return yy38(st);
            }
        },
    }
}

fn yy38(st: *State) Status {
    st.f1 = st.yytm1;
    st.f2 = st.yytm2;
    st.yystate = -1;
    
        const folds = unwind(st, st.f1, st.f2) catch null;
        std.log.debug("folds: {any}", .{folds});

        st.token = st.yycursor;
        return lex(st);

}

fn yy39(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 27;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
        0x09 => {
            st.yycursor += 1;
            return yy39(st);
        },
        0x0D => {
            st.yycursor += 1;
            return yy40(st);
        },
        0x20 => {
            st.yycursor += 1;
            return yy36(st);
        },
        else => {
            st.yycursor += 1;
            return yy41(st);
        },
    }
}

fn yy40(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x0A => {
            st.yycursor += 1;
            return yy42(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 28;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
    }
}

fn yy41(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x00,
        0x01...0x08,
        0x0A...0x0C,
        0x0E...0x1E,
        0x7F => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 29;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
        0x09 => {
            st.yycursor += 1;
            return yy43(st);
        },
        0x0D => {
            st.yytm4 = st.yytm3;
            st.yytm4 = add_mtag(st, st.yytm4, st.yycursor) catch none;
            st.yycursor += 1;
            return yy35(st);
        },
        else => {
            st.yycursor += 1;
            return yy36(st);
        },
    }
}

fn yy42(st: *State) Status {
    st.yytm1 = st.yytm3;
    st.yytm2 = st.yytm5;
    return yy38(st);
}

fn yy43(st: *State) Status {
    const yych = st.yyinput[st.yycursor];
    switch (yych) {
        0x09,
        0x20 => {
            st.yycursor += 1;
            return yy43(st);
        },
        0x0D => {
            st.yycursor += 1;
            return yy40(st);
        },
        else => {
            if (st.yylimit <= st.yycursor) {
                st.yystate = 30;
                return Status.waiting;
            } else {
                return yy29(st);
            }
        },
    }
}

fn yy44(st: *State) Status {
    st.yystate = -1;
    return Status.end;
}

fn yyfnheader(st: *State) Status {
    return yy23(st);
}

fn yy0(st: *State) Status {
    switch (st.yycond) {
        yycmedia_type => { return yyfnmedia_type(st); },
        yycheader => { return yyfnheader(st); },
        else => { @panic("internal lexer error"); },
    }
}

fn lex(st: *State) Status {
    switch (st.yystate) {
        -1 => { return yy0(st); },
        0 => {
            if (st.yylimit <= st.yycursor) { return yy22(st); }
            else  {return yy1(st); }
        },
        1 => {
            if (st.yylimit <= st.yycursor) { return yy3(st); }
            else  {return yy4(st); }
        },
        2 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy5(st); }
        },
        3 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy8(st); }
        },
        4 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy9(st); }
        },
        5 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy11(st); }
        },
        6 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy12(st); }
        },
        7 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy13(st); }
        },
        8 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy15(st); }
        },
        9 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy16(st); }
        },
        10 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy17(st); }
        },
        11 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy18(st); }
        },
        12 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy19(st); }
        },
        13 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy20(st); }
        },
        14 => {
            if (st.yylimit <= st.yycursor) { return yy7(st); }
            else  {return yy21(st); }
        },
        15 => {
            if (st.yylimit <= st.yycursor) { return yy44(st); }
            else  {return yy23(st); }
        },
        16 => {
            if (st.yylimit <= st.yycursor) { return yy25(st); }
            else  {return yy26(st); }
        },
        17 => {
            if (st.yylimit <= st.yycursor) { return yy25(st); }
            else  {return yy27(st); }
        },
        18 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy28(st); }
        },
        19 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy30(st); }
        },
        20 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy31(st); }
        },
        21 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy32(st); }
        },
        22 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy33(st); }
        },
        23 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy34(st); }
        },
        24 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy35(st); }
        },
        25 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy36(st); }
        },
        26 => {
            if (st.yylimit <= st.yycursor) {
                st.yytm1 = st.yytm3;
                st.yytm2 = st.yytm5;
                return yy38(st);
            } else {
                return yy37(st);
            }
        },
        27 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy39(st); }
        },
        28 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy40(st); }
        },
        29 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy41(st); }
        },
        30 => {
            if (st.yylimit <= st.yycursor) { return yy29(st); }
            else  {return yy43(st); }
        },
        else => { @panic("internal lexer error"); },
    }
}



fn run(expect: Status, packets: []const []const u8) !void {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();

    // Create a "pipe" (open the same file for reading and writing).
    const fname = "pipe";
    var fw = try std.fs.cwd().createFile(fname, .{});
    var fr = try std.fs.cwd().openFile(fname, .{ .mode = .read_only});

    // Initialize lexer state: `state` value is -1, all offsets are at the end
    // of buffer. Use unbuffered reader - lexer does its own buffering.
    const alc = arena.allocator();
    const zerobuf: [0]u8 = undefined;
    var reader = fr.reader(&zerobuf);
    var mt = try std.ArrayList(MtagElem).initCapacity(alc, 0);
    defer mt.deinit(alc);
    var st = State{
        .allocator = alc,
        .file = &reader.interface,
        .yyinput = undefined,
        .yycursor = bufsize,
        .yymarker = bufsize,
        .yylimit = bufsize,
        .token = bufsize,
        .yycond = yycmedia_type,
        .yystate = -1,
        .trie = &mt,
        .yyt1 = none,
.yyt2 = none,

        .yytm1 = mtag_root,
.yytm10 = mtag_root,
.yytm2 = mtag_root,
.yytm3 = mtag_root,
.yytm4 = mtag_root,
.yytm5 = mtag_root,
.yytm6 = mtag_root,
.yytm7 = mtag_root,
.yytm8 = mtag_root,
.yytm9 = mtag_root,

        .l1 = none,
        .l2 = none,
        .f1 = mtag_root,
        .f2 = mtag_root,
        .p1 = mtag_root,
        .p2 = mtag_root,
        .p3 = mtag_root,
        .p4 = mtag_root,
        .yyaccept = 0,
    };
    // Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
    st.yyinput[st.yylimit] = 0;

    // Main loop. The buffer contains incomplete data which appears packet by
    // packet. When the lexer needs more input it saves its internal state and
    // returns to the caller which should provide more input and resume lexing.
    var status = Status.ready;
    var send: usize = 0;
    while (true) {
        status = lex(&st);
        if (status == Status.end) {
            break;
        } else if (status == Status.waiting) {
            if (send < packets.len) {
                std.log.debug("sending packet {}", .{send});
                try fw.writeAll(packets[send]);
                send += 1;
            }
            status = fill(&st);
            std.log.debug("filled buffer [{s}], status {}", .{st.yyinput, status});
            if (status != Status.ready) {
                break;
            }
        } else if (status == Status.bad_packet) {
            break;
        }
    }

    // Check results.
    try std.testing.expectEqual(status, expect);

    // Cleanup: remove input file.
    fw.close();
    fr.close();
    try std.fs.cwd().deleteFile(fname);
}

test {
    try run(Status.end,
        &[_][]const u8{"ap", "plication/j", "son;", " charset=\"", "utf\\\"-8\"\r", "\n", ""});
}
