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.

parseFilename

C-style string parsing with a few caveats:

The result is a UTF-8 encoded string.

source_mapping.parseFilename
fn parseFilename(allocator: Allocator, str: []const u8) error

File

lib/compiler/resinator/source_mapping.zig:568

Code

fn parseFilename(allocator: Allocator, str: []const u8) error{ OutOfMemory, InvalidString }![]u8 {
    const State = enum {
        string,
        escape,
        escape_hex,
        escape_octal,
        escape_u,
    };

    var filename = try std.ArrayList(u8).initCapacity(allocator, str.len);
    errdefer filename.deinit(allocator);
    var state: State = .string;
    var index: usize = 0;
    var escape_len: usize = undefined;
    var escape_val: u64 = undefined;
    var escape_expected_len: u8 = undefined;
    while (index < str.len) : (index += 1) {
        const c = str[index];
        switch (state) {
            .string => switch (c) {
                '\\' => state = .escape,
                '"' => return error.InvalidString,
                else => filename.appendAssumeCapacity(c),
            },
            .escape => switch (c) {
                '\'', '"', '\\', '?', 'n', 'r', 't', 'a', 'b', 'e', 'f', 'v' => {
                    const escaped_c = switch (c) {
                        '\'', '"', '\\', '?' => c,
                        'n' => '\n',
                        'r' => '\r',
                        't' => '\t',
                        'a' => '\x07',
                        'b' => '\x08',
                        'e' => '\x1b', // non-standard
                        'f' => '\x0c',
                        'v' => '\x0b',
                        else => unreachable,
                    };
                    filename.appendAssumeCapacity(escaped_c);
                    state = .string;
                },
                'x' => {
                    escape_val = 0;
                    escape_len = 0;
                    state = .escape_hex;
                },
                '0'...'7' => {
                    escape_val = std.fmt.charToDigit(c, 8) catch unreachable;
                    escape_len = 1;
                    state = .escape_octal;
                },
                'u' => {
                    escape_val = 0;
                    escape_len = 0;
                    state = .escape_u;
                    escape_expected_len = 4;
                },
                'U' => {
                    escape_val = 0;
                    escape_len = 0;
                    state = .escape_u;
                    escape_expected_len = 8;
                },
                else => return error.InvalidString,
            },
            .escape_hex => switch (c) {
                '0'...'9', 'a'...'f', 'A'...'F' => {
                    const digit = std.fmt.charToDigit(c, 16) catch unreachable;
                    if (escape_val != 0) escape_val = std.math.mul(u8, @as(u8, @intCast(escape_val)), 16) catch return error.InvalidString;
                    escape_val = std.math.add(u8, @as(u8, @intCast(escape_val)), digit) catch return error.InvalidString;
                    escape_len += 1;
                    if (escape_len == 2) {
                        filename.appendAssumeCapacity(@intCast(escape_val));
                        state = .string;
                    }
                },
                else => {
                    if (escape_len == 0) return error.InvalidString;
                    filename.appendAssumeCapacity(@intCast(escape_val));
                    state = .string;
                    index -= 1; // reconsume
                },
            },
            .escape_octal => switch (c) {
                '0'...'7' => {
                    const digit = std.fmt.charToDigit(c, 8) catch unreachable;
                    if (escape_val != 0) escape_val = std.math.mul(u8, @as(u8, @intCast(escape_val)), 8) catch return error.InvalidString;
                    escape_val = std.math.add(u8, @as(u8, @intCast(escape_val)), digit) catch return error.InvalidString;
                    escape_len += 1;
                    if (escape_len == 3) {
                        filename.appendAssumeCapacity(@intCast(escape_val));
                        state = .string;
                    }
                },
                else => {
                    if (escape_len == 0) return error.InvalidString;
                    filename.appendAssumeCapacity(@intCast(escape_val));
                    state = .string;
                    index -= 1; // reconsume
                },
            },
            .escape_u => switch (c) {
                '0'...'9', 'a'...'f', 'A'...'F' => {
                    const digit = std.fmt.charToDigit(c, 16) catch unreachable;
                    if (escape_val != 0) escape_val = std.math.mul(u21, @as(u21, @intCast(escape_val)), 16) catch return error.InvalidString;
                    escape_val = std.math.add(u21, @as(u21, @intCast(escape_val)), digit) catch return error.InvalidString;
                    escape_len += 1;
                    if (escape_len == escape_expected_len) {
                        var buf: [4]u8 = undefined;
                        const utf8_len = std.unicode.utf8Encode(@intCast(escape_val), &buf) catch return error.InvalidString;
                        filename.appendSliceAssumeCapacity(buf[0..utf8_len]);
                        state = .string;
                    }
                },
                // Requires escape_expected_len valid hex digits
                else => return error.InvalidString,
            },
        }
    } else {
        switch (state) {
            .string => {},
            .escape, .escape_u => return error.InvalidString,
            .escape_hex => {
                if (escape_len == 0) return error.InvalidString;
                filename.appendAssumeCapacity(@intCast(escape_val));
            },
            .escape_octal => {
                filename.appendAssumeCapacity(@intCast(escape_val));
            },
        }
    }

    return filename.toOwnedSlice(allocator);
}