diff --git a/src/lib.rs b/src/lib.rs index 3fad8b9..dbfd6f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,18 @@ pub mod binary { - /// A binary stream for reading and writing primitive types + /// A high-performance binary stream for reading and writing primitive types /// in both big-endian and little-endian byte order. /// /// Commonly used for RakNet & Minecraft Bedrock protocol data. pub struct Stream { buffer: Vec, - offset: u32 + offset: usize, } impl Stream { /// Creates a new stream from an existing buffer and offset. #[inline] pub fn new(buffer: Vec, offset: u32) -> Self { - Self { buffer, offset } + Self { buffer, offset: offset as usize } } /// Creates an empty stream with a specified capacity. @@ -33,25 +33,21 @@ pub mod binary { /// Returns `true` if the stream has reached the end of the buffer. #[inline] pub fn feof(&self) -> bool { - self.offset >= self.buffer.len() as u32 + self.offset >= self.buffer.len() } /// Sets the read/write offset. - /// - /// Panics if the offset is greater than the buffer length. #[inline] pub fn set_offset(&mut self, offset: u32) { - if offset <= self.buffer.len() as u32 { - self.offset = offset; - } else { - panic!("Offset out of bounds"); - } + let offset = offset as usize; + assert!(offset <= self.buffer.len(), "Offset out of bounds"); + self.offset = offset; } /// Returns the current read/write offset. #[inline] pub fn get_offset(&self) -> u32 { - self.offset + self.offset as u32 } /// Returns the entire buffer as a byte slice. @@ -60,33 +56,51 @@ pub mod binary { &self.buffer } + /// Internal: Returns a slice without allocation (zero-copy) + #[inline] + fn get_slice(&mut self, length: usize) -> &[u8] { + let start = self.offset; + let end = start + length; + + assert!( + end <= self.buffer.len(), + "Buffer underflow: offset={}, length={}, buffer_size={}, trying to read until={}", + start, length, self.buffer.len(), end + ); + + self.offset = end; + &self.buffer[start..end] + } + /// Reads a specific number of bytes and advances the offset. /// - /// Panics if there are not enough bytes remaining. + /// **Note**: This allocates a Vec. For better performance, consider using + /// methods that work directly with slices. + #[inline] pub fn get(&mut self, length: u32) -> Vec { - let end_index: usize = (self.offset + length) as usize; - - if end_index <= self.buffer.len() { - let start_index: usize = self.offset as usize; - self.offset += length; - self.buffer[start_index..end_index].to_vec() - } else { - panic!( - "The specified range is invalid. Offset: {}, Length: {}, Buffer size: {}, Trying to read until: {}", - self.offset, length, self.buffer.len(), end_index - ); - } + self.get_slice(length as usize).to_vec() } - /// Returns the remaining unread bytes. - /// - /// Panics if the stream is already at EOF. + /// Returns the remaining unread bytes as a slice (zero-copy). + #[inline] + pub fn remaining_slice(&self) -> &[u8] { + &self.buffer[self.offset..] + } + + /// Returns the remaining unread bytes (allocates a Vec). + #[inline] pub fn get_remaining(&self) -> Vec { - if self.offset >= self.buffer.len() as u32 { - panic!("Error get_remaining(): No bytes left to read"); - } else { - self.buffer[(self.offset as usize)..].to_vec() - } + assert!( + self.offset < self.buffer.len(), + "Error get_remaining(): No bytes left to read" + ); + self.buffer[self.offset..].to_vec() + } + + /// Writes a raw byte slice to the buffer. + #[inline] + pub fn put_slice(&mut self, value: &[u8]) { + self.buffer.extend_from_slice(value); } /// Writes a raw byte vector to the buffer. @@ -95,11 +109,10 @@ pub mod binary { self.buffer.extend(value); } - /// Reads a single byte and returns `true` if it’s nonzero. + /// Reads a single byte and returns `true` if it's nonzero. #[inline] pub fn get_bool(&mut self) -> bool { - let bytes = self.get(1); - bytes[0] != 0 + self.get_byte() != 0 } /// Writes a boolean value (`0x01` for true, `0x00` for false). @@ -111,7 +124,9 @@ pub mod binary { /// Reads a single unsigned byte. #[inline] pub fn get_byte(&mut self) -> u8 { - self.get(1)[0] + let byte = self.buffer[self.offset]; + self.offset += 1; + byte } /// Writes a single unsigned byte. @@ -125,14 +140,14 @@ pub mod binary { /// Reads an unsigned 16-bit integer (big-endian). #[inline] pub fn get_u16_be(&mut self) -> u16 { - let bytes = self.get(2); + let bytes = self.get_slice(2); u16::from_be_bytes([bytes[0], bytes[1]]) } /// Reads a signed 16-bit integer (big-endian). #[inline] pub fn get_i16_be(&mut self) -> i16 { - let bytes = self.get(2); + let bytes = self.get_slice(2); i16::from_be_bytes([bytes[0], bytes[1]]) } @@ -151,14 +166,14 @@ pub mod binary { /// Reads an unsigned 16-bit integer (little-endian). #[inline] pub fn get_u16_le(&mut self) -> u16 { - let bytes = self.get(2); + let bytes = self.get_slice(2); u16::from_le_bytes([bytes[0], bytes[1]]) } /// Reads a signed 16-bit integer (little-endian). #[inline] pub fn get_i16_le(&mut self) -> i16 { - let bytes = self.get(2); + let bytes = self.get_slice(2); i16::from_le_bytes([bytes[0], bytes[1]]) } @@ -179,7 +194,7 @@ pub mod binary { /// Reads a 24-bit unsigned integer (big-endian). #[inline] pub fn get_u24_be(&mut self) -> u32 { - let bytes = self.get(3); + let bytes = self.get_slice(3); u32::from_be_bytes([0, bytes[0], bytes[1], bytes[2]]) } @@ -193,7 +208,7 @@ pub mod binary { /// Reads a 24-bit unsigned integer (little-endian). #[inline] pub fn get_u24_le(&mut self) -> u32 { - let bytes = self.get(3); + let bytes = self.get_slice(3); u32::from_le_bytes([bytes[0], bytes[1], bytes[2], 0]) } @@ -209,8 +224,8 @@ pub mod binary { /// Reads an unsigned 32-bit integer (big-endian). #[inline] pub fn get_u32_be(&mut self) -> u32 { - let bytes = self.get(4); - u32::from_be_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(4); + u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) } /// Writes an unsigned 32-bit integer (big-endian). @@ -222,8 +237,8 @@ pub mod binary { /// Reads an unsigned 32-bit integer (little-endian). #[inline] pub fn get_u32_le(&mut self) -> u32 { - let bytes = self.get(4); - u32::from_le_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(4); + u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) } /// Writes an unsigned 32-bit integer (little-endian). @@ -235,8 +250,8 @@ pub mod binary { /// Reads a signed 32-bit integer (big-endian). #[inline] pub fn get_i32_be(&mut self) -> i32 { - let bytes = self.get(4); - i32::from_be_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(4); + i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) } /// Writes a signed 32-bit integer (big-endian). @@ -248,8 +263,8 @@ pub mod binary { /// Reads a signed 32-bit integer (little-endian). #[inline] pub fn get_i32_le(&mut self) -> i32 { - let bytes = self.get(4); - i32::from_le_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(4); + i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) } /// Writes a signed 32-bit integer (little-endian). @@ -263,8 +278,8 @@ pub mod binary { /// Reads a 32-bit floating-point number (big-endian). #[inline] pub fn get_f32_be(&mut self) -> f32 { - let bytes = self.get(4); - f32::from_be_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(4); + f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) } /// Writes a 32-bit floating-point number (big-endian). @@ -276,8 +291,8 @@ pub mod binary { /// Reads a 32-bit floating-point number (little-endian). #[inline] pub fn get_f32_le(&mut self) -> f32 { - let bytes = self.get(4); - f32::from_le_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(4); + f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) } /// Writes a 32-bit floating-point number (little-endian). @@ -291,8 +306,11 @@ pub mod binary { /// Reads a 64-bit double-precision float (big-endian). #[inline] pub fn get_f64_be(&mut self) -> f64 { - let bytes = self.get(8); - f64::from_be_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(8); + f64::from_be_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]) } /// Writes a 64-bit double-precision float (big-endian). @@ -304,8 +322,11 @@ pub mod binary { /// Reads a 64-bit double-precision float (little-endian). #[inline] pub fn get_f64_le(&mut self) -> f64 { - let bytes = self.get(8); - f64::from_le_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(8); + f64::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]) } /// Writes a 64-bit double-precision float (little-endian). @@ -319,8 +340,11 @@ pub mod binary { /// Reads a signed 64-bit integer (big-endian). #[inline] pub fn get_i64_be(&mut self) -> i64 { - let bytes = self.get(8); - i64::from_be_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(8); + i64::from_be_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]) } /// Writes a signed 64-bit integer (big-endian). @@ -332,8 +356,11 @@ pub mod binary { /// Reads a signed 64-bit integer (little-endian). #[inline] pub fn get_i64_le(&mut self) -> i64 { - let bytes = self.get(8); - i64::from_le_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(8); + i64::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]) } /// Writes a signed 64-bit integer (little-endian). @@ -345,8 +372,11 @@ pub mod binary { /// Reads an unsigned 64-bit integer (big-endian). #[inline] pub fn get_u64_be(&mut self) -> u64 { - let bytes = self.get(8); - u64::from_be_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(8); + u64::from_be_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]) } /// Writes an unsigned 64-bit integer (big-endian). @@ -358,8 +388,11 @@ pub mod binary { /// Reads an unsigned 64-bit integer (little-endian). #[inline] pub fn get_u64_le(&mut self) -> u64 { - let bytes = self.get(8); - u64::from_le_bytes(bytes.try_into().unwrap()) + let bytes = self.get_slice(8); + u64::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]) } /// Writes an unsigned 64-bit integer (little-endian). @@ -371,19 +404,27 @@ pub mod binary { // ===== Variable-length integers ===== /// Reads an unsigned variable-length integer (VarInt). + #[inline] pub fn get_var_u32(&mut self) -> u32 { let mut value = 0u32; - for i in 0..5 { + let mut shift = 0; + + for _ in 0..5 { let b = self.get_byte(); - value |= ((b & 0x7f) as u32) << (i * 7); + value |= ((b & 0x7f) as u32) << shift; + if b & 0x80 == 0 { return value; } + + shift += 7; } + panic!("VarInt did not terminate after 5 bytes!"); } /// Writes an unsigned variable-length integer (VarInt). + #[inline] pub fn put_var_u32(&mut self, mut value: u32) { loop { if value >= 0x80 { @@ -418,19 +459,27 @@ pub mod binary { } /// Reads an unsigned 64-bit variable-length integer (VarLong). + #[inline] pub fn get_var_u64(&mut self) -> u64 { let mut value = 0u64; - for i in 0..10 { + let mut shift = 0; + + for _ in 0..10 { let b = self.get_byte(); - value |= ((b & 0x7f) as u64) << (i * 7); + value |= ((b & 0x7f) as u64) << shift; + if b & 0x80 == 0 { return value; } + + shift += 7; } + panic!("VarLong did not terminate after 10 bytes!"); } /// Writes an unsigned 64-bit variable-length integer (VarLong). + #[inline] pub fn put_var_u64(&mut self, mut value: u64) { loop { if value >= 0x80 { @@ -464,4 +513,4 @@ pub mod binary { self.put_var_u64(encoded); } } -} +} \ No newline at end of file