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: type
str: []const u8
Possible Errors
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;
}