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.

deserialize

Deserialize a PHC-formatted string into a structure HashResult.

Required field in the HashResult structure:

Other fields will also be deserialized from the function parameters section.

phc_encoding.deserialize
pub fn deserialize(comptime HashResult: type, str: []const u8) Error!HashResult

File

lib/std/crypto/phc_encoding.zig:77

Code

pub fn deserialize(comptime HashResult: type, str: []const u8) Error!HashResult {
    if (@hasField(HashResult, version_param_name)) {
        @compileError("Field name '" ++ version_param_name ++ "'' is reserved for the algorithm version");
    }

    var out = mem.zeroes(HashResult);
    var it = mem.splitScalar(u8, str, fields_delimiter_scalar);
    var set_fields: usize = 0;

    while (true) {
        // Read the algorithm identifier
        if ((it.next() orelse return Error.InvalidEncoding).len != 0) return Error.InvalidEncoding;
        out.alg_id = it.next() orelse return Error.InvalidEncoding;
        set_fields += 1;

        // Read the optional version number
        var field = it.next() orelse break;
        if (kvSplit(field)) |opt_version| {
            if (mem.eql(u8, opt_version.key, version_param_name)) {
                if (@hasField(HashResult, "alg_version")) {
                    const ValueType = switch (@typeInfo(@TypeOf(out.alg_version))) {
                        .optional => |opt| opt.child,
                        else => @TypeOf(out.alg_version),
                    };
                    out.alg_version = fmt.parseUnsigned(
                        ValueType,
                        opt_version.value,
                        10,
                    ) catch return Error.InvalidEncoding;
                    set_fields += 1;
                }
                field = it.next() orelse break;
            }
        } else |_| {}

        // Read optional parameters
        var has_params = false;
        var it_params = mem.splitScalar(u8, field, params_delimiter_scalar);
        while (it_params.next()) |params| {
            const param = kvSplit(params) catch break;
            var found = false;
            const info = @typeInfo(HashResult).@"struct";
            inline for (info.field_names, info.field_types) |p_name, p_type| {
                if (mem.eql(u8, p_name, param.key)) {
                    switch (@typeInfo(p_type)) {
                        .int => @field(out, p_name) = fmt.parseUnsigned(
                            p_type,
                            param.value,
                            10,
                        ) catch return Error.InvalidEncoding,
                        .pointer => |ptr| {
                            if (!ptr.attrs.@"const") @compileError("Value slice must be constant");
                            @field(out, p_name) = param.value;
                        },
                        .@"struct" => try @field(out, p_name).fromB64(param.value),
                        else => std.debug.panic(
                            "Value for [{s}] must be an integer, a constant slice or a BinValue",
                            .{p_name},
                        ),
                    }
                    set_fields += 1;
                    found = true;
                    break;
                }
            }
            if (!found) return Error.InvalidEncoding; // An unexpected parameter was found in the string
            has_params = true;
        }

        // No separator between an empty parameters set and the salt
        if (has_params) field = it.next() orelse break;

        // Read an optional salt
        if (@hasField(HashResult, "salt")) {
            try out.salt.fromB64(field);
            set_fields += 1;
        } else {
            return Error.InvalidEncoding;
        }

        // Read an optional hash
        field = it.next() orelse break;
        if (@hasField(HashResult, "hash")) {
            try out.hash.fromB64(field);
            set_fields += 1;
        } else {
            return Error.InvalidEncoding;
        }
        break;
    }

    // Check that all the required fields have been set, excluding optional values and parameters
    // with default values
    var expected_fields: usize = 0;
    const info = @typeInfo(HashResult).@"struct";
    inline for (info.field_types, info.field_attrs) |p_type, p_attrs| {
        if (@typeInfo(p_type) != .optional and p_attrs.default_value_ptr == null) {
            expected_fields += 1;
        }
    }
    if (set_fields < expected_fields) return Error.InvalidEncoding;

    return out;
}