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

@ -0,0 +1,94 @@
pub use servicepoint::BitVec;
use servicepoint::DataRef;
use crate::c_slice::CByteSlice;
/// Creates a new `BitVec` instance.
/// The returned instance has to be freed with `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)))
}
/// 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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_bit_vec_len(this: *const BitVec) -> usize {
(*this).len()
}
/// Returns true if length is 0.
#[no_mangle]
pub unsafe extern "C" fn sp_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 sp_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(),
}
}

View file

@ -0,0 +1,103 @@
pub use servicepoint::ByteGrid;
use servicepoint::{DataRef, Grid};
use crate::c_slice::CByteSlice;
/// Creates a new `ByteGrid` instance.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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(),
}
}

View file

@ -0,0 +1,10 @@
#[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

@ -0,0 +1,166 @@
use std::ptr::null_mut;
use servicepoint::{
BitVec, ByteGrid, CompressionCode, Origin, Packet, PixelGrid,
};
pub use servicepoint::{Brightness, Command, Offset};
/// 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 sp_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 sp_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 sp_command_clear() -> *mut Command {
Box::into_raw(Box::new(Command::Clear))
}
/// Allocates a new `Command::HardReset` instance
#[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
#[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
#[no_mangle]
pub unsafe extern "C" fn sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_command_dealloc(ptr: *mut Command) {
_ = Box::from_raw(ptr);
}

View file

@ -0,0 +1,40 @@
use std::ffi::{c_char, CStr};
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`.
///
/// returns: NULL if connection fails or connected instance
///
/// Panics: bad string encoding
#[no_mangle]
pub unsafe extern "C" fn sp_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 sp_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 sp_connection_dealloc(ptr: *mut Connection) {
_ = Box::from_raw(ptr);
}

View file

@ -0,0 +1,14 @@
pub use servicepoint::{
CompressionCode, PIXEL_COUNT, PIXEL_HEIGHT, PIXEL_WIDTH, TILE_HEIGHT,
TILE_SIZE, TILE_WIDTH,
};
pub mod bit_vec;
pub mod byte_grid;
pub mod c_slice;
pub mod command;
pub mod connection;
pub mod packet;
pub mod pixel_grid;
pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32;

View file

@ -0,0 +1,37 @@
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.
#[no_mangle]
pub unsafe extern "C" fn sp_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 sp_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 sp_packet_dealloc(this: *mut Packet) {
_ = Box::from_raw(this)
}

View file

@ -0,0 +1,102 @@
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`.
#[no_mangle]
pub unsafe extern "C" fn sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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 sp_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(),
}
}