diff --git a/Cargo.lock b/Cargo.lock index 0b18f51..c0e062c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,18 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "bzip2" version = "0.4.4" @@ -267,6 +279,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "getrandom" version = "0.2.15" @@ -419,6 +437,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -542,6 +566,7 @@ dependencies = [ name = "servicepoint" version = "0.5.1" dependencies = [ + "bitvec", "bzip2", "clap 4.5.4", "flate2", @@ -602,6 +627,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.10.1" @@ -766,6 +797,15 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zstd" version = "0.13.1" diff --git a/crates/servicepoint/Cargo.toml b/crates/servicepoint/Cargo.toml index 5e748e3..ac2698f 100644 --- a/crates/servicepoint/Cargo.toml +++ b/crates/servicepoint/Cargo.toml @@ -14,6 +14,7 @@ crate-type = ["rlib"] [dependencies] log = "0.4" +bitvec = "1.0" flate2 = { version = "1.0", optional = true } bzip2 = { version = "0.4", optional = true } zstd = { version = "0.13", optional = true } diff --git a/crates/servicepoint/examples/wiping_clear.rs b/crates/servicepoint/examples/wiping_clear.rs index 1295fc4..fdf84e3 100644 --- a/crates/servicepoint/examples/wiping_clear.rs +++ b/crates/servicepoint/examples/wiping_clear.rs @@ -4,7 +4,7 @@ use std::time::Duration; use clap::Parser; -use servicepoint::*; +use servicepoint::{bitvec::prelude::BitVec, *}; #[derive(Parser, Debug)] struct Cli { @@ -34,7 +34,7 @@ fn main() { // this works because the pixel grid has max size let pixel_data: Vec = enabled_pixels.clone().into(); - let bit_vec = BitVec::from(&*pixel_data); + let bit_vec = BitVec::from_vec(pixel_data); connection .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma)) diff --git a/crates/servicepoint/src/bit_vec.rs b/crates/servicepoint/src/bit_vec.rs deleted file mode 100644 index 23719a7..0000000 --- a/crates/servicepoint/src/bit_vec.rs +++ /dev/null @@ -1,257 +0,0 @@ -use crate::DataRef; - -/// A fixed-size vector of bits -#[derive(Debug, Clone, PartialEq)] -pub struct BitVec { - size: usize, - data: Vec, -} - -impl BitVec { - /// Create a new `BitVec`. - /// - /// # Arguments - /// - /// * `size`: size in bits. - /// - /// returns: `BitVec` with all bits set to false. - /// - /// # Panics - /// - /// When `size` is not divisible by 8. - #[must_use] - pub fn new(size: usize) -> BitVec { - assert_eq!(size % 8, 0); - Self { - size, - data: vec![0; size / 8], - } - } - - /// Sets the value of a bit. - /// - /// # Arguments - /// - /// * `index`: the bit index to edit - /// * `value`: the value to set the bit to - /// - /// returns: old value of the bit - /// - /// # Panics - /// - /// When accessing `index` out of bounds. - pub fn set(&mut self, index: usize, value: bool) -> bool { - let (byte_index, bit_mask) = self.get_indexes(index); - - let byte = self.data[byte_index]; - let old_value = byte & bit_mask != 0; - - self.data[byte_index] = if value { - byte | bit_mask - } else { - byte & (u8::MAX ^ bit_mask) - }; - - old_value - } - - /// Gets the value of a bit. - /// - /// # Arguments - /// - /// * `index`: the bit index to read - /// - /// returns: value of the bit - /// - /// # Panics - /// - /// When accessing `index` out of bounds. - #[must_use] - pub fn get(&self, index: usize) -> bool { - let (byte_index, bit_mask) = self.get_indexes(index); - self.data[byte_index] & bit_mask != 0 - } - - /// Sets all bits to the specified value - /// - /// # Arguments - /// - /// * `value`: the value to set all bits to - /// - /// # Examples - /// ``` - /// use servicepoint::BitVec; - /// let mut vec = BitVec::new(8); - /// vec.fill(true); - /// ``` - pub fn fill(&mut self, value: bool) { - let byte: u8 = if value { 0xFF } else { 0x00 }; - self.data.fill(byte); - } - - /// Gets the length in bits - #[must_use] - pub fn len(&self) -> usize { - self.size - } - - /// returns true if length is 0. - #[must_use] - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } - - /// Get an iterator over every bit in the vector - pub fn iter(&self) -> Iter { - Iter { - bit_vec: self, - index: 0, - end: self.size, - } - } - - /// Calculates the byte index and bitmask for a specific bit in the vector - fn get_indexes(&self, bit_index: usize) -> (usize, u8) { - assert!( - bit_index < self.size, - "bit index {bit_index} is outside of range 0..<{}", - self.size - ); - - let byte_index = bit_index / 8; - let bit_in_byte_index = 7 - bit_index % 8; - let bit_mask: u8 = 1 << bit_in_byte_index; - (byte_index, bit_mask) - } -} - -impl DataRef for BitVec { - fn data_ref_mut(&mut self) -> &mut [u8] { - self.data.as_mut_slice() - } - - fn data_ref(&self) -> &[u8] { - self.data.as_slice() - } -} - -impl From for Vec { - /// Turns the `BitVec` into the underlying `Vec` - fn from(value: BitVec) -> Self { - value.data - } -} - -impl From<&[u8]> for BitVec { - /// Interpret the data as a series of bits and load then into a new `BitVec` instance. - fn from(value: &[u8]) -> Self { - Self { - size: value.len() * 8, - data: Vec::from(value), - } - } -} - -pub struct Iter<'t> { - pub(crate) bit_vec: &'t BitVec, - pub(crate) index: usize, - pub(crate) end: usize, -} - -impl<'t> Iterator for Iter<'t> { - type Item = bool; - - fn next(&mut self) -> Option { - if self.index >= self.end { - return None; - } - - let result = Some(self.bit_vec.get(self.index)); - self.index += 1; - result - } -} - -#[cfg(test)] -mod tests { - use crate::{BitVec, DataRef}; - - #[test] - fn fill() { - let mut vec = BitVec::new(8 * 3); - assert_eq!(vec.data, [0x00, 0x00, 0x00]); - - vec.fill(true); - assert_eq!(vec.data, [0xFF, 0xFF, 0xFF]); - - vec.fill(false); - assert_eq!(vec.data, [0x00, 0x00, 0x00]); - } - - #[test] - fn get_set() { - let mut vec = BitVec::new(8 * 3); - assert!(!vec.get(1)); - assert!(!vec.get(11)); - - vec.set(1, true); - vec.set(11, true); - assert_eq!(vec.data, [0x40, 0x10, 0x00]); - assert!(!vec.get(0)); - assert!(vec.get(1)); - assert!(vec.get(11)); - } - - #[test] - fn load() { - let mut vec = BitVec::new(8 * 3); - vec.set(6, true); - vec.set(7, true); - vec.set(8, true); - vec.set(9, true); - vec.set(10, true); - vec.set(vec.len() - 1, true); - - assert_eq!(vec.data, [0x03, 0xE0, 0x01]); - - let data: Vec = vec.into(); - - let vec = BitVec::from(&*data); - assert_eq!(vec.data, [0x03, 0xE0, 0x01]); - } - - #[test] - fn mut_data_ref() { - let mut vec = BitVec::new(8 * 3); - - let data_ref = vec.data_ref_mut(); - data_ref.copy_from_slice(&[0x40, 0x10, 0x00]); - - assert_eq!(vec.data, [0x40, 0x10, 0x00]); - assert!(vec.get(1)); - } - - #[test] - fn is_empty() { - let vec = BitVec::new(8 * 3); - assert!(!vec.is_empty()); - - let vec = BitVec::new(0); - assert!(vec.is_empty()); - } - - #[test] - fn get_returns_old() { - let mut vec = BitVec::new(8); - assert!(!vec.set(1, true)); - assert!(vec.set(1, true)); - assert!(vec.set(1, false)); - assert!(!vec.set(1, false)); - } - - #[test] - fn debug_print() { - let vec = BitVec::new(8 * 3); - format!("{vec:?}"); - } -} diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index 40721ba..0462b90 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -85,17 +85,12 @@ impl Grid for ByteGrid { /// * `x` and `y`: position of the cell /// * `value`: the value to write to the cell /// - /// returns: old value of the cell. - /// /// # Panics /// /// When accessing `x` or `y` out of bounds. - fn set(&mut self, x: usize, y: usize, value: u8) -> u8 { + fn set(&mut self, x: usize, y: usize, value: u8) { self.check_indexes(x, y); - let pos = &mut self.data[x + y * self.width]; - let old_val = *pos; - *pos = value; - old_val + self.data[x + y * self.width] = value; } /// Gets the current value at the specified position. diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs index 4e653eb..856ba9c 100644 --- a/crates/servicepoint/src/command.rs +++ b/crates/servicepoint/src/command.rs @@ -1,7 +1,9 @@ +use bitvec::prelude::{BitVec, Msb0}; + use crate::command_code::CommandCode; use crate::compression::{into_compressed, into_decompressed}; use crate::{ - BitVec, ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, + ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, SpBitVec, TILE_SIZE, }; @@ -25,6 +27,7 @@ pub type Offset = usize; /// Type alias for documenting the meaning of the u16 in enum values pub type Brightness = u8; +// TODO: check order /// A command to send to the display. #[derive(Debug, Clone, PartialEq)] pub enum Command { @@ -43,16 +46,16 @@ pub enum Command { BitmapLegacy, /// Set pixel data starting at the offset. /// The contained `BitVec` is always uncompressed. - BitmapLinear(Offset, BitVec, CompressionCode), + BitmapLinear(Offset, SpBitVec, CompressionCode), /// Set pixel data according to an and-mask starting at the offset. /// The contained `BitVec` is always uncompressed. - BitmapLinearAnd(Offset, BitVec, CompressionCode), + BitmapLinearAnd(Offset, SpBitVec, CompressionCode), /// Set pixel data according to an or-mask starting at the offset. /// The contained `BitVec` is always uncompressed. - BitmapLinearOr(Offset, BitVec, CompressionCode), + BitmapLinearOr(Offset, SpBitVec, CompressionCode), /// Set pixel data according to a xor-mask starting at the offset. /// The contained `BitVec` is always uncompressed. - BitmapLinearXor(Offset, BitVec, CompressionCode), + BitmapLinearXor(Offset, SpBitVec, CompressionCode), /// Show text on the screen. Note that the byte data has to be CP437 encoded. Cp437Data(Origin, ByteGrid), /// Sets a window of pixels to the specified values @@ -366,7 +369,7 @@ impl Command { /// Helper method for Packets into `BitMapLinear*`-Commands fn packet_into_linear_bitmap( packet: Packet, - ) -> Result<(BitVec, CompressionCode), TryFromPacketError> { + ) -> Result<(BitVec, CompressionCode), TryFromPacketError> { let Packet(Header(_, _, length, sub, reserved), payload) = packet; if reserved != 0 { return Err(TryFromPacketError::ExtraneousHeaderValues); @@ -387,17 +390,18 @@ impl Command { payload.len(), )); } - Ok((BitVec::from(&*payload), sub)) + Ok((BitVec::from_vec(payload), sub)) } } #[cfg(test)] mod tests { + use bitvec::prelude::BitVec; + use crate::command::TryFromPacketError; use crate::command_code::CommandCode; use crate::{ - BitVec, ByteGrid, Command, CompressionCode, Grid, Header, Origin, - Packet, PixelGrid, + ByteGrid, Command, CompressionCode, Header, Origin, Packet, PixelGrid, }; fn round_trip(original: Command) { @@ -462,20 +466,24 @@ mod tests { #[test] fn round_trip_bitmap_linear() { for compression in all_compressions().to_owned() { - round_trip(Command::BitmapLinear(23, BitVec::new(40), compression)); + round_trip(Command::BitmapLinear( + 23, + BitVec::repeat(false, 40), + compression, + )); round_trip(Command::BitmapLinearAnd( 23, - BitVec::new(40), + BitVec::repeat(false, 40), compression, )); round_trip(Command::BitmapLinearOr( 23, - BitVec::new(40), + BitVec::repeat(false, 40), compression, )); round_trip(Command::BitmapLinearXor( 23, - BitVec::new(40), + BitVec::repeat(false, 40), compression, )); round_trip(Command::BitmapLinearWin( @@ -590,8 +598,12 @@ mod tests { #[test] fn error_decompression_failed_and() { for compression in all_compressions().to_owned() { - let p: Packet = - Command::BitmapLinearAnd(0, BitVec::new(8), compression).into(); + let p: Packet = Command::BitmapLinearAnd( + 0, + BitVec::repeat(false, 8), + compression, + ) + .into(); let Packet(header, mut payload) = p; // mangle it @@ -633,7 +645,7 @@ mod tests { fn error_reserved_used() { let Packet(header, payload) = Command::BitmapLinear( 0, - BitVec::new(8), + BitVec::repeat(false, 8), CompressionCode::Uncompressed, ) .into(); @@ -649,7 +661,7 @@ mod tests { fn error_invalid_compression() { let Packet(header, payload) = Command::BitmapLinear( 0, - BitVec::new(8), + BitVec::repeat(false, 8), CompressionCode::Uncompressed, ) .into(); @@ -665,7 +677,7 @@ mod tests { fn error_unexpected_size() { let Packet(header, payload) = Command::BitmapLinear( 0, - BitVec::new(8), + BitVec::repeat(false, 8), CompressionCode::Uncompressed, ) .into(); diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs index 76c80db..882f678 100644 --- a/crates/servicepoint/src/grid.rs +++ b/crates/servicepoint/src/grid.rs @@ -6,12 +6,10 @@ pub trait Grid { /// /// * `x` and `y`: position of the cell to read /// - /// returns: the old value - /// /// # Panics /// /// When accessing `x` or `y` out of bounds. - fn set(&mut self, x: usize, y: usize, value: T) -> T; + fn set(&mut self, x: usize, y: usize, value: T); /// Get the current value at the specified position /// @@ -46,11 +44,12 @@ pub trait Grid { /// * `x` and `y`: position of the cell to read /// /// returns: the old value or None - fn set_optional(&mut self, x: isize, y: isize, value: T) -> Option { + fn set_optional(&mut self, x: isize, y: isize, value: T) -> bool { if self.is_in_bounds(x, y) { - Some(self.set(x as usize, y as usize, value)) + self.set(x as usize, y as usize, value); + true } else { - None + false } } diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index 1951087..dd03d96 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -2,7 +2,6 @@ use std::time::Duration; -pub use crate::bit_vec::BitVec; pub use crate::byte_grid::ByteGrid; pub use crate::command::{Brightness, Command, Offset, Origin}; pub use crate::compression_code::CompressionCode; @@ -11,8 +10,11 @@ pub use crate::data_ref::DataRef; pub use crate::grid::{Grid, RefGrid}; pub use crate::packet::{Header, Packet, Payload}; pub use crate::pixel_grid::PixelGrid; +pub use bitvec; +use bitvec::prelude::{BitVec, Msb0}; + +type SpBitVec = BitVec; -mod bit_vec; mod byte_grid; mod command; mod command_code; diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index 6a24d79..67c8ac6 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -1,11 +1,14 @@ -use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH}; +use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH}; +use bitvec::order::Msb0; +use bitvec::prelude::BitSlice; +use bitvec::slice::Iter; /// A grid of pixels stored in packed bytes. #[derive(Debug, Clone, PartialEq)] pub struct PixelGrid { width: usize, height: usize, - bit_vec: BitVec, + bit_vec: SpBitVec, } impl PixelGrid { @@ -26,7 +29,7 @@ impl PixelGrid { Self { width, height, - bit_vec: BitVec::new(width * height), + bit_vec: BitVec::repeat(false, width * height), } } @@ -56,11 +59,11 @@ impl PixelGrid { Self { width, height, - bit_vec: BitVec::from(data), + bit_vec: BitVec::from_slice(data), } } - pub fn iter(&self) -> crate::bit_vec::Iter { + pub fn iter(&self) -> Iter<'_, u8, Msb0> { self.bit_vec.iter() } @@ -98,13 +101,13 @@ impl Grid for PixelGrid { /// # Panics /// /// When accessing `x` or `y` out of bounds. - fn set(&mut self, x: usize, y: usize, value: bool) -> bool { + fn set(&mut self, x: usize, y: usize, value: bool) { self.check_indexes(x, y); self.bit_vec.set(x + y * self.width, value) } fn get(&self, x: usize, y: usize) -> bool { - self.bit_vec.get(x + y * self.width) + self.bit_vec[x + y * self.width] } /// Sets the state of all pixels in the `PixelGrid`. @@ -128,11 +131,11 @@ impl Grid for PixelGrid { impl DataRef for PixelGrid { fn data_ref_mut(&mut self) -> &mut [u8] { - self.bit_vec.data_ref_mut() + self.bit_vec.as_raw_mut_slice() } fn data_ref(&self) -> &[u8] { - self.bit_vec.data_ref() + self.bit_vec.as_raw_slice() } } @@ -149,19 +152,17 @@ pub struct IterRows<'t> { } impl<'t> Iterator for IterRows<'t> { - type Item = crate::bit_vec::Iter<'t>; + type Item = &'t BitSlice; fn next(&mut self) -> Option { if self.row >= self.pixel_grid.height { return None; } - let result = Some(crate::bit_vec::Iter { - bit_vec: &self.pixel_grid.bit_vec, - index: self.row * self.pixel_grid.width, - end: (self.row + 1) * self.pixel_grid.width, - }); + + let start = self.row * self.pixel_grid.width; + let end = start + self.pixel_grid.width; self.row += 1; - result + Some(&self.pixel_grid.bit_vec[start..end]) } } diff --git a/crates/servicepoint_binding_c/src/bit_vec.rs b/crates/servicepoint_binding_c/src/bit_vec.rs index ba4233d..995d2f6 100644 --- a/crates/servicepoint_binding_c/src/bit_vec.rs +++ b/crates/servicepoint_binding_c/src/bit_vec.rs @@ -2,9 +2,28 @@ //! //! prefix `sp_bit_vec_` -use servicepoint::{BitVec, DataRef}; - use crate::c_slice::CByteSlice; +use servicepoint::bitvec::prelude::{BitVec, Msb0}; + +/// cbindgen:no-export +type SpBitVec = BitVec; + +#[derive(Clone)] +pub struct CBitVec { + actual: SpBitVec, +} + +impl From for CBitVec { + fn from(actual: SpBitVec) -> Self { + Self { actual } + } +} + +impl From for SpBitVec { + fn from(value: CBitVec) -> Self { + value.actual + } +} /// Creates a new `BitVec` instance. /// @@ -25,8 +44,10 @@ use crate::c_slice::CByteSlice; /// - the returned instance is freed in some way, either by using a consuming function or /// by explicitly calling `sp_bit_vec_dealloc`. #[no_mangle] -pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut BitVec { - Box::into_raw(Box::new(BitVec::new(size))) +pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut CBitVec { + Box::into_raw(Box::new(CBitVec { + actual: SpBitVec::repeat(false, size), + })) } /// Interpret the data as a series of bits and load then into a new `BitVec` instance. @@ -43,9 +64,11 @@ pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut BitVec { pub unsafe extern "C" fn sp_bit_vec_load( data: *const u8, data_length: usize, -) -> *mut BitVec { +) -> *mut CBitVec { let data = std::slice::from_raw_parts(data, data_length); - Box::into_raw(Box::new(BitVec::from(data))) + Box::into_raw(Box::new(CBitVec { + actual: SpBitVec::from_slice(data), + })) } /// Clones a `BitVec`. @@ -59,7 +82,9 @@ pub unsafe extern "C" fn sp_bit_vec_load( /// - the returned instance is freed in some way, either by using a consuming function or /// by explicitly calling `sp_bit_vec_dealloc`. #[no_mangle] -pub unsafe extern "C" fn sp_bit_vec_clone(this: *const BitVec) -> *mut BitVec { +pub unsafe extern "C" fn sp_bit_vec_clone( + this: *const CBitVec, +) -> *mut CBitVec { Box::into_raw(Box::new((*this).clone())) } @@ -73,7 +98,7 @@ pub unsafe extern "C" fn sp_bit_vec_clone(this: *const BitVec) -> *mut BitVec { /// - `this` is not used concurrently or after this call /// - `this` was not passed to another consuming function, e.g. to create a `Command` #[no_mangle] -pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut BitVec) { +pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut CBitVec) { _ = Box::from_raw(this); } @@ -98,10 +123,10 @@ pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut BitVec) { /// - `this` is not written to concurrently #[no_mangle] pub unsafe extern "C" fn sp_bit_vec_get( - this: *const BitVec, + this: *const CBitVec, index: usize, ) -> bool { - (*this).get(index) + *(*this).actual.get(index).unwrap() } /// Sets the value of a bit in the `BitVec`. @@ -126,11 +151,11 @@ pub unsafe extern "C" fn sp_bit_vec_get( /// - `this` is not written to or read from concurrently #[no_mangle] pub unsafe extern "C" fn sp_bit_vec_set( - this: *mut BitVec, + this: *mut CBitVec, index: usize, value: bool, -) -> bool { - (*this).set(index, value) +) { + (*this).actual.set(index, value) } /// Sets the value of all bits in the `BitVec`. @@ -146,8 +171,8 @@ pub unsafe extern "C" fn sp_bit_vec_set( /// - `this` points to a valid `BitVec` /// - `this` is not written to or read from concurrently #[no_mangle] -pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut BitVec, value: bool) { - (*this).fill(value) +pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut CBitVec, value: bool) { + (*this).actual.fill(value) } /// Gets the length of the `BitVec` in bits. @@ -158,8 +183,8 @@ pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut BitVec, value: bool) { /// /// - `this` points to a valid `BitVec` #[no_mangle] -pub unsafe extern "C" fn sp_bit_vec_len(this: *const BitVec) -> usize { - (*this).len() +pub unsafe extern "C" fn sp_bit_vec_len(this: *const CBitVec) -> usize { + (*this).actual.len() } /// Returns true if length is 0. @@ -170,8 +195,8 @@ pub unsafe extern "C" fn sp_bit_vec_len(this: *const BitVec) -> usize { /// /// - `this` points to a valid `BitVec` #[no_mangle] -pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const BitVec) -> bool { - (*this).is_empty() +pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const CBitVec) -> bool { + (*this).actual.is_empty() } /// Gets an unsafe reference to the data of the `BitVec` instance. @@ -185,9 +210,9 @@ pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const BitVec) -> bool { /// - the returned memory range is never accessed concurrently, either via the `BitVec` or directly #[no_mangle] pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref( - this: *mut BitVec, + this: *mut CBitVec, ) -> CByteSlice { - let data = (*this).data_ref_mut(); + let data = (*this).actual.as_raw_mut_slice(); CByteSlice { start: data.as_mut_ptr_range().start, length: data.len(), diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs index 0c981f6..400a6ee 100644 --- a/crates/servicepoint_binding_c/src/command.rs +++ b/crates/servicepoint_binding_c/src/command.rs @@ -5,10 +5,12 @@ use std::ptr::null_mut; use servicepoint::{ - BitVec, Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, - Packet, PixelGrid, + Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, Packet, + PixelGrid, }; +use crate::bit_vec::CBitVec; + /// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. /// /// Returns: pointer to new `Command` instance or NULL @@ -140,13 +142,13 @@ pub unsafe extern "C" fn sp_command_char_brightness( #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear( offset: Offset, - bit_vec: *mut BitVec, + bit_vec: *mut CBitVec, compression: CompressionCode, ) -> *mut Command { let bit_vec = *Box::from_raw(bit_vec); Box::into_raw(Box::new(Command::BitmapLinear( offset, - bit_vec, + bit_vec.into(), compression, ))) } @@ -166,13 +168,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear( #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_and( offset: Offset, - bit_vec: *mut BitVec, + bit_vec: *mut CBitVec, compression: CompressionCode, ) -> *mut Command { let bit_vec = *Box::from_raw(bit_vec); Box::into_raw(Box::new(Command::BitmapLinearAnd( offset, - bit_vec, + bit_vec.into(), compression, ))) } @@ -192,13 +194,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and( #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_or( offset: Offset, - bit_vec: *mut BitVec, + bit_vec: *mut CBitVec, compression: CompressionCode, ) -> *mut Command { let bit_vec = *Box::from_raw(bit_vec); Box::into_raw(Box::new(Command::BitmapLinearOr( offset, - bit_vec, + bit_vec.into(), compression, ))) } @@ -218,13 +220,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or( #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_xor( offset: Offset, - bit_vec: *mut BitVec, + bit_vec: *mut CBitVec, compression: CompressionCode, ) -> *mut Command { let bit_vec = *Box::from_raw(bit_vec); Box::into_raw(Box::new(Command::BitmapLinearXor( offset, - bit_vec, + bit_vec.into(), compression, ))) } diff --git a/crates/servicepoint_binding_c/src/lib.rs b/crates/servicepoint_binding_c/src/lib.rs index 47ce5cb..7aaa92e 100644 --- a/crates/servicepoint_binding_c/src/lib.rs +++ b/crates/servicepoint_binding_c/src/lib.rs @@ -22,4 +22,4 @@ pub mod pixel_grid; /// The minimum time needed for the display to refresh the screen in ms. pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32; -mod c_slice; +pub mod c_slice; diff --git a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs index 98fe381..5e958ad 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs +++ b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs @@ -21,46 +21,45 @@ namespace ServicePoint.BindGen /// Creates a new `BitVec` instance. # Arguments * `size`: size in bits. returns: `BitVec` with all bits set to false. # Panics When `size` is not divisible by 8. # Safety The caller has to make sure that: - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_bit_vec_dealloc`. [DllImport(__DllName, EntryPoint = "sp_bit_vec_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern BitVec* sp_bit_vec_new(nuint size); + public static extern CBitVec* sp_bit_vec_new(nuint size); /// Interpret the data as a series of bits and load then into a new `BitVec` instance. # Safety The caller has to make sure that: - `data` points to a valid memory location of at least `data_length` bytes in size. - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_bit_vec_dealloc`. [DllImport(__DllName, EntryPoint = "sp_bit_vec_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern BitVec* sp_bit_vec_load(byte* data, nuint data_length); + public static extern CBitVec* sp_bit_vec_load(byte* data, nuint data_length); /// Clones a `BitVec`. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_bit_vec_dealloc`. [DllImport(__DllName, EntryPoint = "sp_bit_vec_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern BitVec* sp_bit_vec_clone(BitVec* @this); + public static extern CBitVec* sp_bit_vec_clone(CBitVec* @this); /// Deallocates a `BitVec`. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command` [DllImport(__DllName, EntryPoint = "sp_bit_vec_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern void sp_bit_vec_dealloc(BitVec* @this); + public static extern void sp_bit_vec_dealloc(CBitVec* @this); /// Gets the value of a bit from the `BitVec`. # Arguments * `this`: instance to read from * `index`: the bit index to read returns: value of the bit # Panics When accessing `index` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to concurrently [DllImport(__DllName, EntryPoint = "sp_bit_vec_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - public static extern bool sp_bit_vec_get(BitVec* @this, nuint index); + public static extern bool sp_bit_vec_get(CBitVec* @this, nuint index); /// Sets the value of a bit in the `BitVec`. # Arguments * `this`: instance to write to * `index`: the bit index to edit * `value`: the value to set the bit to returns: old value of the bit # Panics When accessing `index` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently [DllImport(__DllName, EntryPoint = "sp_bit_vec_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool sp_bit_vec_set(BitVec* @this, nuint index, [MarshalAs(UnmanagedType.U1)] bool value); + public static extern void sp_bit_vec_set(CBitVec* @this, nuint index, [MarshalAs(UnmanagedType.U1)] bool value); /// Sets the value of all bits in the `BitVec`. # Arguments * `value`: the value to set all bits to # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently [DllImport(__DllName, EntryPoint = "sp_bit_vec_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern void sp_bit_vec_fill(BitVec* @this, [MarshalAs(UnmanagedType.U1)] bool value); + public static extern void sp_bit_vec_fill(CBitVec* @this, [MarshalAs(UnmanagedType.U1)] bool value); /// Gets the length of the `BitVec` in bits. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` [DllImport(__DllName, EntryPoint = "sp_bit_vec_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern nuint sp_bit_vec_len(BitVec* @this); + public static extern nuint sp_bit_vec_len(CBitVec* @this); /// Returns true if length is 0. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` [DllImport(__DllName, EntryPoint = "sp_bit_vec_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - public static extern bool sp_bit_vec_is_empty(BitVec* @this); + public static extern bool sp_bit_vec_is_empty(CBitVec* @this); /// Gets an unsafe reference to the data of the `BitVec` instance. ## Safety The caller has to make sure that: - `this` points to a valid `BitVec` - the returned memory range is never accessed after the passed `BitVec` has been freed - the returned memory range is never accessed concurrently, either via the `BitVec` or directly [DllImport(__DllName, EntryPoint = "sp_bit_vec_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern CByteSlice sp_bit_vec_unsafe_data_ref(BitVec* @this); + public static extern CByteSlice sp_bit_vec_unsafe_data_ref(CBitVec* @this); /// Creates a new `ByteGrid` with the specified dimensions. returns: `ByteGrid` initialized to 0. # Safety The caller has to make sure that: - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_byte_grid_dealloc`. [DllImport(__DllName, EntryPoint = "sp_byte_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] @@ -132,19 +131,19 @@ namespace ServicePoint.BindGen /// Allocates a new `Command::BitmapLinear` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern Command* sp_command_bitmap_linear(nuint offset, BitVec* bit_vec, CompressionCode compression); + public static extern Command* sp_command_bitmap_linear(nuint offset, CBitVec* bit_vec, CompressionCode compression); /// Allocates a new `Command::BitmapLinearAnd` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_and", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern Command* sp_command_bitmap_linear_and(nuint offset, BitVec* bit_vec, CompressionCode compression); + public static extern Command* sp_command_bitmap_linear_and(nuint offset, CBitVec* bit_vec, CompressionCode compression); /// Allocates a new `Command::BitmapLinearOr` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_or", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern Command* sp_command_bitmap_linear_or(nuint offset, BitVec* bit_vec, CompressionCode compression); + public static extern Command* sp_command_bitmap_linear_or(nuint offset, CBitVec* bit_vec, CompressionCode compression); /// Allocates a new `Command::BitmapLinearXor` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_xor", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern Command* sp_command_bitmap_linear_xor(nuint offset, BitVec* bit_vec, CompressionCode compression); + public static extern Command* sp_command_bitmap_linear_xor(nuint offset, CBitVec* bit_vec, CompressionCode compression); /// Allocates a new `Command::Cp437Data` instance. The passed `ByteGrid` gets consumed. # Safety The caller has to make sure that: - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. [DllImport(__DllName, EntryPoint = "sp_command_cp437_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] @@ -227,6 +226,11 @@ namespace ServicePoint.BindGen } + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct CBitVec + { + } + [StructLayout(LayoutKind.Sequential)] public unsafe partial struct CByteSlice { @@ -234,11 +238,6 @@ namespace ServicePoint.BindGen public nuint length; } - [StructLayout(LayoutKind.Sequential)] - public unsafe partial struct BitVec - { - } - [StructLayout(LayoutKind.Sequential)] public unsafe partial struct ByteGrid { diff --git a/crates/servicepoint_binding_cs/ServicePoint/BitVec.cs b/crates/servicepoint_binding_cs/ServicePoint/BitVec.cs index 13b2200..53ee65c 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/BitVec.cs +++ b/crates/servicepoint_binding_cs/ServicePoint/BitVec.cs @@ -2,7 +2,7 @@ using ServicePoint.BindGen; namespace ServicePoint; -public sealed class BitVec : SpNativeInstance +public sealed class BitVec : SpNativeInstance { public static BitVec New(int size) { @@ -80,7 +80,7 @@ public sealed class BitVec : SpNativeInstance } } - private unsafe BitVec(BindGen.BitVec* instance) : base(instance) + private unsafe BitVec(BindGen.CBitVec* instance) : base(instance) { } diff --git a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj index 94691e7..127ffc5 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj +++ b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj @@ -27,19 +27,19 @@ - + + - + + - - + - - + diff --git a/crates/servicepoint_binding_cs/build.rs b/crates/servicepoint_binding_cs/build.rs index 0eb30f9..49747b2 100644 --- a/crates/servicepoint_binding_cs/build.rs +++ b/crates/servicepoint_binding_cs/build.rs @@ -12,7 +12,6 @@ fn main() { .input_extern_file("../servicepoint_binding_c/src/lib.rs") .input_extern_file("../servicepoint_binding_c/src/c_slice.rs") .input_extern_file("../servicepoint_binding_c/src/packet.rs") - .input_extern_file("../servicepoint/src/bit_vec.rs") .input_extern_file("../servicepoint/src/byte_grid.rs") .input_extern_file("../servicepoint/src/command.rs") .input_extern_file("../servicepoint/src/connection.rs")