Skip to content

Commit

Permalink
Add support for half::{f16, bf16}
Browse files Browse the repository at this point in the history
  • Loading branch information
terahlunah committed Sep 28, 2023
1 parent 3826c67 commit 60a4c31
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 2 deletions.
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bytebuffer"
version = "2.1.1"
version = "2.2.0"
authors = ["Terah <[email protected]>", "Shiroy <[email protected]>"]
description = "A bytebuffer for networking and binary protocols"
homepage = "https://github.com/terahlunah/bytebuffer"
Expand All @@ -12,6 +12,10 @@ edition = "2021"
[lib]
name = "bytebuffer"

[features]
default = []
half = ["dep:half"]

[dependencies]
byteorder = "1"

half = { version = "2.3.1", optional = true }
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
85 changes: 85 additions & 0 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<half::f16> {

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<half::bf16> {

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()),
};
}
}
51 changes: 51 additions & 0 deletions src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<half::f16> {

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<half::bf16> {

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),
})
}
}
42 changes: 42 additions & 0 deletions tests/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
48 changes: 48 additions & 0 deletions tests/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 60a4c31

Please sign in to comment.