pkbuffer 0.7.0

Buffer objects made for arbitrary casting and addressing!
Documentation
use pkbuffer::*;
use hex;

#[test]
fn test_ptrbuffer() {
    let data = hex::decode("deadbeefabad1deadeadbea7defaced1").unwrap();
    let buffer = PtrBuffer::new(data.as_ptr(), data.len());

    assert_eq!(buffer.as_ptr(), data.as_ptr());
    assert_eq!(buffer.len(), data.len());
    unsafe { assert_eq!(buffer.eob(), data.as_ptr().add(data.len())); }

    // Test read_val
    let byte_result = buffer.read_val::<i8>(0);
    assert!(byte_result.is_ok());
    assert!(byte_result.unwrap() == -34);

    let error_result = buffer.read_val::<i8>(buffer.len());
    assert!(error_result.is_err());

    // Test raw byte reads
    let read_result = buffer.read(0, 9);
    assert!(read_result.is_ok());
    let bytes = read_result.unwrap();
    assert_eq!(bytes[0..3], [0xDE, 0xAD, 0xBE]);

    let read_result = buffer.read(8, 4);
    assert!(read_result.is_ok());
    assert_eq!(read_result.unwrap(), [0xDE, 0xAD, 0xBE, 0xA7]);

    let read_result = buffer.read(0xC, 4);
    assert!(read_result.is_ok());
    assert_eq!(read_result.unwrap(), [0xDE, 0xFA, 0xCE, 0xD1]);

    let itered_vec = buffer.iter().copied().collect::<Vec<u8>>();
    assert_eq!(itered_vec, data);

    // Search tests
    let search_results = buffer.search([0xDE, 0xFA, 0xCE, 0xD1]);
    assert!(search_results.is_ok());
    assert!(search_results.unwrap().next().is_some());

    // Search for bytes (little-endian representation of u32)
    let search_u32_bytes: [u8; 4] = 0xFACEBABE_u32.to_le_bytes();
    let search_results = buffer.search(search_u32_bytes);
    assert!(search_results.is_ok());
    assert!(search_results.unwrap().next().is_none());

    let search_u32_bytes: [u8; 4] = 0xADABEFBE_u32.to_le_bytes();
    let search_results = buffer.search(search_u32_bytes);
    assert!(search_results.is_ok());
    assert!(search_results.unwrap().next().is_some());

    // Search for slice of bytes (pattern that exists in data)
    let search_bytes: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
    let search_results = buffer.search(search_bytes);
    assert!(search_results.is_ok());
    assert!(search_results.unwrap().next().is_some());

    let dynamic_test = hex::decode("ff2763582764ff276488654327384858642764").unwrap();
    let dynamic_buffer = PtrBuffer::new(dynamic_test.as_ptr(), dynamic_test.len());
    let dynamic_search = dynamic_buffer.search_dynamic(&[None, Some(0x27), Some(0x64), None, Some(0x27), Some(0x64)]);

    assert!(dynamic_search.is_ok());

    let result = dynamic_search.unwrap().next();
    assert!(result.is_some());
    assert_eq!(result.unwrap(), 3);

    assert!(buffer.contains([0xDE, 0xAD, 0xBE, 0xA7]));

    // Contains using byte representation
    let contains_u32_bytes: [u8; 4] = 0xFACEBABE_u32.to_le_bytes();
    assert!(!buffer.contains(contains_u32_bytes));
    let contains_u32_bytes: [u8; 4] = 0xEA1DADAB_u32.to_le_bytes();
    assert!(buffer.contains(contains_u32_bytes));
    // The bytes at offset 8: deadbea7defaced1 = [DE AD BE A7 DE FA CE D1]
    let contains_slice: [u8; 8] = [0xDE, 0xAD, 0xBE, 0xA7, 0xDE, 0xFA, 0xCE, 0xD1];
    assert!(buffer.contains(contains_slice));

    assert_eq!(buffer[0x8..0xC], [0xDE, 0xAD, 0xBE, 0xA7]);
}

#[test]
fn test_vecbuffer() {
    let data = hex::decode("deadbeefabad1deadeadbea7defaced1").unwrap();
    let mut buffer = VecBuffer::from_data(&data);

    assert!(buffer.write(0, &[0xFA, 0xCE, 0xBA, 0xBE]).is_ok());
    assert!(!buffer.contains([0xDE, 0xAD, 0xBE, 0xEF]));

    assert!(buffer.write_val::<u32>(4, &0xEFBEADDE).is_ok());
    let write_u32_bytes: [u8; 4] = 0xEFBEADDE_u32.to_le_bytes();
    assert!(buffer.contains(write_u32_bytes));

    buffer.append(&vec![0xAB, 0xAD, 0x1D, 0xEA]);
    assert!(buffer.contains([0xAB, 0xAD, 0x1D, 0xEA]));

    let rhs = buffer.split_off(0x8);
    assert!(!buffer.contains([0xAB, 0xAD, 0x1D, 0xEA]));

    buffer.resize(0xC, 0x00);
    assert!(buffer.write_val::<u32>(0x8, &0x74EEFFC0).is_ok());

    buffer.append(&rhs);
    assert!(buffer.contains([0xAB, 0xAD, 0x1D, 0xEA]));
    assert!(buffer.contains([0xC0, 0xFF, 0xEE, 0x74]));

    assert_eq!(buffer, hex::decode("facebabedeadbeefc0ffee74deadbea7defaced1abad1dea").unwrap());
}

#[test]
fn test_read_write_val() {
    let mut buffer = VecBuffer::with_initial_size(16);

    // Write and read various types
    buffer.write_val::<u8>(0, &0xAB).unwrap();
    assert_eq!(buffer.read_val::<u8>(0).unwrap(), 0xAB);

    buffer.write_val::<u16>(1, &0xCDAB).unwrap();
    assert_eq!(buffer.read_val::<u16>(1).unwrap(), 0xCDAB);

    buffer.write_val::<u32>(3, &0xEFBEADDE).unwrap();
    assert_eq!(buffer.read_val::<u32>(3).unwrap(), 0xEFBEADDE);

    buffer.write_val::<u64>(7, &0x123456789ABCDEF0).unwrap();
    assert_eq!(buffer.read_val::<u64>(7).unwrap(), 0x123456789ABCDEF0);

    // Test out of bounds
    assert!(buffer.read_val::<u64>(10).is_err());
    assert!(buffer.write_val::<u64>(10, &0x0).is_err());
}

#[test]
fn test_append_val() {
    let mut buffer = VecBuffer::new();

    buffer.append_val::<u8>(&0x01);
    buffer.append_val::<u16>(&0x0302);
    buffer.append_val::<u32>(&0x07060504);
    buffer.append_slice_val::<u8>(&[0x08, 0x09, 0x0A]);

    assert_eq!(buffer, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
}

#[test]
fn test_aligned_ref() {
    let data = hex::decode("deadbeefabad1deadeadbea7defaced1").unwrap();
    let buffer = VecBuffer::from_data(&data);

    // Offset 0 is aligned for any type
    let _val: &u32 = buffer.get_aligned_ref(0).unwrap();
    assert_eq!(*_val, 0xEFBEADDE);

    // Offset 4 is aligned for u32
    let val: &u32 = buffer.get_aligned_ref(4).unwrap();
    assert_eq!(*val, 0xEA1DADAB);

    // Offset 8 is aligned for u64
    let val: &u64 = buffer.get_aligned_ref(8).unwrap();
    assert_eq!(*val, 0xD1CEFADEA7BEADDE);

    // Test that read-only reference does not allow mutation
    let val: &u32 = buffer.get_aligned_ref(0).unwrap();
    // val is &u32, not &mut u32, so this won't compile if uncommented:
    // *val = 0;
}

#[test]
fn test_aligned_ref_misaligned() {
    let data = hex::decode("deadbeefabad1deadeadbea7defaced1").unwrap();
    let buffer = VecBuffer::from_data(&data);

    // Offset 1 is not aligned for u32 (requires 4-byte alignment)
    let result: Result<&u32, Error> = buffer.get_aligned_ref(1);
    assert!(result.is_err());
    if let Err(Error::AlignmentMismatch(offset, align)) = result {
        assert_eq!(offset, 1);
        assert_eq!(align, 4);
    } else {
        panic!("Expected AlignmentMismatch error");
    }

    // Offset 2 is not aligned for u64 (requires 8-byte alignment)
    let result: Result<&u64, Error> = buffer.get_aligned_ref(2);
    assert!(result.is_err());
    if let Err(Error::AlignmentMismatch(offset, align)) = result {
        assert_eq!(offset, 2);
        assert_eq!(align, 8);
    } else {
        panic!("Expected AlignmentMismatch error");
    }

    // Offset 3 is not aligned for u32
    let result: Result<&u32, Error> = buffer.get_aligned_ref(3);
    assert!(result.is_err());
    if let Err(Error::AlignmentMismatch(_, _)) = result {
        // expected
    } else {
        panic!("Expected AlignmentMismatch error");
    }
}

#[test]
fn test_aligned_mut() {
    let mut buffer = VecBuffer::with_initial_size(16);

    // Write initial values at aligned offsets
    buffer.write_val::<u32>(0, &0xDEADBEEF).unwrap();
    buffer.write_val::<u32>(4, &0xCAFEBABE).unwrap();
    buffer.write_val::<u64>(8, &0x123456789ABCDEF0).unwrap();

    // Get mutable reference and modify
    let val: &mut u32 = buffer.get_aligned_mut(0).unwrap();
    *val = 0xFEEDFACE;

    // Verify change persisted
    let val: &u32 = buffer.get_aligned_ref(0).unwrap();
    assert_eq!(*val, 0xFEEDFACE);

    // Other values unchanged
    let val: &u32 = buffer.get_aligned_ref(4).unwrap();
    assert_eq!(*val, 0xCAFEBABE);

    // Modify at different aligned offset
    let val: &mut u64 = buffer.get_aligned_mut(8).unwrap();
    *val = 0xABCD123456789DEF;

    let val: &u64 = buffer.get_aligned_ref(8).unwrap();
    assert_eq!(*val, 0xABCD123456789DEF);
}

#[test]
fn test_aligned_mut_misaligned() {
    let mut buffer = VecBuffer::with_initial_size(16);

    // Offset 1 is not aligned for u32
    let result: Result<&mut u32, Error> = buffer.get_aligned_mut(1);
    assert!(result.is_err());
    if let Err(Error::AlignmentMismatch(offset, align)) = result {
        assert_eq!(offset, 1);
        assert_eq!(align, 4);
    } else {
        panic!("Expected AlignmentMismatch error");
    }

    // Offset 5 is not aligned for u32 (5 % 4 != 0)
    let result: Result<&mut u32, Error> = buffer.get_aligned_mut(5);
    assert!(result.is_err());
    if let Err(Error::AlignmentMismatch(_, _)) = result {
        // expected
    } else {
        panic!("Expected AlignmentMismatch error");
    }

    // Offset 2 is not aligned for u64
    let result: Result<&mut u64, Error> = buffer.get_aligned_mut(2);
    assert!(result.is_err());
    if let Err(Error::AlignmentMismatch(_, _)) = result {
        // expected
    } else {
        panic!("Expected AlignmentMismatch error");
    }
}

#[test]
fn test_aligned_out_of_bounds() {
    let mut buffer = VecBuffer::with_initial_size(16);

    // Offset 14 is misaligned for u32, so returns AlignmentMismatch before bounds check
    let result: Result<&u32, Error> = buffer.get_aligned_ref(14);
    assert!(result.is_err());
    assert!(matches!(result, Err(Error::AlignmentMismatch(_, _))));

    // Offset 16 is aligned for u32 but 16+4=20 > 16, so returns OutOfBounds
    let result: Result<&u32, Error> = buffer.get_aligned_ref(16);
    assert!(result.is_err());
    if let Err(Error::OutOfBounds(_, _)) = result {
        // expected
    } else {
        panic!("Expected OutOfBounds error");
    }

    // Offset 12 is aligned for u32 (12+4=16) - at boundary, works
    let result: Result<&u32, Error> = buffer.get_aligned_ref(12);
    assert!(result.is_ok());

    // Offset 8 is aligned for u64 (8+8=16) - at boundary for u64, works
    let result: Result<&u64, Error> = buffer.get_aligned_ref(8);
    assert!(result.is_ok());

    // Offset 12 is misaligned for u64 (12 % 8 = 4), returns AlignmentMismatch
    let result: Result<&mut u64, Error> = buffer.get_aligned_mut(12);
    assert!(result.is_err());
    assert!(matches!(result, Err(Error::AlignmentMismatch(_, _))));

    // Offset 16 is aligned for u64 (16 % 8 = 0) but 16+8=24 > 16, returns OutOfBounds
    let result: Result<&mut u64, Error> = buffer.get_aligned_mut(16);
    assert!(result.is_err());
    if let Err(Error::OutOfBounds(_, _)) = result {
        // expected
    } else {
        panic!("Expected OutOfBounds error");
    }

    // Offset 15 with u64 (15+8=23 > 16) - misaligned AND out of bounds
    let result: Result<&u64, Error> = buffer.get_aligned_ref(15);
    assert!(result.is_err());
    // Returns AlignmentMismatch first (alignment check happens before bounds)
    assert!(matches!(result, Err(Error::AlignmentMismatch(_, _))));
}