use base64::Engine;
use base64::engine::general_purpose::STANDARD as BASE64;
use md5::{Digest, Md5};
use xxhash_rust::xxh64::xxh64;
const HASH_SEED: u64 = 0xdabadaba_u32 as i32 as i64 as u64;
pub fn partition_identifier(partition_path: &str) -> &str {
if partition_path.is_empty() {
"."
} else {
partition_path
}
}
pub fn xxhash64_bytes(message: &str) -> [u8; 8] {
xxh64(message.as_bytes(), HASH_SEED).to_be_bytes()
}
pub fn md5_bytes(message: &str) -> [u8; 16] {
let digest = Md5::digest(message.as_bytes());
let mut out = [0u8; 16];
out.copy_from_slice(&digest);
out
}
fn b64(bytes: &[u8]) -> String {
BASE64.encode(bytes)
}
pub fn column_index_id_b64(column_name: &str) -> String {
b64(&xxhash64_bytes(column_name))
}
pub fn partition_index_id_b64(partition_path: &str) -> String {
b64(&xxhash64_bytes(partition_identifier(partition_path)))
}
pub fn file_index_id_b64(file_name: &str) -> String {
b64(&md5_bytes(file_name))
}
pub fn column_stats_index_key(partition_path: &str, file_name: &str, column_name: &str) -> String {
format!(
"{}{}{}",
column_index_id_b64(column_name),
partition_index_id_b64(partition_path),
file_index_id_b64(file_name)
)
}
pub fn partition_stats_index_key(partition_path: &str, column_name: &str) -> String {
format!(
"{}{}",
column_index_id_b64(column_name),
partition_index_id_b64(partition_path)
)
}
#[cfg(test)]
mod tests {
use super::*;
fn hex_upper(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02X}")).collect()
}
#[test]
fn xxhash64_matches_java_runtime() {
assert_eq!(hex_upper(&xxhash64_bytes("Hudi")), "B9A47174FD473C0E");
assert_eq!(hex_upper(&xxhash64_bytes("cnt")), "B22F51D19144F1D5");
assert_eq!(hex_upper(&xxhash64_bytes("part=p1")), "B6CB202E0F44EE9D");
}
#[test]
fn stats_keys_match_spark_written_mdt() {
assert_eq!(column_index_id_b64("cnt"), "si9R0ZFE8dU=");
assert_eq!(column_index_id_b64("_hoodie_record_key"), "uSJGdDiVr/k=");
assert_eq!(partition_index_id_b64("part=p1"), "tssgLg9E7p0=");
assert_eq!(partition_index_id_b64("part=p2"), "tFBD22Dgg24=");
assert_eq!(
partition_stats_index_key("part=p1", "cnt"),
"si9R0ZFE8dU=tssgLg9E7p0="
);
assert_eq!(
column_stats_index_key(
"part=p1",
"9c313941-89ac-46f5-9346-b14cd8e6e19d-0_0-21-22_20261027125934360.parquet",
"cnt"
),
"si9R0ZFE8dU=tssgLg9E7p0=fMkQyotsskcUseEa7+Im8A=="
);
}
#[test]
fn md5_matches_java_golden_hex() {
assert_eq!(
hex_upper(&md5_bytes("Hudi")),
"09DAB749F255311C1C9EF6DD7B790170"
);
assert_eq!(
hex_upper(&md5_bytes("A")),
"7FC56270E7A70FA81A5935B72EACBE29"
);
}
#[test]
fn column_stats_key_concat_order() {
let key = column_stats_index_key("city=sf", "a.parquet", "id");
assert!(key.starts_with(&column_index_id_b64("id")));
assert!(key.contains(&partition_index_id_b64("city=sf")));
assert!(key.ends_with(&file_index_id_b64("a.parquet")));
}
#[test]
fn empty_partition_uses_dot_identifier() {
assert_eq!(partition_identifier(""), ".");
assert_eq!(partition_index_id_b64(""), partition_index_id_b64("."));
}
}