mx-cache 0.1.0

Shared cache utilities (local + Redis) for MultiversX Rust services.
Documentation
//! Shared utility functions for cache key operations.

use bytes::Bytes;

/// Creates a namespaced key by prefixing with the given prefix.
///
/// Uses a stack buffer for small keys (<=96 bytes) to avoid heap allocation
/// during key construction. Returns `Bytes` which is cheap to clone into
/// async blocks (just an atomic ref count increment after the initial copy).
///
/// # Arguments
///
/// * `prefix` - The namespace prefix to prepend
/// * `key` - The key to namespace
///
/// # Returns
///
/// A `Bytes` containing the prefixed key.
#[inline]
pub fn namespaced(prefix: &str, key: &[u8]) -> Bytes {
    let needed = prefix.len() + key.len();

    // Use stack buffer for small keys to avoid heap allocation during construction
    if needed <= 96 {
        let mut stack = [0u8; 96];
        stack[..prefix.len()].copy_from_slice(prefix.as_bytes());
        stack[prefix.len()..needed].copy_from_slice(key);
        // Single copy from stack to Bytes (Bytes manages its own ref-counted buffer)
        return Bytes::copy_from_slice(&stack[..needed]);
    }

    // For larger keys, build directly into a Vec and convert to Bytes
    let mut buf = Vec::with_capacity(needed);
    buf.extend_from_slice(prefix.as_bytes());
    buf.extend_from_slice(key);
    Bytes::from(buf)
}

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

    #[test]
    fn test_namespaced_small_key() {
        let result = namespaced("prefix:", b"key");
        assert_eq!(result.as_ref(), b"prefix:key");
    }

    #[test]
    fn test_namespaced_empty_prefix() {
        let result = namespaced("", b"key");
        assert_eq!(result.as_ref(), b"key");
    }

    #[test]
    fn test_namespaced_empty_key() {
        let result = namespaced("prefix:", b"");
        assert_eq!(result.as_ref(), b"prefix:");
    }

    #[test]
    fn test_namespaced_large_key() {
        let prefix = "prefix:";
        let key = vec![b'x'; 100]; // Large key to exceed 96-byte stack buffer
        let result = namespaced(prefix, &key);
        assert_eq!(result.len(), prefix.len() + 100);
        assert!(result.starts_with(b"prefix:"));
    }

    #[test]
    fn test_namespaced_exactly_96_bytes() {
        let prefix = "p:";
        let key = vec![b'k'; 94]; // 2 + 94 = 96 bytes exactly
        let result = namespaced(prefix, &key);
        assert_eq!(result.len(), 96);
    }
}