Function deserialize [src]

Deserialize a PHC-formatted string into a structure HashResult. Required field in the HashResult structure: alg_id: algorithm identifier Optional, special fields: alg_version: algorithm version (unsigned integer) salt: salt hash: output of the hash function Other fields will also be deserialized from the function parameters section.

Prototype

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

Parameters

HashResult: typestr: []const u8

Possible Errors

InvalidEncoding EncodingError
NoSpaceLeft

Source

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 value_type_info = switch (@typeInfo(@TypeOf(out.alg_version))) { .optional => |opt| @typeInfo(opt.child), else => |t| t, }; out.alg_version = fmt.parseUnsigned( @Type(value_type_info), 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; inline for (comptime meta.fields(HashResult)) |p| { 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.is_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; inline for (comptime meta.fields(HashResult)) |p| { if (@typeInfo(p.type) != .optional and p.default_value_ptr == null) { expected_fields += 1; } } if (set_fields < expected_fields) return Error.InvalidEncoding; return out; }