///|
fn hash_field(hasher : @crypto.SHA256, value : String) -> Unit {
let bytes = @utf8.encode(value[:])
hasher.update(bytes.length().to_uint64().to_be_bytes())
hasher.update(bytes)
}
///|
/// A deterministic content fingerprint, not a signature or authentication check.
pub fn Dataset::fingerprint(self : Dataset) -> String raise SdmxError {
let hasher = @crypto.SHA256::new()
hash_field(hasher, "moonsdmx.dataset/1")
hash_field(hasher, self.structure.to_profile_json())
let ordered = self.rows.copy()
ordered.sort_by((a, b) => a.key_token().lexical_compare(b.key_token()))
hasher.update(ordered.length().to_uint64().to_be_bytes())
for row in ordered {
for value in row.key {
hash_field(hasher, value)
}
for group in [row.measures, row.attributes] {
for value in group {
match value {
None => hasher.update(b"\x00")
Some(text) => {
hasher.update(b"\x01")
hash_field(hasher, text)
}
}
}
}
}
@crypto.bytes_to_hex_string(hasher.finalize())
}
///|
pub fn Structure::fingerprint(self : Structure) -> String raise SdmxError {
let hasher = @crypto.SHA256::new()
hash_field(hasher, "moonsdmx.structure/1")
hash_field(hasher, self.to_profile_json())
@crypto.bytes_to_hex_string(hasher.finalize())
}