protocache-core 0.1.1

Core zero-copy runtime and encoding primitives for ProtoCache
Documentation
use crate::{
    ArrayView, Buffer, MapView, MessageView, StringView, Unit,
    build_perfect_hash_index_with_positions, detect_array_with, detect_map_with, detect_slice_end,
    serialize_map, serialize_scalar, serialize_str,
};

#[test]
fn reads_scalar_message() {
    let words = [0x0000_0100u32, 42u32];
    let view = MessageView::new(&words).unwrap();
    assert_eq!(view.scalar::<i32>(0), Some(42));
    assert!(view.scalar::<i32>(1).is_none());
}

#[test]
fn reads_inline_string() {
    let words = [0x0000_0100u32, 0x0069_6808u32];
    let view = MessageView::new(&words).unwrap();
    let string = view.string(0).unwrap();
    assert_eq!(string.as_str(), Some("hi"));
}

#[test]
fn empty_views_are_rejected() {
    assert!(MessageView::new(&[]).is_none());
    assert!(StringView::new(&[]).is_none());
    assert!(ArrayView::new(&[]).is_none());
    assert!(MapView::new(&[]).is_none());
}

#[test]
fn invalid_map_header_is_rejected() {
    let words = [0u32];
    assert!(MapView::new(&words).is_none());
}

#[test]
fn detect_returns_precise_object_ranges() {
    let words = [0x0000_0100u32, 0x0069_6808u32];
    let root = MessageView::new(&words).unwrap();

    assert_eq!(MessageView::detect(&words).unwrap(), &words[..2]);
    assert_eq!(
        root.field(0).unwrap().detect_scalar().unwrap(),
        &words[1..2]
    );

    let string_msg = [0x0000_0100u32, 0x0000_0007u32, 0x0069_6808u32];
    let root = MessageView::new(&string_msg).unwrap();
    assert_eq!(
        root.field(0).unwrap().detect_string().unwrap(),
        &string_msg[2..3]
    );
    assert_eq!(
        StringView::detect(&string_msg[2..]).unwrap(),
        &string_msg[2..3]
    );
}

#[test]
fn detect_array_stops_after_last_extending_item() {
    let mut buffer = Buffer::new();
    let short = serialize_str("abc", &mut buffer).unwrap();
    let long = serialize_str("this string spills past the array body", &mut buffer).unwrap();
    let _array = crate::serialize_array(&[short, long], &mut buffer).unwrap();
    let words = buffer.view();

    let mut calls = 0usize;
    let detected = detect_array_with(words, |field| {
        calls += 1;
        field.detect_string()
    })
    .unwrap();

    assert!(detected.len() > ArrayView::detect_len(words).unwrap());
    assert_eq!(calls, 1);
}

#[test]
fn detect_map_stops_after_last_extending_value() {
    let keys_src = vec![b"k0".to_vec(), b"k1".to_vec()];
    let (index, positions) = build_perfect_hash_index_with_positions(&keys_src).unwrap();

    let mut buffer = Buffer::new();
    let key0 = serialize_str("k0", &mut buffer).unwrap();
    let key1 = serialize_str("k1", &mut buffer).unwrap();
    let short = serialize_str("x", &mut buffer).unwrap();
    let long = serialize_str("this map value spills past the map body", &mut buffer).unwrap();

    let last = positions
        .iter()
        .enumerate()
        .max_by_key(|(_, pos)| *pos)
        .map(|(idx, _)| idx)
        .unwrap();

    let mut keys = vec![Unit::empty(); 2];
    let mut values = vec![Unit::empty(); 2];
    keys[positions[0]] = key0;
    keys[positions[1]] = key1;
    values[positions[last]] = long;
    values[positions[1 - last]] = short;

    let _map = serialize_map(&index, &keys, &values, &mut buffer).unwrap();
    let words = buffer.view();

    let mut key_calls = 0usize;
    let mut value_calls = 0usize;
    let detected = detect_map_with(
        words,
        |field| {
            key_calls += 1;
            field.detect_string()
        },
        |field| {
            value_calls += 1;
            field.detect_string()
        },
    )
    .unwrap();

    assert!(detected.len() > MapView::detect_len(words).unwrap());
    assert_eq!(value_calls, 1);
    assert_eq!(key_calls, 0);
}

#[test]
fn detect_helpers_reject_slices_from_another_allocation() {
    let foreign = [123u32];
    let words = [10u32, 20, 30];
    let mut end = 1usize;
    assert!(detect_slice_end(&words, &foreign, &mut end).is_none());
    assert_eq!(end, 1);

    let mut array_buffer = Buffer::new();
    let item = serialize_scalar::<u32>(7);
    let _array = crate::serialize_array(&[item], &mut array_buffer).unwrap();
    assert!(detect_array_with(array_buffer.view(), |_| Some(&foreign)).is_none());

    let keys_src = [b"key".as_slice()];
    let (index, _) = build_perfect_hash_index_with_positions(&keys_src).unwrap();
    let mut map_buffer = Buffer::new();
    let key = serialize_str("key", &mut map_buffer).unwrap();
    let value = serialize_scalar::<u32>(9);
    let _map = serialize_map(&index, &[key], &[value], &mut map_buffer).unwrap();
    assert!(detect_map_with(map_buffer.view(), |_| Some(&foreign), |_| Some(&foreign),).is_none());
}

#[test]
fn detect_slice_end_rejects_a_sibling_slice_outside_words() {
    let backing = [1u32, 2, 3, 4];
    let words = &backing[2..];
    let detected = &backing[..1];
    let mut end = 0usize;
    assert!(detect_slice_end(words, detected, &mut end).is_none());
    assert_eq!(end, 0);
}