akar_function/scalar/
hash.rs1use super::get_string;
2use crate::registry::*;
3use akar_common::types::Value;
4use md5::{Digest, Md5};
5use sha2::Sha256;
6
7fn 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}