From 3dc18a4765d7a94ca1a4a7b9ede7d20a5d1bae6d Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 30 May 2024 21:55:55 +0200 Subject: [PATCH 01/16] fix cargo: output should be cargo:: --- crates/servicepoint_binding_c/examples/lang_c/build.rs | 6 +++--- crates/servicepoint_binding_cs/build.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/servicepoint_binding_c/examples/lang_c/build.rs b/crates/servicepoint_binding_c/examples/lang_c/build.rs index 279439f..db090d4 100644 --- a/crates/servicepoint_binding_c/examples/lang_c/build.rs +++ b/crates/servicepoint_binding_c/examples/lang_c/build.rs @@ -1,9 +1,9 @@ const SP_INCLUDE: &str = "DEP_SERVICEPOINT_INCLUDE"; fn main() { - println!("cargo:rerun-if-changed=src/main.c"); - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed={SP_INCLUDE}"); + println!("cargo::rerun-if-changed=src/main.c"); + println!("cargo::rerun-if-changed=build.rs"); + println!("cargo::rerun-if-env-changed={SP_INCLUDE}"); let sp_include = std::env::var_os(SP_INCLUDE).unwrap().into_string().unwrap(); diff --git a/crates/servicepoint_binding_cs/build.rs b/crates/servicepoint_binding_cs/build.rs index 7f64dcf..0eb30f9 100644 --- a/crates/servicepoint_binding_cs/build.rs +++ b/crates/servicepoint_binding_cs/build.rs @@ -1,8 +1,8 @@ //! Build script generating the C# code needed to call methods from the `servicepoint` C library. fn main() { - println!("cargo:rerun-if-changed=../servicepoint_binding_c/src"); - println!("cargo:rerun-if-changed=build.rs"); + println!("cargo::rerun-if-changed=../servicepoint_binding_c/src"); + println!("cargo::rerun-if-changed=build.rs"); csbindgen::Builder::default() .input_extern_file("../servicepoint_binding_c/src/bit_vec.rs") .input_extern_file("../servicepoint_binding_c/src/byte_grid.rs") From 372582cec68c2625b8624fa6df24cc77ad1666fe Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 30 May 2024 21:56:16 +0200 Subject: [PATCH 02/16] fix C README --- crates/servicepoint_binding_c/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/servicepoint_binding_c/README.md b/crates/servicepoint_binding_c/README.md index fb2c70c..eacd65d 100644 --- a/crates/servicepoint_binding_c/README.md +++ b/crates/servicepoint_binding_c/README.md @@ -17,19 +17,19 @@ This crate contains C bindings for the `servicepoint` library, enabling users to #include "servicepoint.h" int main(void) { - sp2_Connection *connection = sp2_connection_open("localhost:2342"); + sp_Connection *connection = sp_connection_open("localhost:2342"); if (connection == NULL) return 1; - sp2_PixelGrid *pixels = sp2_pixel_grid_new(sp2_PIXEL_WIDTH, sp2_PIXEL_HEIGHT); - sp2_pixel_grid_fill(pixels, true); + sp_PixelGrid *pixels = sp_pixel_grid_new(sp_PIXEL_WIDTH, sp_PIXEL_HEIGHT); + sp_pixel_grid_fill(pixels, true); - sp2_Command *command = sp2_command_bitmap_linear_win(0, 0, pixels, Uncompressed); - sp2_Packet *packet = sp2_packet_from_command(command); - if (!sp2_connection_send(connection, packet)) + sp_Command *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); + sp_Packet *packet = sp_packet_from_command(command); + if (!sp_connection_send(connection, packet)) return 1; - sp2_connection_dealloc(connection); + sp_connection_dealloc(connection); return 0; } ``` @@ -52,12 +52,12 @@ You have the choice of linking statically (recommended) or dynamically. ## Notes on differences to rust library -- function names are: `sp2_` \ \. -- Use the rust documentation. +- function names are: `sp_` \ \. - Instances get consumed in the same way they do when writing rust / C# code. Do not use an instance after an (implicit!) free. - Option or Result turn into nullable return values - check for NULL! - There are no specifics for C++ here yet. You might get a nicer header when generating directly for C++, but it should be usable. - Reading and writing to instances concurrently is not safe. Only reading concurrently is safe. +- documentation is included in the header and available [online](https://docs.rs/servicepoint_binding_c/latest/servicepoint_binding_c/) ## Everything else From 6d562a49621b2cf965ea57405af249c4968188bb Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Jun 2024 13:13:49 +0200 Subject: [PATCH 03/16] remove Grid::window(), move Grid::new() to implementations --- crates/servicepoint/src/byte_grid.rs | 38 ++++++-------- crates/servicepoint/src/grid.rs | 73 ++++++++++++--------------- crates/servicepoint/src/pixel_grid.rs | 64 ++++++++--------------- 3 files changed, 69 insertions(+), 106 deletions(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index d9b515c..bc06534 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -9,6 +9,22 @@ pub struct ByteGrid { } impl ByteGrid { + /// Creates a new `ByteGrid` with the specified dimensions. + /// + /// # Arguments + /// + /// - width: size in x-direction + /// - height: size in y-direction + /// + /// returns: `ByteGrid` initialized to 0. + pub fn new(width: usize, height: usize) -> Self { + Self { + data: vec![0; width * height], + width, + height, + } + } + /// Loads a `ByteGrid` with the specified dimensions from the provided data. /// /// returns: `ByteGrid` that contains a copy of the provided data @@ -41,17 +57,6 @@ impl ByteGrid { } impl Grid for ByteGrid { - /// Creates a new `ByteGrid` with the specified dimensions. - /// - /// returns: `ByteGrid` initialized to 0. - fn new(width: usize, height: usize) -> Self { - Self { - data: vec![0; width * height], - width, - height, - } - } - /// Sets the value of the cell at the specified position in the `ByteGrid. /// /// # Arguments @@ -97,17 +102,6 @@ impl Grid for ByteGrid { fn height(&self) -> usize { self.height } - - fn window(&self, x: usize, y: usize, w: usize, h: usize) -> Self { - let mut win = Self::new(w, h); - for win_x in 0..w { - for win_y in 0..h { - let value = self.get(x + win_x, y + win_y); - win.set(win_x, win_y, value); - } - } - win - } } impl DataRef for ByteGrid { diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs index 1a53f77..b9502f4 100644 --- a/crates/servicepoint/src/grid.rs +++ b/crates/servicepoint/src/grid.rs @@ -1,24 +1,47 @@ /// A two-dimensional grid of `T` pub trait Grid { - #[must_use] - /// Creates a new Grid with the specified dimensions. + /// Sets the value at the specified position /// /// # Arguments /// - /// - width: size in x-direction - /// - height: size in y-direction - /// - /// returns: Grid with all cells initialized to default state. - fn new(width: usize, height: usize) -> Self; - - /// Sets the value at the specified position + /// * `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; /// Get the current value at the specified position + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell to read + /// + /// # Panics + /// + /// When accessing `x` or `y` out of bounds. fn get(&self, x: usize, y: usize) -> T; + /// Get the current value at the specified position if the position is inside of bounds + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell to read + /// + /// returns: Value at position or None + fn get_optional(&self, x: isize, y: isize) -> Option; + + /// Sets the value at the specified position if the position is inside of bounds + /// + /// # Arguments + /// + /// * `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; + /// Sets all cells in the grid to the specified value fn fill(&mut self, value: T); @@ -27,36 +50,4 @@ pub trait Grid { /// the height in y-direction fn height(&self) -> usize; - - /// Creates a new instance containing the specified window. - /// - /// Use concrete types to avoid boxing. - /// - /// # Arguments - /// - /// * `x`: column of the top left cell - /// * `y`: row of the top left cell - /// * `w`: size of window in x-direction - /// * `h`: size of window in y-direction - /// - /// returns: Self - /// - /// # Examples - /// To avoid boxing, this example is using the concrete type `ByteGrid`. - /// ``` - /// use servicepoint::{ByteGrid, Grid}; - /// fn split(grid: ByteGrid) -> (ByteGrid, ByteGrid) { - /// assert!(grid.width() >= 2); - /// let split_x = grid.width() / 2; - /// let right_w = grid.width() - split_x; - /// - /// let left = grid.window(0, 0, split_x, grid.height()); - /// let right = grid.window(split_x, 0, right_w, grid.height()); - /// (left, right) - /// } - /// - /// let (l, r) = split(ByteGrid::new(9, 5)); - /// ``` - #[must_use] - fn window(&self, x: usize, y: usize, w: usize, h: usize) -> Self; } diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index 2b7ec20..c6d02dd 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -9,6 +9,27 @@ pub struct PixelGrid { } impl PixelGrid { + /// Creates a new `PixelGrid` with the specified dimensions. + /// + /// # Arguments + /// + /// * `width`: size in pixels in x-direction + /// * `height`: size in pixels in y-direction + /// + /// returns: `PixelGrid` initialized to all pixels off + /// + /// # Panics + /// + /// - when the width is not dividable by 8 + pub fn new(width: usize, height: usize) -> Self { + assert_eq!(width % 8, 0); + Self { + width, + height, + bit_vec: BitVec::new(width * height), + } + } + /// Creates a new pixel grid with the size of the whole screen. #[must_use] pub fn max_sized() -> Self { @@ -54,27 +75,6 @@ impl PixelGrid { } impl Grid for PixelGrid { - /// Creates a new `PixelGrid` with the specified dimensions. - /// - /// # Arguments - /// - /// * `width`: size in pixels in x-direction - /// * `height`: size in pixels in y-direction - /// - /// returns: `PixelGrid` initialized to all pixels off - /// - /// # Panics - /// - /// - when the width is not dividable by 8 - fn new(width: usize, height: usize) -> Self { - assert_eq!(width % 8, 0); - Self { - width, - height, - bit_vec: BitVec::new(width * height), - } - } - /// Sets the value of the specified position in the `PixelGrid`. /// /// # Arguments @@ -92,15 +92,6 @@ impl Grid for PixelGrid { self.bit_vec.set(x + y * self.width, value) } - /// Gets the current value at the specified position. - /// - /// # Arguments - /// - /// * `x` and `y`: position of the cell to read - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. fn get(&self, x: usize, y: usize) -> bool { self.bit_vec.get(x + y * self.width) } @@ -122,19 +113,6 @@ impl Grid for PixelGrid { fn height(&self) -> usize { self.height } - - fn window(&self, x: usize, y: usize, w: usize, h: usize) -> Self { - // TODO: how to deduplicate? - // this cannot be moved into the trait because there, Self is not Sized - let mut win = Self::new(w, h); - for win_x in 0..w { - for win_y in 0..h { - let value = self.get(x + win_x, y + win_y); - win.set(win_x, win_y, value); - } - } - win - } } impl DataRef for PixelGrid { From ffe3a87319cb7429a93d548c9ccf21489496929a Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Jun 2024 13:16:26 +0200 Subject: [PATCH 04/16] add grid functions for getting references or optional values --- crates/servicepoint/src/byte_grid.rs | 54 +++++++++++++++++++++++++++ crates/servicepoint/src/grid.rs | 43 +++++++++++++++++++++ crates/servicepoint/src/lib.rs | 2 +- crates/servicepoint/src/pixel_grid.rs | 25 +++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index bc06534..bc58c7e 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -1,3 +1,4 @@ +use crate::grid::RefGrid; use crate::{DataRef, Grid}; /// A 2D grid of bytes @@ -42,6 +43,10 @@ impl ByteGrid { } } + fn is_position_valid(&self, x: isize, y: isize) -> bool { + x > 0 && x < self.width as isize && y > 0 && y < self.height as isize + } + fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -54,6 +59,11 @@ impl ByteGrid { self.height ); } + + pub fn get_mut(&mut self, x: usize, y: usize) -> &mut u8 { + self.check_indexes(x, y); + &mut self.data[x + y * self.width] + } } impl Grid for ByteGrid { @@ -91,6 +101,22 @@ impl Grid for ByteGrid { self.data[x + y * self.width] } + fn get_optional(&self, x: isize, y: isize) -> Option { + if self.is_position_valid(x, y) { + Some(self.get(x as usize, y as usize)) + } else { + None + } + } + + fn set_optional(&mut self, x: isize, y: isize, value: u8) -> Option { + if self.is_position_valid(x, y) { + Some(self.set(x as usize, y as usize, value)) + } else { + None + } + } + fn fill(&mut self, value: u8) { self.data.fill(value); } @@ -123,6 +149,34 @@ impl From for Vec { } } +impl RefGrid for ByteGrid { + fn get_ref(&self, x: usize, y: usize) -> &u8 { + self.check_indexes(x, y); + &self.data[x + y * self.width] + } + + fn get_ref_optional(&self, x: isize, y: isize) -> Option<&u8> { + if self.is_position_valid(x, y) { + Some(&self.data[x as usize + y as usize * self.width]) + } else { + None + } + } + + fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut u8 { + self.check_indexes(x, y); + &mut self.data[x + y * self.width] + } + + fn get_ref_mut_optional(&mut self, x: isize, y: isize) -> Option<&mut u8> { + if self.is_position_valid(x, y) { + Some(&mut self.data[x as usize + y as usize * self.width]) + } else { + None + } + } +} + #[cfg(test)] mod tests { use crate::{ByteGrid, DataRef, Grid}; diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs index b9502f4..79cdbfb 100644 --- a/crates/servicepoint/src/grid.rs +++ b/crates/servicepoint/src/grid.rs @@ -51,3 +51,46 @@ pub trait Grid { /// the height in y-direction fn height(&self) -> usize; } + +/// A grid that can return cells as references. +pub trait RefGrid { + /// Get a reference to the current value at the specified position + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell + /// + /// # Panics + /// + /// When accessing `x` or `y` out of bounds. + fn get_ref(&self, x: usize, y: usize) -> &T; + + /// Get a reference to the current value at the specified position if the position is in bounds. + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell + /// + /// returns: Reference to cell or None + fn get_ref_optional(&self, x: isize, y: isize) -> Option<&T>; + + /// Get a mutable reference to the current value at the specified position + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell + /// + /// # Panics + /// + /// When accessing `x` or `y` out of bounds. + fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut T; + + /// Get a mutable reference to the current value at the specified position if position is in bounds. + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell + /// + /// returns: Reference to cell or None + fn get_ref_mut_optional(&mut self, x: isize, y: isize) -> Option<&mut T>; +} diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index d68801c..1951087 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -8,7 +8,7 @@ pub use crate::command::{Brightness, Command, Offset, Origin}; pub use crate::compression_code::CompressionCode; pub use crate::connection::Connection; pub use crate::data_ref::DataRef; -pub use crate::grid::Grid; +pub use crate::grid::{Grid, RefGrid}; pub use crate::packet::{Header, Packet, Payload}; pub use crate::pixel_grid::PixelGrid; diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index c6d02dd..e6a3840 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -60,6 +60,10 @@ impl PixelGrid { } } + fn is_position_valid(&self, x: isize, y: isize) -> bool { + x > 0 && x < self.width as isize && y > 0 && y < self.height as isize + } + fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -96,6 +100,27 @@ impl Grid for PixelGrid { self.bit_vec.get(x + y * self.width) } + fn get_optional(&self, x: isize, y: isize) -> Option { + if self.is_position_valid(x, y) { + Some(self.get(x as usize, y as usize)) + } else { + None + } + } + + fn set_optional( + &mut self, + x: isize, + y: isize, + value: bool, + ) -> Option { + if self.is_position_valid(x, y) { + Some(self.set(x as usize, y as usize, value)) + } else { + None + } + } + /// Sets the state of all pixels in the `PixelGrid`. /// /// # Arguments From c017b85962bdb915156a9ddf82159fed0c21e841 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Jun 2024 13:20:31 +0200 Subject: [PATCH 05/16] remove duplicate fn --- crates/servicepoint/src/byte_grid.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index bc58c7e..163237a 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -59,11 +59,6 @@ impl ByteGrid { self.height ); } - - pub fn get_mut(&mut self, x: usize, y: usize) -> &mut u8 { - self.check_indexes(x, y); - &mut self.data[x + y * self.width] - } } impl Grid for ByteGrid { From b8baf8127d6af1b93e84ed52ca47645fbe2071db Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Jun 2024 14:00:31 +0200 Subject: [PATCH 06/16] remove duplicate code into trait impl --- crates/servicepoint/src/byte_grid.rs | 24 ++---------------------- crates/servicepoint/src/grid.rs | 24 ++++++++++++++++++++++-- crates/servicepoint/src/pixel_grid.rs | 25 ------------------------- 3 files changed, 24 insertions(+), 49 deletions(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index 163237a..d6d5759 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -43,10 +43,6 @@ impl ByteGrid { } } - fn is_position_valid(&self, x: isize, y: isize) -> bool { - x > 0 && x < self.width as isize && y > 0 && y < self.height as isize - } - fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -96,22 +92,6 @@ impl Grid for ByteGrid { self.data[x + y * self.width] } - fn get_optional(&self, x: isize, y: isize) -> Option { - if self.is_position_valid(x, y) { - Some(self.get(x as usize, y as usize)) - } else { - None - } - } - - fn set_optional(&mut self, x: isize, y: isize, value: u8) -> Option { - if self.is_position_valid(x, y) { - Some(self.set(x as usize, y as usize, value)) - } else { - None - } - } - fn fill(&mut self, value: u8) { self.data.fill(value); } @@ -151,7 +131,7 @@ impl RefGrid for ByteGrid { } fn get_ref_optional(&self, x: isize, y: isize) -> Option<&u8> { - if self.is_position_valid(x, y) { + if self.is_in_bounds(x, y) { Some(&self.data[x as usize + y as usize * self.width]) } else { None @@ -164,7 +144,7 @@ impl RefGrid for ByteGrid { } fn get_ref_mut_optional(&mut self, x: isize, y: isize) -> Option<&mut u8> { - if self.is_position_valid(x, y) { + if self.is_in_bounds(x, y) { Some(&mut self.data[x as usize + y as usize * self.width]) } else { None diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs index 79cdbfb..76c80db 100644 --- a/crates/servicepoint/src/grid.rs +++ b/crates/servicepoint/src/grid.rs @@ -31,7 +31,13 @@ pub trait Grid { /// * `x` and `y`: position of the cell to read /// /// returns: Value at position or None - fn get_optional(&self, x: isize, y: isize) -> Option; + fn get_optional(&self, x: isize, y: isize) -> Option { + if self.is_in_bounds(x, y) { + Some(self.get(x as usize, y as usize)) + } else { + None + } + } /// Sets the value at the specified position if the position is inside of bounds /// @@ -40,7 +46,13 @@ 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) -> Option { + if self.is_in_bounds(x, y) { + Some(self.set(x as usize, y as usize, value)) + } else { + None + } + } /// Sets all cells in the grid to the specified value fn fill(&mut self, value: T); @@ -50,6 +62,14 @@ pub trait Grid { /// the height in y-direction fn height(&self) -> usize; + + /// Checks whether the specified position is + fn is_in_bounds(&self, x: isize, y: isize) -> bool { + x > 0 + && x < self.width() as isize + && y > 0 + && y < self.height() as isize + } } /// A grid that can return cells as references. diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index e6a3840..c6d02dd 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -60,10 +60,6 @@ impl PixelGrid { } } - fn is_position_valid(&self, x: isize, y: isize) -> bool { - x > 0 && x < self.width as isize && y > 0 && y < self.height as isize - } - fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -100,27 +96,6 @@ impl Grid for PixelGrid { self.bit_vec.get(x + y * self.width) } - fn get_optional(&self, x: isize, y: isize) -> Option { - if self.is_position_valid(x, y) { - Some(self.get(x as usize, y as usize)) - } else { - None - } - } - - fn set_optional( - &mut self, - x: isize, - y: isize, - value: bool, - ) -> Option { - if self.is_position_valid(x, y) { - Some(self.set(x as usize, y as usize, value)) - } else { - None - } - } - /// Sets the state of all pixels in the `PixelGrid`. /// /// # Arguments From ee0c9dceab6bd083f5e58b828caaaa30f7527746 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Jun 2024 14:03:40 +0200 Subject: [PATCH 07/16] add some iterators --- crates/servicepoint/src/bit_vec.rs | 27 +++++++++++++++++++++++++++ crates/servicepoint/src/byte_grid.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/crates/servicepoint/src/bit_vec.rs b/crates/servicepoint/src/bit_vec.rs index 7b2d5c1..5af9da8 100644 --- a/crates/servicepoint/src/bit_vec.rs +++ b/crates/servicepoint/src/bit_vec.rs @@ -101,6 +101,14 @@ impl BitVec { self.data.is_empty() } + /// Get an iterator over every bit in the vector + pub fn iter(&self) -> Iter { + Iter { + bit_vec: self, + index: 0, + } + } + /// Calculates the byte index and bitmask for a specific bit in the vector fn get_indexes(&self, bit_index: usize) -> (usize, u8) { assert!( @@ -143,6 +151,25 @@ impl From<&[u8]> for BitVec { } } +pub struct Iter<'t> { + bit_vec: &'t BitVec, + index: usize, +} + +impl<'t> Iterator for Iter<'t> { + type Item = bool; + + fn next(&mut self) -> Option { + if self.index >= self.bit_vec.size { + return None; + } + + let result = Some(self.bit_vec.get(self.index)); + self.index += 1; + result + } +} + #[cfg(test)] mod tests { use crate::{BitVec, DataRef}; diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index d6d5759..7b3f78b 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -43,6 +43,13 @@ impl ByteGrid { } } + pub fn iter(&self) -> Iter { + Iter { + byte_grid: self, + index: 0, + } + } + fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -152,6 +159,25 @@ impl RefGrid for ByteGrid { } } +pub struct Iter<'t> { + byte_grid: &'t ByteGrid, + index: usize, +} + +impl<'t> Iterator for Iter<'t> { + type Item = u8; + + fn next(&mut self) -> Option { + if self.index >= self.byte_grid.data.len() { + return None; + } + + let result = self.byte_grid.data[self.index]; + self.index += 1; + Some(result) + } +} + #[cfg(test)] mod tests { use crate::{ByteGrid, DataRef, Grid}; From 17d41ef6c66e9ca7bedd38bb72446a811931aeaf Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Jun 2024 14:34:22 +0200 Subject: [PATCH 08/16] add more iterators --- crates/servicepoint/src/bit_vec.rs | 8 +++-- crates/servicepoint/src/byte_grid.rs | 47 ++++++++++++++++++++++----- crates/servicepoint/src/grid.rs | 7 ++++ crates/servicepoint/src/pixel_grid.rs | 33 +++++++++++++++++++ 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/crates/servicepoint/src/bit_vec.rs b/crates/servicepoint/src/bit_vec.rs index 5af9da8..23719a7 100644 --- a/crates/servicepoint/src/bit_vec.rs +++ b/crates/servicepoint/src/bit_vec.rs @@ -106,6 +106,7 @@ impl BitVec { Iter { bit_vec: self, index: 0, + end: self.size, } } @@ -152,15 +153,16 @@ impl From<&[u8]> for BitVec { } pub struct Iter<'t> { - bit_vec: &'t BitVec, - index: usize, + 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.bit_vec.size { + if self.index >= self.end { return None; } diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index 7b3f78b..5dbc654 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -43,13 +43,6 @@ impl ByteGrid { } } - pub fn iter(&self) -> Iter { - Iter { - byte_grid: self, - index: 0, - } - } - fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -99,6 +92,21 @@ impl Grid for ByteGrid { self.data[x + y * self.width] } + fn iter(&self) -> impl Iterator { + Iter { + byte_grid: self, + index: 0, + end: self.data.len(), + } + } + + fn iter_rows(&self) -> impl Iterator> { + IterRows { + byte_grid: self, + row: 0, + } + } + fn fill(&mut self, value: u8) { self.data.fill(value); } @@ -162,13 +170,14 @@ impl RefGrid for ByteGrid { pub struct Iter<'t> { byte_grid: &'t ByteGrid, index: usize, + end: usize, } impl<'t> Iterator for Iter<'t> { type Item = u8; fn next(&mut self) -> Option { - if self.index >= self.byte_grid.data.len() { + if self.index >= self.end { return None; } @@ -178,6 +187,28 @@ impl<'t> Iterator for Iter<'t> { } } +pub struct IterRows<'t> { + byte_grid: &'t ByteGrid, + row: usize, +} + +impl<'t> Iterator for IterRows<'t> { + type Item = Iter<'t>; + + fn next(&mut self) -> Option { + if self.row >= self.byte_grid.height { + return None; + } + let result = Some(Iter { + byte_grid: self.byte_grid, + index: self.row * self.byte_grid.width, + end: (self.row + 1) * self.byte_grid.width, + }); + self.row += 1; + result + } +} + #[cfg(test)] mod tests { use crate::{ByteGrid, DataRef, Grid}; diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs index 76c80db..c9d93e0 100644 --- a/crates/servicepoint/src/grid.rs +++ b/crates/servicepoint/src/grid.rs @@ -54,6 +54,13 @@ pub trait Grid { } } + /// Get an iterator over every cell in the grid. + /// + /// Iteration is done in memory order, rows first. + fn iter(&self) -> impl Iterator; + + fn iter_rows(&self) -> impl Iterator>; + /// Sets all cells in the grid to the specified value fn fill(&mut self, value: T); diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index c6d02dd..3f55a70 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -96,6 +96,17 @@ impl Grid for PixelGrid { self.bit_vec.get(x + y * self.width) } + fn iter(&self) -> impl Iterator { + self.bit_vec.iter() + } + + fn iter_rows(&self) -> impl Iterator> { + IterRows { + pixel_grid: self, + row: 0, + } + } + /// Sets the state of all pixels in the `PixelGrid`. /// /// # Arguments @@ -132,6 +143,28 @@ impl From for Vec { } } +pub struct IterRows<'t> { + pixel_grid: &'t PixelGrid, + row: usize, +} + +impl<'t> Iterator for IterRows<'t> { + type Item = crate::bit_vec::Iter<'t>; + + 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, + }); + self.row += 1; + result + } +} + #[cfg(test)] mod tests { use crate::{DataRef, Grid, PixelGrid}; From c600761f290a671d971710b1883ce5f9e5f61e14 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 3 Jun 2024 19:32:10 +0200 Subject: [PATCH 09/16] remove iter from trait --- crates/servicepoint/src/byte_grid.rs | 68 ++++++++++----------------- crates/servicepoint/src/grid.rs | 7 --- crates/servicepoint/src/pixel_grid.rs | 22 ++++----- 3 files changed, 37 insertions(+), 60 deletions(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index 5dbc654..40721ba 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -1,3 +1,5 @@ +use std::slice::{Iter, IterMut}; + use crate::grid::RefGrid; use crate::{DataRef, Grid}; @@ -43,6 +45,24 @@ impl ByteGrid { } } + pub fn iter(&self) -> Iter { + self.data.iter() + } + + pub fn iter_rows(&self) -> IterRows { + IterRows { + byte_grid: self, + row: 0, + } + } + + /// Returns an iterator that allows modifying each value. + /// + /// The iterator yields all cells from top left to bottom right. + pub fn iter_mut(&mut self) -> IterMut { + self.data.iter_mut() + } + fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -92,21 +112,6 @@ impl Grid for ByteGrid { self.data[x + y * self.width] } - fn iter(&self) -> impl Iterator { - Iter { - byte_grid: self, - index: 0, - end: self.data.len(), - } - } - - fn iter_rows(&self) -> impl Iterator> { - IterRows { - byte_grid: self, - row: 0, - } - } - fn fill(&mut self, value: u8) { self.data.fill(value); } @@ -167,45 +172,24 @@ impl RefGrid for ByteGrid { } } -pub struct Iter<'t> { - byte_grid: &'t ByteGrid, - index: usize, - end: usize, -} - -impl<'t> Iterator for Iter<'t> { - type Item = u8; - - fn next(&mut self) -> Option { - if self.index >= self.end { - return None; - } - - let result = self.byte_grid.data[self.index]; - self.index += 1; - Some(result) - } -} - pub struct IterRows<'t> { byte_grid: &'t ByteGrid, row: usize, } impl<'t> Iterator for IterRows<'t> { - type Item = Iter<'t>; + type Item = Iter<'t, u8>; fn next(&mut self) -> Option { if self.row >= self.byte_grid.height { return None; } - let result = Some(Iter { - byte_grid: self.byte_grid, - index: self.row * self.byte_grid.width, - end: (self.row + 1) * self.byte_grid.width, - }); + + let start = self.row * self.byte_grid.width; + let end = start + self.byte_grid.width; + let result = self.byte_grid.data[start..end].iter(); self.row += 1; - result + Some(result) } } diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs index c9d93e0..76c80db 100644 --- a/crates/servicepoint/src/grid.rs +++ b/crates/servicepoint/src/grid.rs @@ -54,13 +54,6 @@ pub trait Grid { } } - /// Get an iterator over every cell in the grid. - /// - /// Iteration is done in memory order, rows first. - fn iter(&self) -> impl Iterator; - - fn iter_rows(&self) -> impl Iterator>; - /// Sets all cells in the grid to the specified value fn fill(&mut self, value: T); diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index 3f55a70..6a24d79 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -60,6 +60,17 @@ impl PixelGrid { } } + pub fn iter(&self) -> crate::bit_vec::Iter { + self.bit_vec.iter() + } + + pub fn iter_rows(&self) -> IterRows { + IterRows { + pixel_grid: self, + row: 0, + } + } + fn check_indexes(&self, x: usize, y: usize) { assert!( x < self.width, @@ -96,17 +107,6 @@ impl Grid for PixelGrid { self.bit_vec.get(x + y * self.width) } - fn iter(&self) -> impl Iterator { - self.bit_vec.iter() - } - - fn iter_rows(&self) -> impl Iterator> { - IterRows { - pixel_grid: self, - row: 0, - } - } - /// Sets the state of all pixels in the `PixelGrid`. /// /// # Arguments From 947a3fe60ed2d0b9daedba04d1cac8b6df981186 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 3 Jun 2024 21:08:26 +0200 Subject: [PATCH 10/16] replace bit_vec module with bitvec library --- Cargo.lock | 40 +++ crates/servicepoint/Cargo.toml | 1 + crates/servicepoint/examples/wiping_clear.rs | 4 +- crates/servicepoint/src/bit_vec.rs | 257 ------------------ crates/servicepoint/src/byte_grid.rs | 9 +- crates/servicepoint/src/command.rs | 48 ++-- crates/servicepoint/src/grid.rs | 11 +- crates/servicepoint/src/lib.rs | 6 +- crates/servicepoint/src/pixel_grid.rs | 33 +-- crates/servicepoint_binding_c/src/bit_vec.rs | 67 +++-- crates/servicepoint_binding_c/src/command.rs | 22 +- crates/servicepoint_binding_c/src/lib.rs | 2 +- .../ServicePoint/BindGen/ServicePoint.g.cs | 39 ++- .../ServicePoint/BitVec.cs | 4 +- .../ServicePoint/ServicePoint.csproj | 12 +- crates/servicepoint_binding_cs/build.rs | 1 - 16 files changed, 187 insertions(+), 369 deletions(-) delete mode 100644 crates/servicepoint/src/bit_vec.rs 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") From a4d53d0e5623ddb0cc26b64433fe36115bde0108 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 3 Jun 2024 22:10:52 +0200 Subject: [PATCH 11/16] add missing docs --- crates/servicepoint/src/byte_grid.rs | 13 +++++++++++++ crates/servicepoint/src/command.rs | 5 ++--- crates/servicepoint/src/pixel_grid.rs | 13 +++++++++++++ crates/servicepoint_binding_c/src/bit_vec.rs | 1 + crates/servicepoint_binding_c/src/c_slice.rs | 2 ++ 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index 0462b90..e02217f 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -45,10 +45,23 @@ impl ByteGrid { } } + /// Iterate over all cells in `ByteGrid`. + /// + /// Order is equivalent to the following loop: + /// ``` + /// # use servicepoint::{ByteGrid, Grid}; + /// # let grid = ByteGrid::new(2,2); + /// for y in 0..grid.height() { + /// for x in 0..grid.width() { + /// grid.get(x, y) + /// } + /// } + /// ``` pub fn iter(&self) -> Iter { self.data.iter() } + /// Iterate over all rows in `ByteGrid` top to bottom. pub fn iter_rows(&self) -> IterRows { IterRows { byte_grid: self, diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs index 856ba9c..b488426 100644 --- a/crates/servicepoint/src/command.rs +++ b/crates/servicepoint/src/command.rs @@ -1,4 +1,4 @@ -use bitvec::prelude::{BitVec, Msb0}; +use bitvec::prelude::BitVec; use crate::command_code::CommandCode; use crate::compression::{into_compressed, into_decompressed}; @@ -27,7 +27,6 @@ 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 { @@ -369,7 +368,7 @@ impl Command { /// Helper method for Packets into `BitMapLinear*`-Commands fn packet_into_linear_bitmap( packet: Packet, - ) -> Result<(BitVec, CompressionCode), TryFromPacketError> { + ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { let Packet(Header(_, _, length, sub, reserved), payload) = packet; if reserved != 0 { return Err(TryFromPacketError::ExtraneousHeaderValues); diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index 67c8ac6..1ff865d 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -63,10 +63,23 @@ impl PixelGrid { } } + /// Iterate over all cells in `PixelGrid`. + /// + /// Order is equivalent to the following loop: + /// ``` + /// # use servicepoint::{PixelGrid, Grid}; + /// # let grid = PixelGrid::new(8,2); + /// for y in 0..grid.height() { + /// for x in 0..grid.width() { + /// grid.get(x, y) + /// } + /// } + /// ``` pub fn iter(&self) -> Iter<'_, u8, Msb0> { self.bit_vec.iter() } + /// Iterate over all rows in `PixelGrid` top to bottom. pub fn iter_rows(&self) -> IterRows { IterRows { pixel_grid: self, diff --git a/crates/servicepoint_binding_c/src/bit_vec.rs b/crates/servicepoint_binding_c/src/bit_vec.rs index 995d2f6..2592aea 100644 --- a/crates/servicepoint_binding_c/src/bit_vec.rs +++ b/crates/servicepoint_binding_c/src/bit_vec.rs @@ -8,6 +8,7 @@ use servicepoint::bitvec::prelude::{BitVec, Msb0}; /// cbindgen:no-export type SpBitVec = BitVec; +/// Opaque struct needed for correct code generation. #[derive(Clone)] pub struct CBitVec { actual: SpBitVec, diff --git a/crates/servicepoint_binding_c/src/c_slice.rs b/crates/servicepoint_binding_c/src/c_slice.rs index b255c1b..566213d 100644 --- a/crates/servicepoint_binding_c/src/c_slice.rs +++ b/crates/servicepoint_binding_c/src/c_slice.rs @@ -1,3 +1,5 @@ +//! FFI slice helper + #[repr(C)] /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. /// From 59301a5fc51acbf46c63c710a772000d9f986fbf Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 3 Jun 2024 22:48:12 +0200 Subject: [PATCH 12/16] add PixelGrid::iter_mut, tests --- crates/servicepoint/src/byte_grid.rs | 46 ++++++++++++++++++++++++ crates/servicepoint/src/pixel_grid.rs | 52 ++++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index e02217f..c9f8a63 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -256,4 +256,50 @@ mod tests { assert_eq!(vec.data, [1, 2, 3, 4]); assert_eq!(vec.get(1, 0), 2) } + + #[test] + fn iter() { + let mut vec = ByteGrid::new(2, 2); + vec.set(1, 1, 5); + + let mut iter = vec.iter(); + assert_eq!(*iter.next().unwrap(), 0); + assert_eq!(*iter.next().unwrap(), 0); + assert_eq!(*iter.next().unwrap(), 0); + assert_eq!(*iter.next().unwrap(), 5); + } + + #[test] + fn iter_mut() { + let mut vec = ByteGrid::new(2, 3); + for (index, cell) in vec.iter_mut().enumerate() { + *cell = index as u8; + } + + assert_eq!(vec.data_ref(), [0, 1, 2, 3, 4, 5]); + } + + #[test] + fn iter_rows() { + let vec = ByteGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); + for (y, row) in vec.iter_rows().enumerate() { + for (x, val) in row.enumerate() { + assert_eq!(*val, (x + y) as u8); + } + } + } + + #[test] + #[should_panic] + fn out_of_bounds_x() { + let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); + vec.set(2, 1, 5); + } + + #[test] + #[should_panic] + fn out_of_bounds_y() { + let vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); + vec.get(1, 2); + } } diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index 1ff865d..bb8d4c2 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -1,7 +1,9 @@ -use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH}; use bitvec::order::Msb0; use bitvec::prelude::BitSlice; -use bitvec::slice::Iter; +use bitvec::ptr::Mutability; +use bitvec::slice::IterMut; + +use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH}; /// A grid of pixels stored in packed bytes. #[derive(Debug, Clone, PartialEq)] @@ -75,8 +77,35 @@ impl PixelGrid { /// } /// } /// ``` - pub fn iter(&self) -> Iter<'_, u8, Msb0> { - self.bit_vec.iter() + pub fn iter(&self) -> impl Iterator { + self.bit_vec.iter().by_refs() + } + + /// Iterate over all cells in `PixelGrid` mutably. + /// + /// Order is equivalent to the following loop: + /// ``` + /// # use servicepoint::{PixelGrid, Grid}; + /// # let mut grid = PixelGrid::new(8,2); + /// # let value = false; + /// for y in 0..grid.height() { + /// for x in 0..grid.width() { + /// grid.set(x, y, value) + /// } + /// } + /// ``` + /// + /// # Example + /// ``` + /// # use servicepoint::{PixelGrid, Grid}; + /// # let mut grid = PixelGrid::new(8,2); + /// # let value = false; + /// for (index, mut pixel) in grid.iter_mut().enumerate() { + /// pixel.set(index % 2 == 0) + /// } + /// ``` + pub fn iter_mut(&mut self) -> IterMut { + self.bit_vec.iter_mut() } /// Iterate over all rows in `PixelGrid` top to bottom. @@ -120,6 +149,7 @@ impl Grid for PixelGrid { } fn get(&self, x: usize, y: usize) -> bool { + self.check_indexes(x, y); self.bit_vec[x + y * self.width] } @@ -226,4 +256,18 @@ mod tests { let grid = PixelGrid::load(8, 3, &data); assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]); } + + #[test] + #[should_panic] + fn out_of_bounds_x() { + let vec = PixelGrid::new(8, 2); + vec.get(8, 1); + } + + #[test] + #[should_panic] + fn out_of_bounds_y() { + let mut vec = PixelGrid::new(8, 2); + vec.set(1, 2, false); + } } From 9a4987787ed4f7390d83260b9e2de31d6732ca97 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 3 Jun 2024 22:49:00 +0200 Subject: [PATCH 13/16] add brightness tester --- .../examples/brightness_tester.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 crates/servicepoint/examples/brightness_tester.rs diff --git a/crates/servicepoint/examples/brightness_tester.rs b/crates/servicepoint/examples/brightness_tester.rs new file mode 100644 index 0000000..b1b1ac3 --- /dev/null +++ b/crates/servicepoint/examples/brightness_tester.rs @@ -0,0 +1,37 @@ +//! Show a brightness level test pattern on screen + +use clap::Parser; + +use servicepoint::Command::BitmapLinearWin; +use servicepoint::*; + +#[derive(Parser, Debug)] +struct Cli { + #[arg(short, long, default_value = "localhost:2342")] + destination: String, +} + +fn main() { + let cli = Cli::parse(); + let connection = Connection::open(cli.destination).unwrap(); + + let mut pixels = PixelGrid::max_sized(); + pixels.fill(true); + + connection + .send(BitmapLinearWin( + Origin(0, 0), + pixels, + CompressionCode::Uncompressed, + )) + .expect("send failed"); + + let mut brightnesses = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT); + for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() { + *byte = (index % u8::MAX as usize) as u8; + } + + connection + .send(Command::CharBrightness(Origin(0, 0), brightnesses)) + .expect("send failed"); +} From b08fd9706639920bc89fa4536a766441b26ff225 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 5 Jun 2024 20:14:05 +0200 Subject: [PATCH 14/16] more tests, move stuff --- crates/servicepoint/src/byte_grid.rs | 88 ++++++++++++++------------- crates/servicepoint/src/grid.rs | 59 ++++++------------ crates/servicepoint/src/lib.rs | 7 ++- crates/servicepoint/src/pixel_grid.rs | 47 ++++++++------ 4 files changed, 95 insertions(+), 106 deletions(-) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index c9f8a63..1912bb3 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -1,6 +1,5 @@ use std::slice::{Iter, IterMut}; -use crate::grid::RefGrid; use crate::{DataRef, Grid}; /// A 2D grid of bytes @@ -53,7 +52,7 @@ impl ByteGrid { /// # let grid = ByteGrid::new(2,2); /// for y in 0..grid.height() { /// for x in 0..grid.width() { - /// grid.get(x, y) + /// grid.get(x, y); /// } /// } /// ``` @@ -76,17 +75,37 @@ impl ByteGrid { self.data.iter_mut() } - fn check_indexes(&self, x: usize, y: usize) { - assert!( - x < self.width, - "cannot access byte {x}-{y} because x is outside of bounds 0..{}", - self.width - ); - assert!( - y < self.height, - "cannot access byte {x}-{y} because y is outside of bounds 0..{}", - self.height - ); + /// Get a mutable reference to the current value at the specified position. + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell + /// + /// # Panics + /// + /// When accessing `x` or `y` out of bounds. + pub fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut u8 { + self.assert_in_bounds(x, y); + &mut self.data[x + y * self.width] + } + + /// Get a mutable reference to the current value at the specified position if position is in bounds. + /// + /// # Arguments + /// + /// * `x` and `y`: position of the cell + /// + /// returns: Reference to cell or None + pub fn get_ref_mut_optional( + &mut self, + x: isize, + y: isize, + ) -> Option<&mut u8> { + if self.is_in_bounds(x, y) { + Some(&mut self.data[x as usize + y as usize * self.width]) + } else { + None + } } } @@ -102,7 +121,7 @@ impl Grid for ByteGrid { /// /// When accessing `x` or `y` out of bounds. fn set(&mut self, x: usize, y: usize, value: u8) { - self.check_indexes(x, y); + self.assert_in_bounds(x, y); self.data[x + y * self.width] = value; } @@ -116,7 +135,7 @@ impl Grid for ByteGrid { /// /// When accessing `x` or `y` out of bounds. fn get(&self, x: usize, y: usize) -> u8 { - self.check_indexes(x, y); + self.assert_in_bounds(x, y); self.data[x + y * self.width] } @@ -152,34 +171,6 @@ impl From for Vec { } } -impl RefGrid for ByteGrid { - fn get_ref(&self, x: usize, y: usize) -> &u8 { - self.check_indexes(x, y); - &self.data[x + y * self.width] - } - - fn get_ref_optional(&self, x: isize, y: isize) -> Option<&u8> { - if self.is_in_bounds(x, y) { - Some(&self.data[x as usize + y as usize * self.width]) - } else { - None - } - } - - fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut u8 { - self.check_indexes(x, y); - &mut self.data[x + y * self.width] - } - - fn get_ref_mut_optional(&mut self, x: isize, y: isize) -> Option<&mut u8> { - if self.is_in_bounds(x, y) { - Some(&mut self.data[x as usize + y as usize * self.width]) - } else { - None - } - } -} - pub struct IterRows<'t> { byte_grid: &'t ByteGrid, row: usize, @@ -302,4 +293,15 @@ mod tests { let vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); vec.get(1, 2); } + + #[test] + fn ref_mut() { + let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); + + let top_left = vec.get_ref_mut(0, 0); + *top_left += 5; + + assert_eq!(None, vec.get_ref_mut_optional(2, 2)); + assert_eq!(Some(&mut 5), vec.get_ref_mut_optional(0, 0)); + } } diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs index 882f678..7e71532 100644 --- a/crates/servicepoint/src/grid.rs +++ b/crates/servicepoint/src/grid.rs @@ -62,54 +62,29 @@ pub trait Grid { /// the height in y-direction fn height(&self) -> usize; - /// Checks whether the specified position is + /// Checks whether the specified signed position is in grid bounds fn is_in_bounds(&self, x: isize, y: isize) -> bool { - x > 0 + x >= 0 && x < self.width() as isize - && y > 0 + && y >= 0 && y < self.height() as isize } -} -/// A grid that can return cells as references. -pub trait RefGrid { - /// Get a reference to the current value at the specified position - /// - /// # Arguments - /// - /// * `x` and `y`: position of the cell + /// Asserts that the specified unsigned position is in grid bounds. /// /// # Panics /// - /// When accessing `x` or `y` out of bounds. - fn get_ref(&self, x: usize, y: usize) -> &T; - - /// Get a reference to the current value at the specified position if the position is in bounds. - /// - /// # Arguments - /// - /// * `x` and `y`: position of the cell - /// - /// returns: Reference to cell or None - fn get_ref_optional(&self, x: isize, y: isize) -> Option<&T>; - - /// Get a mutable reference to the current value at the specified position - /// - /// # Arguments - /// - /// * `x` and `y`: position of the cell - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut T; - - /// Get a mutable reference to the current value at the specified position if position is in bounds. - /// - /// # Arguments - /// - /// * `x` and `y`: position of the cell - /// - /// returns: Reference to cell or None - fn get_ref_mut_optional(&mut self, x: isize, y: isize) -> Option<&mut T>; + /// When the specified position is out of bounds for this grid. + fn assert_in_bounds(&self, x: usize, y: usize) { + assert!( + x < self.width(), + "cannot access index [{x}, {y}] because x is outside of bounds 0..{}", + self.width() + ); + assert!( + y < self.height(), + "cannot access byte [{x}, {y}] because y is outside of bounds 0..{}", + self.height() + ); + } } diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index dd03d96..dcb2755 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -2,16 +2,17 @@ use std::time::Duration; +pub use bitvec; +use bitvec::prelude::{BitVec, Msb0}; + pub use crate::byte_grid::ByteGrid; pub use crate::command::{Brightness, Command, Offset, Origin}; pub use crate::compression_code::CompressionCode; pub use crate::connection::Connection; pub use crate::data_ref::DataRef; -pub use crate::grid::{Grid, RefGrid}; +pub use crate::grid::Grid; pub use crate::packet::{Header, Packet, Payload}; pub use crate::pixel_grid::PixelGrid; -pub use bitvec; -use bitvec::prelude::{BitVec, Msb0}; type SpBitVec = BitVec; diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index bb8d4c2..f078a00 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -1,6 +1,5 @@ use bitvec::order::Msb0; use bitvec::prelude::BitSlice; -use bitvec::ptr::Mutability; use bitvec::slice::IterMut; use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH}; @@ -73,7 +72,7 @@ impl PixelGrid { /// # let grid = PixelGrid::new(8,2); /// for y in 0..grid.height() { /// for x in 0..grid.width() { - /// grid.get(x, y) + /// grid.get(x, y); /// } /// } /// ``` @@ -90,7 +89,7 @@ impl PixelGrid { /// # let value = false; /// for y in 0..grid.height() { /// for x in 0..grid.width() { - /// grid.set(x, y, value) + /// grid.set(x, y, value); /// } /// } /// ``` @@ -115,19 +114,6 @@ impl PixelGrid { row: 0, } } - - fn check_indexes(&self, x: usize, y: usize) { - assert!( - x < self.width, - "cannot access pixel {x}-{y} because x is outside of bounds 0..{}", - self.width - ); - assert!( - y < self.height, - "cannot access pixel {x}-{y} because y is outside of bounds 0..{}", - self.height - ); - } } impl Grid for PixelGrid { @@ -144,12 +130,12 @@ impl Grid for PixelGrid { /// /// When accessing `x` or `y` out of bounds. fn set(&mut self, x: usize, y: usize, value: bool) { - self.check_indexes(x, y); + self.assert_in_bounds(x, y); self.bit_vec.set(x + y * self.width, value) } fn get(&self, x: usize, y: usize) -> bool { - self.check_indexes(x, y); + self.assert_in_bounds(x, y); self.bit_vec[x + y * self.width] } @@ -270,4 +256,29 @@ mod tests { let mut vec = PixelGrid::new(8, 2); vec.set(1, 2, false); } + + #[test] + fn iter() { + let grid = PixelGrid::new(8, 2); + assert_eq!(16, grid.iter().count()) + } + + #[test] + fn iter_rows() { + let grid = PixelGrid::load(8, 2, &[0x04, 0x40]); + let mut iter = grid.iter_rows(); + + assert_eq!(iter.next().unwrap().count_ones(), 1); + assert_eq!(iter.next().unwrap().count_ones(), 1); + assert_eq!(None, iter.next()); + } + + #[test] + fn iter_mut() { + let mut grid = PixelGrid::new(8, 2); + for (index, mut pixel) in grid.iter_mut().enumerate() { + pixel.set(index % 2 == 0); + } + assert_eq!(grid.data_ref(), [0xAA, 0xAA]); + } } From 894de966bcb2263dcddc5e190f62dadbb6034245 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 5 Jun 2024 20:23:44 +0200 Subject: [PATCH 15/16] more tests --- crates/servicepoint/src/byte_grid.rs | 12 ++++++++++++ crates/servicepoint/src/packet.rs | 6 ++++++ crates/servicepoint/src/pixel_grid.rs | 8 ++++++++ 3 files changed, 26 insertions(+) diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index 1912bb3..74b9e3c 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -304,4 +304,16 @@ mod tests { assert_eq!(None, vec.get_ref_mut_optional(2, 2)); assert_eq!(Some(&mut 5), vec.get_ref_mut_optional(0, 0)); } + + #[test] + fn optional() { + let mut grid = ByteGrid::load(2, 2, &[0, 1, 2, 3]); + grid.set_optional(0, 0, 5); + grid.set_optional(-1, 0, 8); + grid.set_optional(0, 8, 42); + assert_eq!(grid.data, [5, 1, 2, 3]); + + assert_eq!(grid.get_optional(0, 0), Some(5)); + assert_eq!(grid.get_optional(0, 8), None); + } } diff --git a/crates/servicepoint/src/packet.rs b/crates/servicepoint/src/packet.rs index d6cb78b..5ad80f1 100644 --- a/crates/servicepoint/src/packet.rs +++ b/crates/servicepoint/src/packet.rs @@ -69,4 +69,10 @@ mod tests { let p = Packet::try_from(&*data).unwrap(); assert_eq!(p, Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23])); } + + #[test] + fn too_small() { + let data = vec![0u8; 4]; + assert_eq!(Packet::try_from(data.as_slice()), Err(())) + } } diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index f078a00..f8c34b4 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -281,4 +281,12 @@ mod tests { } assert_eq!(grid.data_ref(), [0xAA, 0xAA]); } + + #[test] + fn data_ref_mut() { + let mut grid = PixelGrid::new(8, 2); + let data = grid.data_ref_mut(); + data[1] = 0x0F; + assert!(grid.get(7, 1)); + } } From a488131481946bfa5fb5a994364f436bfbd88a4f Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 5 Jun 2024 20:29:41 +0200 Subject: [PATCH 16/16] c# project file --- crates/servicepoint_binding_cs/ServicePoint.sln | 6 ++++++ .../ServicePoint/ServicePoint.csproj | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/servicepoint_binding_cs/ServicePoint.sln b/crates/servicepoint_binding_cs/ServicePoint.sln index 3d278f9..c4d9165 100644 --- a/crates/servicepoint_binding_cs/ServicePoint.sln +++ b/crates/servicepoint_binding_cs/ServicePoint.sln @@ -4,6 +4,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "ServicePoin EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lang-cs", "examples/lang_cs/lang_cs.csproj", "{DA3B8B6E-993A-47DA-844B-F92AF520FF59}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{C2F8EC4A-2426-4DC3-990F-C43810B183F5}" + ProjectSection(SolutionItems) = preProject + ..\..\shell.nix = ..\..\shell.nix + ..\..\.envrc = ..\..\.envrc + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj index 127ffc5..103497f 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj +++ b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj @@ -35,13 +35,7 @@ - - - - - - - +