move c_api into binding_c crate, update C# binding

This commit is contained in:
Vinzenz Schroeter 2024-05-26 13:15:11 +02:00
parent edcad6fd03
commit f759395393
40 changed files with 895 additions and 916 deletions

View file

@ -10,7 +10,7 @@ repository = "https://github.com/cccb/servicepoint"
readme = "../../README.md"
[lib]
crate-type = ["staticlib", "rlib", "cdylib"]
crate-type = ["rlib"]
[dependencies]
log = "0.4"
@ -20,10 +20,9 @@ zstd = { version = "0.13", optional = true }
rust-lzma = { version = "0.6.0", optional = true }
[features]
default = ["compression_lzma", "c_api"]
default = ["compression_lzma"]
compression_zlib = ["dep:flate2"]
compression_bzip2 = ["dep:bzip2"]
compression_lzma = ["dep:rust-lzma"]
compression_zstd = ["dep:zstd"]
all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"]
c_api = []

View file

@ -135,103 +135,6 @@ impl From<&[u8]> for BitVec {
}
}
#[cfg(feature = "c_api")]
pub mod c_api {
use crate::{BitVec, CByteSlice, DataRef};
/// Creates a new `BitVec` instance.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_new(size: usize) -> *mut BitVec {
Box::into_raw(Box::new(BitVec::new(size)))
}
/// Loads a `BitVec` from the provided data.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_load(
data: *const u8,
data_length: usize,
) -> *mut BitVec {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(BitVec::from(data)))
}
/// Clones a `BitVec`.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_clone(
this: *const BitVec,
) -> *mut BitVec {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `BitVec`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_dealloc(this: *mut BitVec) {
_ = Box::from_raw(this);
}
/// Gets the value of a bit from the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_get(
this: *const BitVec,
index: usize,
) -> bool {
(*this).get(index)
}
/// Sets the value of a bit in the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_set(
this: *mut BitVec,
index: usize,
value: bool,
) -> bool {
(*this).set(index, value)
}
/// Sets the value of all bits in the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_fill(this: *mut BitVec, value: bool) {
(*this).fill(value)
}
/// Gets the length of the `BitVec` in bits.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_len(this: *const BitVec) -> usize {
(*this).len()
}
/// Returns true if length is 0.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_is_empty(this: *const BitVec) -> bool {
(*this).is_empty()
}
/// Gets an unsafe reference to the data of the `BitVec` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `BitVec`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_unsafe_data_ref(
this: *mut BitVec,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}
}
#[cfg(test)]
mod tests {
use crate::{BitVec, DataRef};

View file

@ -108,119 +108,6 @@ impl From<ByteGrid> for Vec<u8> {
}
}
#[cfg(feature = "c_api")]
pub mod c_api {
use crate::data_ref::DataRef;
use crate::grid::Grid;
use crate::{ByteGrid, CByteSlice};
/// Creates a new `ByteGrid` instance.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_new(
width: usize,
height: usize,
) -> *mut ByteGrid {
Box::into_raw(Box::new(ByteGrid::new(width, height)))
}
/// Loads a `ByteGrid` with the specified dimensions from the provided data.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut ByteGrid {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(ByteGrid::load(width, height, data)))
}
/// Clones a `ByteGrid`.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_clone(
this: *const ByteGrid,
) -> *mut ByteGrid {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `ByteGrid`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_dealloc(this: *mut ByteGrid) {
_ = Box::from_raw(this);
}
/// Get the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_get(
this: *const ByteGrid,
x: usize,
y: usize,
) -> u8 {
(*this).get(x, y)
}
/// Sets the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_set(
this: *mut ByteGrid,
x: usize,
y: usize,
value: u8,
) {
(*this).set(x, y, value);
}
/// Fills the whole `ByteGrid` with the specified value
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_fill(
this: *mut ByteGrid,
value: u8,
) {
(*this).fill(value);
}
/// Gets the width in pixels of the `ByteGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_width(
this: *const ByteGrid,
) -> usize {
(*this).width
}
/// Gets the height in pixels of the `ByteGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_height(
this: *const ByteGrid,
) -> usize {
(*this).height
}
/// Gets an unsafe reference to the data of the `ByteGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `ByteGrid`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_unsafe_data_ref(
this: *mut ByteGrid,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}
}
#[cfg(test)]
mod tests {
use crate::{ByteGrid, DataRef, Grid};

View file

@ -1,11 +0,0 @@
#[cfg(feature = "c_api")]
#[repr(C)]
/// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
///
/// Usage of this type is inherently unsafe.
pub struct CByteSlice {
/// The start address of the memory
pub start: *mut u8,
/// The amount of memory in bytes
pub length: usize,
}

View file

@ -391,179 +391,6 @@ impl Command {
}
}
#[cfg(feature = "c_api")]
pub mod c_api {
use std::ptr::null_mut;
use crate::{
BitVec, Brightness, ByteGrid, Command, CompressionCode, Offset, Origin,
Packet, PixelGrid,
};
/// Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process.
///
/// Returns: pointer to command or NULL
#[no_mangle]
pub unsafe extern "C" fn sp2_command_try_from_packet(
packet: *mut Packet,
) -> *mut Command {
let packet = *Box::from_raw(packet);
match Command::try_from(packet) {
Err(_) => null_mut(),
Ok(command) => Box::into_raw(Box::new(command)),
}
}
/// Clones a `Command` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_clone(
original: *const Command,
) -> *mut Command {
Box::into_raw(Box::new((*original).clone()))
}
/// Allocates a new `Command::Clear` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_clear() -> *mut Command {
Box::into_raw(Box::new(Command::Clear))
}
/// Allocates a new `Command::HardReset` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_hard_reset() -> *mut Command {
Box::into_raw(Box::new(Command::HardReset))
}
/// Allocates a new `Command::FadeOut` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_fade_out() -> *mut Command {
Box::into_raw(Box::new(Command::FadeOut))
}
/// Allocates a new `Command::Brightness` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_brightness(
brightness: Brightness,
) -> *mut Command {
Box::into_raw(Box::new(Command::Brightness(brightness)))
}
/// Allocates a new `Command::CharBrightness` instance.
/// The passed `ByteGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_char_brightness(
x: usize,
y: usize,
byte_grid: *mut ByteGrid,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::CharBrightness(
Origin(x, y),
byte_grid,
)))
}
/// Allocates a new `Command::BitmapLinear` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinear(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearAnd` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_and(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearAnd(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearOr` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_or(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearOr(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearXor` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_xor(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearXor(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::Cp437Data` instance.
/// The passed `ByteGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_cp437_data(
x: usize,
y: usize,
byte_grid: *mut ByteGrid,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::Cp437Data(Origin(x, y), byte_grid)))
}
/// Allocates a new `Command::BitmapLinearWin` instance.
/// The passed `PixelGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_win(
x: usize,
y: usize,
byte_grid: *mut PixelGrid,
compression_code: CompressionCode,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::BitmapLinearWin(
Origin(x, y),
byte_grid,
compression_code,
)))
}
/// 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.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_dealloc(ptr: *mut Command) {
_ = Box::from_raw(ptr);
}
}
#[cfg(test)]
mod tests {
use crate::command::TryFromPacketError;

View file

@ -10,8 +10,6 @@ pub struct Connection {
socket: UdpSocket,
}
impl Connection {
/// Open a new UDP socket and connect to the provided host.
///
@ -64,7 +62,10 @@ impl Connection {
/// connection.send(Command::BitmapLinearWin(servicepoint::Origin(0, 0), pixels, CompressionCode::Uncompressed))
/// .expect("send failed");
/// ```
pub fn send(&self, packet: impl Into<Packet>) -> Result<(), std::io::Error> {
pub fn send(
&self,
packet: impl Into<Packet>,
) -> Result<(), std::io::Error> {
let packet = packet.into();
debug!("sending {packet:?}");
let data: Vec<u8> = packet.into();
@ -72,46 +73,3 @@ impl Connection {
Ok(())
}
}
#[cfg(feature = "c_api")]
pub mod c_api {
use std::ffi::{c_char, CStr};
use std::ptr::null_mut;
use crate::{Connection, Packet};
/// Creates a new instance of Connection.
/// The returned instance has to be deallocated with `connection_dealloc`.
///
/// returns: NULL if connection fails or connected instance
///
/// Panics: bad string encoding
#[no_mangle]
pub unsafe extern "C" fn sp2_connection_open(
host: *const c_char,
) -> *mut Connection {
let host = CStr::from_ptr(host).to_str().expect("Bad encoding");
let connection = match Connection::open(host) {
Err(_) => return null_mut(),
Ok(value) => value,
};
Box::into_raw(Box::new(connection))
}
/// Sends the command instance. The instance is consumed / destroyed and cannot be used after this call.
#[no_mangle]
pub unsafe extern "C" fn sp2_connection_send(
connection: *const Connection,
command_ptr: *mut Packet,
) -> bool {
let packet = Box::from_raw(command_ptr);
(*connection).send(*packet).is_ok()
}
/// Closes and deallocates a connection instance
#[no_mangle]
pub unsafe extern "C" fn sp2_connection_dealloc(ptr: *mut Connection) {
_ = Box::from_raw(ptr);
}
}

View file

@ -2,8 +2,6 @@ use std::time::Duration;
pub use crate::bit_vec::BitVec;
pub use crate::byte_grid::ByteGrid;
#[cfg(feature = "c_api")]
pub use crate::c_slice::CByteSlice;
pub use crate::command::{Brightness, Command, Offset, Origin};
pub use crate::compression_code::CompressionCode;
pub use crate::connection::Connection;
@ -14,7 +12,6 @@ pub use crate::pixel_grid::PixelGrid;
mod bit_vec;
mod byte_grid;
mod c_slice;
mod command;
mod command_code;
mod compression;

View file

@ -58,46 +58,6 @@ impl TryFrom<&[u8]> for Packet {
}
}
#[cfg(feature = "c_api")]
mod c_api {
use std::ptr::null_mut;
use crate::{Command, Packet};
/// Turns a `Command` into a `Packet`. The command gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_packet_from_command(
command: *mut Command,
) -> *mut Packet {
let command = *Box::from_raw(command);
let packet = command.into();
Box::into_raw(Box::new(packet))
}
/// 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
#[no_mangle]
pub unsafe extern "C" fn sp2_packet_try_load(
data: *const u8,
length: usize,
) -> *mut Packet {
let data = std::slice::from_raw_parts(data, length);
match Packet::try_from(data) {
Err(_) => null_mut(),
Ok(packet) => Box::into_raw(Box::new(packet)),
}
}
/// Deallocates a `Packet`.
///
/// Note: do not call this if the instance has been consumed in another way, e.g. by sending it.
#[no_mangle]
pub unsafe extern "C" fn sp2_packet_dealloc(this: *mut Packet) {
_ = Box::from_raw(this)
}
}
#[cfg(test)]
mod tests {
use crate::{Header, Packet};

View file

@ -127,118 +127,6 @@ impl From<PixelGrid> for Vec<u8> {
}
}
#[cfg(feature = "c_api")]
pub mod c_api {
use crate::c_slice::CByteSlice;
use crate::{DataRef, Grid, PixelGrid};
/// Creates a new `PixelGrid` instance.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_new(
width: usize,
height: usize,
) -> *mut PixelGrid {
Box::into_raw(Box::new(PixelGrid::new(width, height)))
}
/// Loads a `PixelGrid` with the specified dimensions from the provided data.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut PixelGrid {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(PixelGrid::load(width, height, data)))
}
/// Clones a `PixelGrid`.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_clone(
this: *const PixelGrid,
) -> *mut PixelGrid {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `PixelGrid`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_dealloc(this: *mut PixelGrid) {
_ = Box::from_raw(this);
}
/// Get the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_get(
this: *const PixelGrid,
x: usize,
y: usize,
) -> bool {
(*this).get(x, y)
}
/// Sets the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_set(
this: *mut PixelGrid,
x: usize,
y: usize,
value: bool,
) {
(*this).set(x, y, value);
}
/// Fills the whole `PixelGrid` with the specified value
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_fill(
this: *mut PixelGrid,
value: bool,
) {
(*this).fill(value);
}
/// Gets the width in pixels of the `PixelGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_width(
this: *const PixelGrid,
) -> usize {
(*this).width
}
/// Gets the height in pixels of the `PixelGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_height(
this: *const PixelGrid,
) -> usize {
(*this).height
}
/// Gets an unsafe reference to the data of the `PixelGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `PixelGrid`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_unsafe_data_ref(
this: *mut PixelGrid,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}
}
#[cfg(test)]
mod tests {
use crate::{DataRef, Grid, PixelGrid};