hayson-engine 0.14.0

Storage engine for hayson document databases — the embeddable core behind hayson-server, hayson-cli and hayson-browser
Documentation
use base64::engine::general_purpose::STANDARD;
use base64::Engine;

/// Decodes an image field's `content` — standard base64 (ADR-y5x8's predecessor decision:
/// image bytes travel as base64, not raw UTF-8) — into the real bytes to hash/store.
pub fn decode_image_content(content: &str) -> Result<Vec<u8>, String> {
    STANDARD
        .decode(content)
        .map_err(|_| "expected \"content\" to be valid base64".to_string())
}

const FNV_OFFSET: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;

fn fnv1a(bytes: &[u8]) -> u64 {
    let mut hash = FNV_OFFSET;
    for &b in bytes {
        hash ^= b as u64;
        hash = hash.wrapping_mul(FNV_PRIME);
    }
    hash
}

/// Content-addressed relative path for an image's bytes: same bytes always hash to the same
/// path, which is what lets the engine deduplicate storage instead of writing a copy per record.
pub fn physical_path_of(bytes: &[u8]) -> String {
    format!("{:016x}.bin", fnv1a(bytes))
}

/// Whether `value` is a path [`physical_path_of`] could have produced and nothing else. The only
/// legitimate source of an already-stored image's `physicalPath` is this engine's own earlier
/// output, so anything else is a caller naming a file of its own choosing.
pub fn is_physical_path(value: &str) -> bool {
    let Some(hash) = value.strip_suffix(".bin") else {
        return false;
    };
    hash.len() == 16 && hash.bytes().all(|byte| matches!(byte, b'0'..=b'9' | b'a'..=b'f'))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn hashes_identical_bytes_to_the_same_path() {
        assert_eq!(physical_path_of(b"hello"), physical_path_of(b"hello"));
    }

    #[test]
    fn hashes_different_bytes_to_different_paths() {
        assert_ne!(physical_path_of(b"hello"), physical_path_of(b"world"));
    }

    #[test]
    fn recognises_its_own_output_and_nothing_else_as_a_physical_path() {
        assert!(is_physical_path(&physical_path_of(b"hello")));
        assert!(is_physical_path("0123456789abcdef.bin"));

        assert!(!is_physical_path("0123456789ABCDEF.bin"));
        assert!(!is_physical_path("0123456789abcde.bin"));
        assert!(!is_physical_path("0123456789abcdef0.bin"));
        assert!(!is_physical_path("0123456789abcdef.bin.bin"));
        assert!(!is_physical_path("../../victim/secret.txt"));
        assert!(!is_physical_path("../0123456789abcdef.bin"));
        assert!(!is_physical_path("/etc/0123456789abcdef.bin"));
        assert!(!is_physical_path("0123456789abcdef.bin/x"));
        assert!(!is_physical_path(".bin"));
        assert!(!is_physical_path(""));
    }
}