Skip to main content

akar_function/scalar/
hash.rs

1use super::get_string;
2use crate::registry::*;
3use akar_common::types::Value;
4use md5::{Digest, Md5};
5use sha2::Sha256;
6
7// ==================== Hash functions ====================
8
9/// Simple non-cryptographic hash for any Value (matching C++ murmurhash64 semantics).
10fn hash_value(v: &Value) -> u64 {
11    match v {
12        Value::Null => u64::MAX,
13        Value::Bool(b) => murmur64(*b as u64),
14        Value::Int64(x) => murmur64(*x as u64),
15        Value::Int32(x) => murmur64(*x as u64),
16        Value::Double(x) => {
17            if *x == 0.0 {
18                murmur64(0)
19            } else {
20                murmur64(x.to_bits())
21            }
22        }
23        Value::String(s) => hash_string(s),
24        Value::List(items) => {
25            let mut h: u64 = 0;
26            for item in items {
27                h = combine_hash(h, hash_value(item));
28            }
29            h
30        }
31        _ => {
32            let s = format!("{:?}", v);
33            hash_string(&s)
34        }
35    }
36}
37
38fn murmur64(mut x: u64) -> u64 {
39    x ^= x >> 32;
40    x = x.wrapping_mul(0xd6e8feb86659fd93);
41    x ^= x >> 32;
42    x = x.wrapping_mul(0xd6e8feb86659fd93);
43    x ^= x >> 32;
44    x
45}
46
47fn combine_hash(a: u64, b: u64) -> u64 {
48    a.wrapping_mul(0xbf58476d1ce4e5b9) ^ b
49}
50
51fn hash_string(s: &str) -> u64 {
52    let bytes = s.as_bytes();
53    let mut h: u64 = 0;
54    for chunk in bytes.chunks(8) {
55        let mut val: u64 = 0;
56        for (i, &b) in chunk.iter().enumerate() {
57            val |= (b as u64) << (i * 8);
58        }
59        h = combine_hash(h, murmur64(val));
60    }
61    h
62}
63
64pub(crate) fn evaluate_hash(op: HashOp, args: &[Value]) -> Result<Value, String> {
65    match op {
66        HashOp::Md5 => {
67            let s = get_string(&args[0])?;
68            let mut hasher = Md5::new();
69            hasher.update(s.as_bytes());
70            let result = hasher.finalize();
71            Ok(Value::String(result.iter().map(|b| format!("{:02x}", b)).collect()))
72        }
73        HashOp::Sha256 => {
74            let s = get_string(&args[0])?;
75            let mut hasher = Sha256::new();
76            hasher.update(s.as_bytes());
77            let result = hasher.finalize();
78            Ok(Value::String(result.iter().map(|b| format!("{:02x}", b)).collect()))
79        }
80        HashOp::Hash => {
81            if args.is_empty() {
82                return Err("hash requires at least one argument".into());
83            }
84            let h = hash_value(&args[0]);
85            Ok(Value::Int64(h as i64))
86        }
87    }
88}