diff --git a/Cargo.toml b/Cargo.toml index 7cdb485..7a14e24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bytebuffer" -version = "2.1.1" +version = "2.2.0" authors = ["Terah ", "Shiroy "] description = "A bytebuffer for networking and binary protocols" homepage = "https://github.com/terahlunah/bytebuffer" @@ -12,6 +12,10 @@ edition = "2021" [lib] name = "bytebuffer" +[features] +default = [] +half = ["dep:half"] + [dependencies] byteorder = "1" - +half = { version = "2.3.1", optional = true } \ No newline at end of file diff --git a/README.md b/README.md index 2e370fe..7870483 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ let _ = reader.read_bit(); let _ = reader.read_bits(3); ``` +Also support [crate half](https://crates.io/crates/half/) 16 bits floats with +```rust +features = ["half"] +``` + --- ### License diff --git a/src/buffer.rs b/src/buffer.rs index 3ee0965..f5711d0 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -691,3 +691,88 @@ impl ByteBuffer { } } } + +#[cfg(feature = "half")] +impl ByteBuffer { + /// Read a 16 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_f16(&mut self) -> Result { + + let offset = 2; + + self.flush_bits(); + if self.rpos + offset > self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let range = self.rpos..self.rpos + offset; + self.rpos += offset; + + let bytes: [u8; 2] = self.data[range].try_into().expect("range should always be 2"); + + Ok(match self.endian { + Endian::BigEndian => half::f16::from_be_bytes(bytes), + Endian::LittleEndian => half::f16::from_le_bytes(bytes), + }) + } + + /// Append a 16 bits floating point number to the buffer. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_f16(half::f16::from_f32(0.1)) + /// ``` + pub fn write_f16(&mut self, val: half::f16) { + match self.endian { + Endian::BigEndian => self.write_bytes(&val.to_be_bytes()), + Endian::LittleEndian => self.write_bytes(&val.to_le_bytes()), + }; + } + + /// Read a truncated 16 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_bf16(&mut self) -> Result { + + let offset = 2; + + self.flush_bits(); + if self.rpos + offset > self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let range = self.rpos..self.rpos + offset; + self.rpos += offset; + + let bytes: [u8; 2] = self.data[range].try_into().expect("range should always be 2"); + + Ok(match self.endian { + Endian::BigEndian => half::bf16::from_be_bytes(bytes), + Endian::LittleEndian => half::bf16::from_le_bytes(bytes), + }) + } + + /// Append a truncated 16 bits floating point number to the buffer. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_bf16(half::bf16::from_f32(0.1)) + /// ``` + pub fn write_bf16(&mut self, val: half::bf16) { + match self.endian { + Endian::BigEndian => self.write_bytes(&val.to_be_bytes()), + Endian::LittleEndian => self.write_bytes(&val.to_le_bytes()), + }; + } +} \ No newline at end of file diff --git a/src/reader.rs b/src/reader.rs index 17abe2e..42c4294 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -366,3 +366,54 @@ impl<'a> ByteReader<'a> { self.rbit = 0 } } + +#[cfg(feature = "half")] +impl ByteReader<'_> { + /// Read a 16 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_f16(&mut self) -> Result { + + let offset = 2; + + self.flush_bits(); + if self.rpos + offset > self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let range = self.rpos..self.rpos + offset; + self.rpos += offset; + + let bytes: [u8; 2] = self.data[range].try_into().expect("range should always be 2"); + + Ok(match self.endian { + Endian::BigEndian => half::f16::from_be_bytes(bytes), + Endian::LittleEndian => half::f16::from_le_bytes(bytes), + }) + } + + /// Read a truncated 16 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_bf16(&mut self) -> Result { + + let offset = 2; + + self.flush_bits(); + if self.rpos + offset > self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let range = self.rpos..self.rpos + offset; + self.rpos += offset; + + let bytes: [u8; 2] = self.data[range].try_into().expect("range should always be 2"); + + Ok(match self.endian { + Endian::BigEndian => half::bf16::from_be_bytes(bytes), + Endian::LittleEndian => half::bf16::from_le_bytes(bytes), + }) + } +} \ No newline at end of file diff --git a/tests/buffer.rs b/tests/buffer.rs index 8655bcf..423e826 100644 --- a/tests/buffer.rs +++ b/tests/buffer.rs @@ -13,6 +13,10 @@ fn test_api() { buffer.write_i32(1); buffer.write_u64(1); buffer.write_i64(1); + if cfg!(feature = "half") { + buffer.write_bf16(half::bf16::from_f32(12.5)); + buffer.write_f16(half::f16::from_f32(12.5)); + } buffer.write_f32(0.1); buffer.write_f64(0.1); buffer.write_string("Hello"); @@ -32,6 +36,10 @@ fn test_api() { let _ = buffer.read_i32(); let _ = buffer.read_u64(); let _ = buffer.read_i64(); + if cfg!(feature = "half") { + let _ = buffer.read_bf16(); + let _ = buffer.read_f16(); + } let _ = buffer.read_f32(); let _ = buffer.read_f64(); let _ = buffer.read_string(); @@ -163,6 +171,40 @@ fn test_to_string() { ); } +#[test] +#[cfg(feature = "half")] +fn test_f16() { + let mut buffer = ByteBuffer::new(); + buffer.write_f16(half::f16::from_f32(0.1)); + assert_eq!(buffer.read_f16().unwrap(), half::f16::from_f32(0.1)); +} + +#[test] +#[cfg(feature = "half")] +fn test_f16_little_endian() { + let mut buffer = ByteBuffer::new(); + buffer.set_endian(Endian::LittleEndian); + buffer.write_f16(half::f16::from_f32(0.1)); + assert_eq!(buffer.read_f16().unwrap(), half::f16::from_f32(0.1)); +} + +#[test] +#[cfg(feature = "half")] +fn test_bf16() { + let mut buffer = ByteBuffer::new(); + buffer.write_bf16(half::bf16::from_f32(0.1)); + assert_eq!(buffer.read_bf16().unwrap(), half::bf16::from_f32(0.1)); +} + +#[test] +#[cfg(feature = "half")] +fn test_bf16_little_endian() { + let mut buffer = ByteBuffer::new(); + buffer.set_endian(Endian::LittleEndian); + buffer.write_bf16(half::bf16::from_f32(0.1)); + assert_eq!(buffer.read_bf16().unwrap(), half::bf16::from_f32(0.1)); +} + #[test] fn test_wpos() { let mut buffer = ByteBuffer::new(); diff --git a/tests/reader.rs b/tests/reader.rs index e54455a..e91fe29 100644 --- a/tests/reader.rs +++ b/tests/reader.rs @@ -13,6 +13,10 @@ fn test_api() { buffer.write_i32(1); buffer.write_u64(1); buffer.write_i64(1); + if cfg!(feature = "half") { + buffer.write_bf16(half::bf16::from_f32(12.5)); + buffer.write_f16(half::f16::from_f32(12.5)); + } buffer.write_f32(0.1); buffer.write_f64(0.1); buffer.write_string("Hello"); @@ -30,6 +34,10 @@ fn test_api() { let _ = reader.read_i32(); let _ = reader.read_u64(); let _ = reader.read_i64(); + if cfg!(feature = "half") { + let _ = reader.read_bf16(); + let _ = reader.read_f16(); + } let _ = reader.read_f32(); let _ = reader.read_f64(); let _ = reader.read_string(); @@ -140,6 +148,46 @@ fn test_signed_little_endian() { assert_eq!(reader.read_u8().unwrap(), 0xFF); } +#[test] +#[cfg(feature = "half")] +fn test_f16() { + let mut buffer = ByteBuffer::new(); + buffer.write_f16(half::f16::from_f32(0.1)); + let mut reader = ByteReader::from(buffer.as_bytes()); + assert_eq!(reader.read_f16().unwrap(), half::f16::from_f32(0.1)); +} + +#[test] +#[cfg(feature = "half")] +fn test_f16_little_endian() { + let mut buffer = ByteBuffer::new(); + buffer.set_endian(Endian::LittleEndian); + buffer.write_f16(half::f16::from_f32(0.1)); + let mut reader = ByteReader::from(buffer.as_bytes()); + reader.set_endian(Endian::LittleEndian); + assert_eq!(reader.read_f16().unwrap(), half::f16::from_f32(0.1)); +} + +#[test] +#[cfg(feature = "half")] +fn test_bf16() { + let mut buffer = ByteBuffer::new(); + buffer.write_bf16(half::bf16::from_f32(0.1)); + let mut reader = ByteReader::from(buffer.as_bytes()); + assert_eq!(reader.read_bf16().unwrap(), half::bf16::from_f32(0.1)); +} + +#[test] +#[cfg(feature = "half")] +fn test_bf16_little_endian() { + let mut buffer = ByteBuffer::new(); + buffer.set_endian(Endian::LittleEndian); + buffer.write_bf16(half::bf16::from_f32(0.1)); + let mut reader = ByteReader::from(buffer.as_bytes()); + reader.set_endian(Endian::LittleEndian); + assert_eq!(reader.read_bf16().unwrap(), half::bf16::from_f32(0.1)); +} + #[test] fn test_string() { let mut buffer = ByteBuffer::new();