use crate::result::{Result, TransactionViewError};
#[inline(always)]
pub fn check_remaining(bytes: &[u8], offset: usize, num_bytes: usize) -> Result<()> {
if num_bytes > bytes.len().wrapping_sub(offset) {
Err(TransactionViewError::ParseError)
} else {
Ok(())
}
}
#[inline(always)]
pub fn read_byte(bytes: &[u8], offset: &mut usize) -> Result<u8> {
let value = bytes
.get(*offset)
.copied()
.ok_or(TransactionViewError::ParseError);
*offset = offset.wrapping_add(1);
value
}
#[inline(always)]
pub unsafe fn unchecked_read_byte(bytes: &[u8], offset: &mut usize) -> u8 {
let value = *bytes.get_unchecked(*offset);
*offset = offset.wrapping_add(1);
value
}
#[allow(dead_code)]
#[inline(always)]
pub fn read_compressed_u16(bytes: &[u8], offset: &mut usize) -> Result<u16> {
let mut result = 0u16;
let mut shift = 0u16;
for i in 0..3 {
let byte = *bytes
.get(offset.wrapping_add(i))
.ok_or(TransactionViewError::ParseError)?;
if (i > 0 && byte == 0) || (i == 2 && byte > 3) {
return Err(TransactionViewError::ParseError);
}
result |= ((byte & 0x7F) as u16) << shift;
shift = shift.wrapping_add(7);
if byte & 0x80 == 0 {
*offset = offset.wrapping_add(i).wrapping_add(1);
return Ok(result);
}
}
*offset = offset.wrapping_add(3);
Ok(result)
}
#[inline(always)]
pub fn optimized_read_compressed_u16(bytes: &[u8], offset: &mut usize) -> Result<u16> {
let mut result = 0u16;
let byte1 = *bytes.get(*offset).ok_or(TransactionViewError::ParseError)?;
result |= (byte1 & 0x7F) as u16;
if byte1 & 0x80 == 0 {
*offset = offset.wrapping_add(1);
return Ok(result);
}
let byte2 = *bytes
.get(offset.wrapping_add(1))
.ok_or(TransactionViewError::ParseError)?;
if byte2 == 0 || byte2 & 0x80 != 0 {
return Err(TransactionViewError::ParseError); }
result |= ((byte2 & 0x7F) as u16) << 7;
*offset = offset.wrapping_add(2);
Ok(result)
}
#[inline(always)]
pub fn advance_offset_for_array<T: Sized>(
bytes: &[u8],
offset: &mut usize,
num_elements: u16,
) -> Result<()> {
let array_len_bytes = usize::from(num_elements).wrapping_mul(core::mem::size_of::<T>());
check_remaining(bytes, *offset, array_len_bytes)?;
*offset = offset.wrapping_add(array_len_bytes);
Ok(())
}
#[inline(always)]
pub fn advance_offset_for_type<T: Sized>(bytes: &[u8], offset: &mut usize) -> Result<()> {
let type_size = core::mem::size_of::<T>();
check_remaining(bytes, *offset, type_size)?;
*offset = offset.wrapping_add(type_size);
Ok(())
}
#[inline(always)]
pub unsafe fn read_slice_data<'a, T: Sized>(
bytes: &'a [u8],
offset: &mut usize,
num_elements: u16,
) -> Result<&'a [T]> {
let current_ptr = bytes.as_ptr().add(*offset);
advance_offset_for_array::<T>(bytes, offset, num_elements)?;
Ok(unsafe { core::slice::from_raw_parts(current_ptr as *const T, usize::from(num_elements)) })
}
#[inline(always)]
pub unsafe fn unchecked_read_slice_data<'a, T: Sized>(
bytes: &'a [u8],
offset: &mut usize,
num_elements: u16,
) -> &'a [T] {
let current_ptr = bytes.as_ptr().add(*offset);
let array_len_bytes = usize::from(num_elements).wrapping_mul(core::mem::size_of::<T>());
*offset = offset.wrapping_add(array_len_bytes);
unsafe { core::slice::from_raw_parts(current_ptr as *const T, usize::from(num_elements)) }
}
#[inline(always)]
pub unsafe fn read_type<'a, T: Sized>(bytes: &'a [u8], offset: &mut usize) -> Result<&'a T> {
let current_ptr = bytes.as_ptr().add(*offset);
advance_offset_for_type::<T>(bytes, offset)?;
Ok(unsafe { &*(current_ptr as *const T) })
}
#[cfg(test)]
mod tests {
use {
super::*,
bincode::{serialize_into, DefaultOptions, Options},
solana_packet::PACKET_DATA_SIZE,
solana_short_vec::ShortU16,
};
#[test]
fn test_check_remaining() {
assert!(check_remaining(&[], 0, 0).is_ok());
assert!(check_remaining(&[], 0, 1).is_err());
assert!(check_remaining(&[1, 2, 3], 0, 0).is_ok());
assert!(check_remaining(&[1, 2, 3], 0, 1).is_ok());
assert!(check_remaining(&[1, 2, 3], 0, 3).is_ok());
assert!(check_remaining(&[1, 2, 3], 0, 4).is_err());
assert!(check_remaining(&[1, 2, 3], 1, 0).is_ok());
assert!(check_remaining(&[1, 2, 3], 1, 1).is_ok());
assert!(check_remaining(&[1, 2, 3], 1, 2).is_ok());
assert!(check_remaining(&[1, 2, 3], 1, usize::MAX).is_err());
}
#[test]
fn test_read_byte() {
let bytes = [5, 6, 7];
let mut offset = 0;
assert_eq!(read_byte(&bytes, &mut offset), Ok(5));
assert_eq!(offset, 1);
assert_eq!(read_byte(&bytes, &mut offset), Ok(6));
assert_eq!(offset, 2);
assert_eq!(read_byte(&bytes, &mut offset), Ok(7));
assert_eq!(offset, 3);
assert!(read_byte(&bytes, &mut offset).is_err());
}
#[test]
fn test_read_compressed_u16() {
let mut buffer = [0u8; 1024];
let options = DefaultOptions::new().with_fixint_encoding();
for value in 0..=u16::MAX {
let mut offset;
let short_u16 = ShortU16(value);
serialize_into(&mut buffer[..], &short_u16).expect("Serialization failed");
let serialized_len = options
.serialized_size(&short_u16)
.expect("Failed to get serialized size");
offset = 0;
let read_value = read_compressed_u16(&buffer, &mut offset);
assert_eq!(read_value, Ok(value), "Value mismatch for: {value}");
assert_eq!(
offset, serialized_len as usize,
"Offset mismatch for: {value}"
);
}
assert_eq!(Ok(0), read_compressed_u16(&[0; 3], &mut 0));
assert!(read_compressed_u16(&[0xFF, 0xFF, 0x04], &mut 0).is_err());
assert_eq!(
read_compressed_u16(&[0xFF, 0xFF, 0x03], &mut 0),
Ok(u16::MAX)
);
assert!(read_compressed_u16(&[u8::MAX; 1], &mut 0).is_err());
assert!(read_compressed_u16(&[u8::MAX; 2], &mut 0).is_err());
assert!(read_compressed_u16(&[0x81, 0x80, 0x00], &mut 0).is_err());
}
#[test]
fn test_optimized_read_compressed_u16() {
let mut buffer = [0u8; 1024];
let options = DefaultOptions::new().with_fixint_encoding();
for value in 0..=PACKET_DATA_SIZE as u16 {
let mut offset;
let short_u16 = ShortU16(value);
serialize_into(&mut buffer[..], &short_u16).expect("Serialization failed");
let serialized_len = options
.serialized_size(&short_u16)
.expect("Failed to get serialized size");
offset = 0;
let read_value = optimized_read_compressed_u16(&buffer, &mut offset);
assert_eq!(read_value, Ok(value), "Value mismatch for: {value}");
assert_eq!(
offset, serialized_len as usize,
"Offset mismatch for: {value}"
);
}
assert_eq!(Ok(0), optimized_read_compressed_u16(&[0; 3], &mut 0));
assert!(optimized_read_compressed_u16(&[0xFF, 0xFF, 0x04], &mut 0).is_err());
assert!(optimized_read_compressed_u16(&[0xFF, 0x80], &mut 0).is_err());
assert!(optimized_read_compressed_u16(&[u8::MAX; 1], &mut 0).is_err());
assert!(optimized_read_compressed_u16(&[u8::MAX; 2], &mut 0).is_err());
assert!(optimized_read_compressed_u16(&[0x81, 0x00], &mut 0).is_err());
}
#[test]
fn test_advance_offset_for_array() {
#[repr(C)]
struct MyStruct {
_a: u8,
_b: u8,
}
const _: () = assert!(core::mem::size_of::<MyStruct>() == 2);
let bytes = [0u8; 1];
let mut offset = 0;
assert!(advance_offset_for_array::<MyStruct>(&bytes, &mut offset, 1).is_err());
let bytes = [0u8; 4];
let mut offset = 0;
assert!(advance_offset_for_array::<MyStruct>(&bytes, &mut offset, 2).is_ok());
assert_eq!(offset, 4);
}
#[test]
fn test_advance_offset_for_type() {
#[repr(C)]
struct MyStruct {
_a: u8,
_b: u8,
}
const _: () = assert!(core::mem::size_of::<MyStruct>() == 2);
let bytes = [0u8; 1];
let mut offset = 0;
assert!(advance_offset_for_type::<MyStruct>(&bytes, &mut offset).is_err());
let bytes = [0u8; 4];
let mut offset = 0;
assert!(advance_offset_for_type::<MyStruct>(&bytes, &mut offset).is_ok());
assert_eq!(offset, 2);
}
}