use core::array::ArrayTrait; use core::fmt::{Debug, Formatter}; use super::util::one_shift_left_bytes_u256; /// A byte array with storage format similar to `core::ByteArray`, but /// suitable for reading data from it. #[derive(Drop, Clone, PartialEq, Serde)] pub struct ByteBuffer { // Number of bytes stored in the last item of `self.data` (or 0 if it's empty). num_last_bytes: u8, // Bytes in big endian. Each item except the last one stores 31 bytes. // If `num_last_bytes < 31`, unused most significant bytes of the last item will be unused. data: Array, } impl DebugByteBuffer of Debug { fn fmt(self: @ByteBuffer, ref f: Formatter) -> Result<(), core::fmt::Error> { write!(f, "ByteBuffer {{ num_last_bytes: {}, data: [", self.num_last_bytes)?; let mut data = self.data.clone(); loop { match data.pop_front() { Option::Some(v) => { write!(f, "{:?}, ", v).unwrap(); }, Option::None => { break; }, } }; write!(f, "]}}") } } impl DefaultByteBuffer of Default { fn default() -> ByteBuffer { ByteBufferImpl::new(array![], 0) } } #[generate_trait] pub impl ByteBufferImpl of ByteBufferTrait { /// Creates a byte array with the data. fn new(data: Array, num_last_bytes: u8) -> ByteBuffer { if data.len() == 0 { assert!(num_last_bytes == 0); } else { assert!(num_last_bytes > 0); assert!(num_last_bytes <= 31); let last: u256 = (*data.at(data.len() - 1)).into(); assert!( last / one_shift_left_bytes_u256(num_last_bytes) == 0, "ByteBufferImpl::new: last value is too large" ); } ByteBuffer { num_last_bytes, data } } /// Removes 31 or less bytes from the start of the array. /// Returns the value and the number of bytes. fn pop_front(ref self: ByteBuffer) -> Option<(felt252, u8)> { let item = self.data.pop_front()?; if self.data.is_empty() { let num_bytes = self.num_last_bytes; self.num_last_bytes = 0; Option::Some((item, num_bytes)) } else { Option::Some((item, 31)) } } fn len(self: @ByteBuffer) -> usize { if self.data.is_empty() { 0 } else { (self.data.len() - 1) * 31 + (*self.num_last_bytes).into() } } } #[cfg(test)] mod tests { use super::{ByteBuffer, ByteBufferImpl}; use pyth::util::array_try_into; #[test] fn empty_byte_array() { let mut array = ByteBufferImpl::new(array![], 0); assert!(array.len() == 0); assert!(array.pop_front() == Option::None); } #[test] fn byte_array_3_zeros() { let mut array = ByteBufferImpl::new(array![0], 3); assert!(array.len() == 3); assert!(array.pop_front() == Option::Some((0, 3))); assert!(array.len() == 0); assert!(array.pop_front() == Option::None); } #[test] fn byte_array_3_bytes() { let mut array = ByteBufferImpl::new(array![0x010203], 3); assert!(array.len() == 3); assert!(array.pop_front() == Option::Some((0x010203, 3))); assert!(array.len() == 0); assert!(array.pop_front() == Option::None); } #[test] fn byte_array_single_full() { let value_31_bytes = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; let mut array = ByteBufferImpl::new(array![value_31_bytes], 31); assert!(array.len() == 31); assert!(array.pop_front() == Option::Some((value_31_bytes, 31))); assert!(array.len() == 0); assert!(array.pop_front() == Option::None); } #[test] fn byte_array_two_full() { let value_31_bytes = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; let value2_31_bytes = 0x2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; let mut array = ByteBufferImpl::new(array![value_31_bytes, value2_31_bytes], 31); assert!(array.len() == 62); assert!(array.pop_front() == Option::Some((value_31_bytes, 31))); assert!(array.len() == 31); assert!(array.pop_front() == Option::Some((value2_31_bytes, 31))); assert!(array.len() == 0); assert!(array.pop_front() == Option::None); } #[test] fn byte_array_3_items() { let value_31_bytes = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; let value2_31_bytes = 0x2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; let value3_5_bytes = 0x4142434445; let mut array = ByteBufferImpl::new( array![value_31_bytes, value2_31_bytes, value3_5_bytes], 5 ); assert!(array.len() == 67); assert!(array.pop_front() == Option::Some((value_31_bytes, 31))); assert!(array.len() == 36); assert!(array.pop_front() == Option::Some((value2_31_bytes, 31))); assert!(array.len() == 5); assert!(array.pop_front() == Option::Some((value3_5_bytes, 5))); assert!(array.pop_front() == Option::None); } #[test] #[should_panic] fn byte_array_empty_invalid() { ByteBufferImpl::new(array![], 5); } #[test] #[should_panic] fn byte_array_last_too_large() { ByteBufferImpl::new(array![1, 2, 3], 35); } #[test] #[should_panic] fn byte_array_last_zero_invalid() { ByteBufferImpl::new(array![1, 2, 0], 0); } #[test] #[should_panic] fn byte_array_last_too_many_bytes() { ByteBufferImpl::new(array![1, 2, 0x010203], 2); } }