diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index bead3da..2b7ec20 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -75,15 +75,42 @@ impl Grid for PixelGrid { } } + /// Sets the value of the specified position in the `PixelGrid`. + /// + /// # Arguments + /// + /// * `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: bool) -> bool { self.check_indexes(x, y); 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) } + /// Sets the state of all pixels in the `PixelGrid`. + /// + /// # Arguments + /// + /// * `this`: instance to write to + /// * `value`: the value to set all pixels to fn fill(&mut self, value: bool) { self.bit_vec.fill(value); } diff --git a/crates/servicepoint_binding_c/src/byte_grid.rs b/crates/servicepoint_binding_c/src/byte_grid.rs index c77654a..d5dabb1 100644 --- a/crates/servicepoint_binding_c/src/byte_grid.rs +++ b/crates/servicepoint_binding_c/src/byte_grid.rs @@ -151,7 +151,7 @@ pub unsafe extern "C" fn sp_byte_grid_fill(this: *mut ByteGrid, value: u8) { (*this).fill(value); } -/// Gets the width in pixels of the `ByteGrid` instance. +/// Gets the width of the `ByteGrid` instance. /// /// # Arguments /// @@ -167,7 +167,7 @@ pub unsafe extern "C" fn sp_byte_grid_width(this: *const ByteGrid) -> usize { (*this).width() } -/// Gets the height in pixels of the `ByteGrid` instance. +/// Gets the height of the `ByteGrid` instance. /// /// # Arguments /// diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs index 2d4e7a1..23b9b7a 100644 --- a/crates/servicepoint_binding_c/src/command.rs +++ b/crates/servicepoint_binding_c/src/command.rs @@ -5,9 +5,19 @@ use servicepoint::{ }; pub use servicepoint::{Brightness, Command, Offset}; -/// Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process. +/// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. /// -/// Returns: pointer to command or NULL +/// Returns: pointer to new `Command` instance or NULL +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `packet` points to a valid instance of `Packet` +/// - `packet` is not used concurrently or after this call +/// - the result is checked for NULL +/// - the returned `Command` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_command_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_command_try_from_packet( packet: *mut Packet, @@ -19,7 +29,16 @@ pub unsafe extern "C" fn sp_command_try_from_packet( } } -/// Clones a `Command` instance +/// Clones a `Command` instance. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid instance of `Command` +/// - `this` is not written to concurrently +/// - the returned `Command` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_command_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_command_clone( original: *const Command, @@ -27,25 +46,53 @@ pub unsafe extern "C" fn sp_command_clone( Box::into_raw(Box::new((*original).clone())) } -/// Allocates a new `Command::Clear` instance +/// Allocates a new `Command::Clear` instance. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - the returned `Command` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_command_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_command_clear() -> *mut Command { Box::into_raw(Box::new(Command::Clear)) } -/// Allocates a new `Command::HardReset` instance +/// Allocates a new `Command::HardReset` instance. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - the returned `Command` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_command_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_command_hard_reset() -> *mut Command { Box::into_raw(Box::new(Command::HardReset)) } -/// Allocates a new `Command::FadeOut` instance +/// Allocates a new `Command::FadeOut` instance. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - the returned `Command` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_command_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command { Box::into_raw(Box::new(Command::FadeOut)) } -/// Allocates a new `Command::Brightness` instance +/// Allocates a new `Command::Brightness` instance. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - the returned `Command` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_command_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_command_brightness( brightness: Brightness, @@ -54,7 +101,16 @@ pub unsafe extern "C" fn sp_command_brightness( } /// Allocates a new `Command::CharBrightness` instance. -/// The passed `ByteGrid` gets deallocated in the process. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_char_brightness( x: usize, @@ -66,7 +122,17 @@ pub unsafe extern "C" fn sp_command_char_brightness( } /// Allocates a new `Command::BitmapLinear` instance. -/// The passed `BitVec` gets deallocated in the process. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear( offset: Offset, @@ -82,7 +148,17 @@ pub unsafe extern "C" fn sp_command_bitmap_linear( } /// Allocates a new `Command::BitmapLinearAnd` instance. -/// The passed `BitVec` gets deallocated in the process. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_and( offset: Offset, @@ -98,7 +174,17 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and( } /// Allocates a new `Command::BitmapLinearOr` instance. -/// The passed `BitVec` gets deallocated in the process. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_or( offset: Offset, @@ -114,7 +200,17 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or( } /// Allocates a new `Command::BitmapLinearXor` instance. -/// The passed `BitVec` gets deallocated in the process. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_xor( offset: Offset, @@ -130,7 +226,16 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor( } /// Allocates a new `Command::Cp437Data` instance. -/// The passed `ByteGrid` gets deallocated in the process. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_cp437_data( x: usize, @@ -142,15 +247,25 @@ pub unsafe extern "C" fn sp_command_cp437_data( } /// Allocates a new `Command::BitmapLinearWin` instance. -/// The passed `PixelGrid` gets deallocated in the process. +/// The passed `PixelGrid` gets consumed. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `pixel_grid` points to a valid instance of `PixelGrid` +/// - `pixel_grid` 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_win( x: usize, y: usize, - byte_grid: *mut PixelGrid, + pixel_grid: *mut PixelGrid, compression_code: CompressionCode, ) -> *mut Command { - let byte_grid = *Box::from_raw(byte_grid); + let byte_grid = *Box::from_raw(pixel_grid); Box::into_raw(Box::new(Command::BitmapLinearWin( Origin(x, y), byte_grid, @@ -158,8 +273,15 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( ))) } -/// Deallocates a `Command`. Note that connection_send does this implicitly, so you only need -/// to do this if you use the library for parsing commands. +/// Deallocates a `Command`. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `Command` +/// - `this` is not used concurrently or after this call +/// - `this` was not passed to another consuming function, e.g. to create a `Packet` #[no_mangle] pub unsafe extern "C" fn sp_command_dealloc(ptr: *mut Command) { _ = Box::from_raw(ptr); diff --git a/crates/servicepoint_binding_c/src/connection.rs b/crates/servicepoint_binding_c/src/connection.rs index b1ea334..355dd7c 100644 --- a/crates/servicepoint_binding_c/src/connection.rs +++ b/crates/servicepoint_binding_c/src/connection.rs @@ -4,12 +4,20 @@ use std::ptr::null_mut; pub use servicepoint::Connection; use servicepoint::Packet; -/// Creates a new instance of Connection. -/// The returned instance has to be deallocated with `connection_dealloc`. +/// Creates a new instance of `Connection`. /// -/// returns: NULL if connection fails or connected instance +/// returns: NULL if connection fails, or connected instance /// -/// Panics: bad string encoding +/// # Panics +/// +/// Bad string encoding +/// +/// # 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_connection_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_connection_open( host: *const c_char, @@ -23,17 +31,35 @@ pub unsafe extern "C" fn sp_connection_open( Box::into_raw(Box::new(connection)) } -/// Sends the command instance. The instance is consumed / destroyed and cannot be used after this call. +/// Sends a `Packet` to the display using the `Connection`. +/// The passed `Packet` gets consumed. +/// +/// returns: true in case of success +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `connection` points to a valid instance of `Connection` +/// - `packet` points to a valid instance of `Packet` +/// - `packet` is not used concurrently or after this call #[no_mangle] pub unsafe extern "C" fn sp_connection_send( connection: *const Connection, - command_ptr: *mut Packet, + packet: *mut Packet, ) -> bool { - let packet = Box::from_raw(command_ptr); + let packet = Box::from_raw(packet); (*connection).send(*packet).is_ok() } -/// Closes and deallocates a connection instance +/// Closes and deallocates a `Connection`. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `Connection` +/// - `this` is not used concurrently or after this call #[no_mangle] pub unsafe extern "C" fn sp_connection_dealloc(ptr: *mut Connection) { _ = Box::from_raw(ptr); diff --git a/crates/servicepoint_binding_c/src/lib.rs b/crates/servicepoint_binding_c/src/lib.rs index 61d7898..48aa258 100644 --- a/crates/servicepoint_binding_c/src/lib.rs +++ b/crates/servicepoint_binding_c/src/lib.rs @@ -5,26 +5,39 @@ pub use servicepoint::{ TILE_SIZE, TILE_WIDTH, }; +pub use crate::c_slice::CByteSlice; + /// C functions for interacting with `BitVec`s +/// +/// prefix `sp_bit_vec_` pub mod bit_vec; /// C functions for interacting with `ByteGrid`s +/// +/// prefix `sp_byte_grid_` pub mod byte_grid; -/// C functions for interacting with `BitVec`s -pub mod c_slice; - /// C functions for interacting with `Command`s +/// +/// prefix `sp_command_` pub mod command; /// C functions for interacting with `Connection`s +/// +/// prefix `sp_connection_` pub mod connection; /// C functions for interacting with `Packet`s +/// +/// prefix `sp_packet_` pub mod packet; /// C functions for interacting with `PixelGrid`s +/// +/// prefix `sp_pixel_grid_` 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; diff --git a/crates/servicepoint_binding_c/src/packet.rs b/crates/servicepoint_binding_c/src/packet.rs index 9e90f3f..48cdd9b 100644 --- a/crates/servicepoint_binding_c/src/packet.rs +++ b/crates/servicepoint_binding_c/src/packet.rs @@ -3,7 +3,17 @@ use std::ptr::null_mut; use servicepoint::Command; pub use servicepoint::Packet; -/// Turns a `Command` into a `Packet`. The command gets deallocated in the process. +/// Turns a `Command` into a `Packet`. +/// The `Command` gets consumed. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `command` points to a valid instance of `Command` +/// - `command` is not used concurrently or after this call +/// - the returned `Packet` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_packet_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_packet_from_command( command: *mut Command, @@ -16,6 +26,15 @@ pub unsafe extern "C" fn sp_packet_from_command( /// Tries to load a `Packet` from the passed array with the specified length. /// /// returns: NULL in case of an error, pointer to the allocated packet otherwise +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `data` points to a valid memory region of at least `length` bytes +/// - `data` is not written to concurrently +/// - the returned `Packet` instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_packet_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_packet_try_load( data: *const u8, diff --git a/crates/servicepoint_binding_c/src/pixel_grid.rs b/crates/servicepoint_binding_c/src/pixel_grid.rs index 87090d9..0f07f5d 100644 --- a/crates/servicepoint_binding_c/src/pixel_grid.rs +++ b/crates/servicepoint_binding_c/src/pixel_grid.rs @@ -2,8 +2,25 @@ use servicepoint::{DataRef, Grid, PixelGrid}; use crate::c_slice::CByteSlice; -/// Creates a new `PixelGrid` instance. -/// The returned instance has to be freed with `pixel_grid_dealloc`. +/// 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 +/// +/// # 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_pixel_grid_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_new( width: usize, @@ -13,7 +30,26 @@ pub unsafe extern "C" fn sp_pixel_grid_new( } /// Loads a `PixelGrid` with the specified dimensions from the provided data. -/// The returned instance has to be freed with `pixel_grid_dealloc`. +/// +/// # Arguments +/// +/// * `width`: size in pixels in x-direction +/// * `height`: size in pixels in y-direction +/// +/// returns: `PixelGrid` that contains a copy of the provided data +/// +/// # Panics +/// +/// - when the dimensions and data size do not match exactly. +/// - when the width is not dividable by 8 +/// +/// # 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_pixel_grid_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_load( width: usize, @@ -26,7 +62,15 @@ pub unsafe extern "C" fn sp_pixel_grid_load( } /// Clones a `PixelGrid`. -/// The returned instance has to be freed with `pixel_grid_dealloc`. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `PixelGrid` +/// - `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_pixel_grid_dealloc`. #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_clone( this: *const PixelGrid, @@ -36,13 +80,35 @@ pub unsafe extern "C" fn sp_pixel_grid_clone( /// Deallocates a `PixelGrid`. /// -/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command. +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `PixelGrid` +/// - `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_pixel_grid_dealloc(this: *mut PixelGrid) { _ = Box::from_raw(this); } -/// Get the current value at the specified position +/// Gets the current value at the specified position in the `PixelGrid`. +/// +/// # Arguments +/// +/// * `this`: instance to read from +/// * `x` and `y`: position of the cell to read +/// +/// # Panics +/// +/// When accessing `x` or `y` out of bounds. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `PixelGrid` +/// - `this` is not written to concurrently #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_get( this: *const PixelGrid, @@ -52,7 +118,26 @@ pub unsafe extern "C" fn sp_pixel_grid_get( (*this).get(x, y) } -/// Sets the current value at the specified position +/// Sets the value of the specified position in the `PixelGrid`. +/// +/// # Arguments +/// +/// * `this`: instance to write to +/// * `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. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `PixelGrid` +/// - `this` is not written to or read from concurrently #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_set( this: *mut PixelGrid, @@ -63,19 +148,51 @@ pub unsafe extern "C" fn sp_pixel_grid_set( (*this).set(x, y, value); } -/// Fills the whole `PixelGrid` with the specified value +/// Sets the state of all pixels in the `PixelGrid`. +/// +/// # Arguments +/// +/// * `this`: instance to write to +/// * `value`: the value to set all pixels to +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `PixelGrid` +/// - `this` is not written to or read from concurrently #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_fill(this: *mut PixelGrid, value: bool) { (*this).fill(value); } /// Gets the width in pixels of the `PixelGrid` instance. +/// +/// # Arguments +/// +/// * `this`: instance to read from +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `PixelGrid` #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_width(this: *const PixelGrid) -> usize { (*this).width() } /// Gets the height in pixels of the `PixelGrid` instance. +/// +/// # Arguments +/// +/// * `this`: instance to read from +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `this` points to a valid `PixelGrid` #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_height(this: *const PixelGrid) -> usize { (*this).height() @@ -85,11 +202,11 @@ pub unsafe extern "C" fn sp_pixel_grid_height(this: *const PixelGrid) -> usize { /// /// ## Safety /// -/// The caller has to make sure to never access the returned memory after the `PixelGrid` -/// instance has been consumed or manually deallocated. +/// The caller has to make sure that: /// -/// Reading and writing concurrently to either the original instance or the returned data will -/// result in undefined behavior. +/// - `this` points to a valid `PixelGrid` +/// - the returned memory range is never accessed after the passed `PixelGrid` has been freed +/// - the returned memory range is never accessed concurrently, either via the `PixelGrid` or directly #[no_mangle] pub unsafe extern "C" fn sp_pixel_grid_unsafe_data_ref( this: *mut PixelGrid,