Compare commits

..

17 commits

Author SHA1 Message Date
Vinzenz Schroeter 613f21310e fix rebase
Some checks failed
Rust / build (pull_request) Failing after 1m23s
2025-04-12 16:33:39 +02:00
Vinzenz Schroeter 7c8ede61b0 move generic safety information into README 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 0c3bc0b004 wip fix documentation 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter cf2c72de7c use brightness type from base crate 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 30bec50f7c use NotNull for parameters 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter b443adc3ab add /*notnull*/ comments in header 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 24ff341f9d re-export constants from base lib 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 35cb42df48 wip remove newtypes 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 5120e220ec functions for manipulating packets 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter d1ecd21114 wip remove newtypes 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 2ab80d395e cargo fmt 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 69bed7c665 wip 80k example 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 57d9e90b0f wip update to next servicepoint version 2025-04-12 16:25:20 +02:00
Vinzenz Schroeter 3c8eafecfb fix example path 2025-04-12 16:25:17 +02:00
Vinzenz Schroeter 65a26a9110 remove redundant infos
All checks were successful
Rust / build (push) Successful in 3m8s
2025-02-16 18:27:50 +01:00
Vinzenz Schroeter ed8e362574 extend installation instructions, fix perm
All checks were successful
Rust / build (push) Successful in 3m8s
2025-02-16 18:17:08 +01:00
Vinzenz Schroeter 77a8a242bf simplify example
Some checks failed
Rust / build (push) Failing after 1m21s
2025-02-16 18:06:19 +01:00
23 changed files with 1453 additions and 3228 deletions

View file

@ -25,10 +25,20 @@ jobs:
- name: Install rust toolchain
run: sudo apt-get install -qy cargo rust-clippy
- name: install lzma
run: sudo apt-get update && sudo apt-get install -y liblzma-dev
run: sudo apt-get install -qy liblzma-dev
- name: install gcc
run: sudo apt-get install -qy gcc make
- name: Run Clippy
run: cargo clippy
- name: generate bindings
run: ./generate-binding.sh
- name: check that generated files did not change
run: output=$(git status --porcelain) && [ -z "$output" ]
- name: build
run: cargo build
- name: build example
run: cd example && make

View file

@ -1,31 +0,0 @@
# Contributing
Contributions are accepted in any form (issues, documentation, feature requests, code, review, ...).
All creatures welcome.
If you have access, please contribute on the [CCCB Forgejo](https://git.berlin.ccc.de/servicepoint/servicepoint).
Contributions on GitHub will be copied over and merged there.
## Pull requests
Feel free to create a PR, even if your change is not done yet.
Mark your PR as a draft as long as you do not want it to be merged.
The main branch is supposed to be a working version, including language bindings,
which means sometimes your PR may be merged into a temporary development branch.
Unit tests and documentation are required for the core library.
## Language bindings
Pull requests for your preferred language will be accepted.
If there is no code generator, it should call the C ABI methods provided by `servicepoint_binding_c`.
It should be able to send most of the basic commands in a way the simulator accepts, receiving is
not required for the merge.
It is okay for the feature set of a language binding to lag behind the one of the rust crate.
This also means you do not have to expose a feature to all the language bindings when adding something to the core.
If your change may break other language bindings, please note that in your PR description so someone can check them.

View file

@ -3,12 +3,12 @@
[![crates.io](https://img.shields.io/crates/v/servicepoint_binding_c.svg)](https://crates.io/crates/servicepoint)
[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint_binding_c)](https://crates.io/crates/servicepoint)
[![docs.rs](https://img.shields.io/docsrs/servicepoint_binding_c)](https://docs.rs/servicepoint/latest/servicepoint/)
[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint_binding_c)](../LICENSE)
[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint_binding_c)](./LICENSE)
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall.
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall.
It is called "Service Point Display" or "Airport Display".
This crate contains C bindings for the `servicepoint` library, enabling users to parse, encode and send packets to this display via UDP.
This crate contains C bindings for the [servicepoint](https://git.berlin.ccc.de/servicepoint/servicepoint) library, enabling users to parse, encode and send packets to this display via UDP.
## Examples
@ -43,21 +43,61 @@ Please specify the full version including patch in your Cargo.toml until 1.0 is
## Installation
Copy the header to your project and compile against.
1. Add this repo as a submodule:
```bash
git submodule add https://git.berlin.ccc.de/servicepoint/servicepoint-binding-c.git
git commit -m "add servicepoint-binding-c submodule"
```
2. Add a build step for the servicepoint library. If you use make, this could look something like this:
```
dependencies: FORCE
cargo build --manifest-path=servicepoint-binding-c/Cargo.toml --release
FORCE: ;
```
3. Link against the library. If you are on linux and linking statically:
```
${CC} main.c \
-I servicepoint-binding-c/include \
-L servicepoint-binding-c/target/release \
-Wl,-Bstatic -lservicepoint_binding_c \
-Wl,-Bdynamic -llzma \
-o out/example
```
You have the choice of linking statically (recommended) or dynamically.
- The C example shows how to link statically against the `staticlib` variant.
- When linked dynamically, you have to provide the `cdylib` at runtime in the _same_ version, as there are no API/ABI guarantees yet.
- When linked dynamically, you have to provide the `cdylib` at runtime in the _same_ version, as there are no API/ABI
guarantees yet.
## Notes on differences to rust library
- function names are: `sp_` \<struct_name\> \<rust name\>.
- Instances get consumed in the same way they do when writing rust code. Do not use an instance after an (implicit!) free.
- Instances get consumed in the same way they do when writing rust code. Do not use an instance after an (implicit!)
free.
- Option<T> or Result<T, E> 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.
- 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/)
- documentation is included in the header and
available [online](https://docs.rs/servicepoint_binding_c/latest/servicepoint_binding_c/)
## Safety
Functions expect that C code honors NonNull annotations.
Any created instances have to be freed in some way.
Pointers to those instances cannot be used anymore after that.
Instances cannot be shared between threads and need to be locked in the using code.
Enum values have to be used as-is. Do not pass values that are not part of the enum.
UTF-8 or UTF-32 encoding has to be used properly.
Brightness values provided as u8 parameters must be in range.
## Everything else
Look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for further information.
Look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for
further information.

View file

@ -1,41 +0,0 @@
# About the display
- Resolution: 352x160=56,320 pixels
- Pixels are grouped into 44x20=880 tiles (8x8=64 pixels each)
- Smallest addressable unit: row of pixels inside of a tile (8 pixels = 1 byte)
- The brightness can only be set per tile
- Screen content can be changed using a simple UDP protocol
- Between each row of tiles, there is a gap of around 4 pixels size. This gap changes the aspect ratio of the display.
### Binary format
A UDP package sent to the display has a header size of 10 bytes.
Each header value has a size of two bytes (unsigned 16 bit integer).
Depending on the command, there can be a payload following the header.
To change screen contents, these commands are the most relevant:
1. Clear screen
- command: `0x0002`
- (rest does not matter)
2. Send CP437 data: render specified text into rectangular region
- command: `0x0003`
- top left tile x
- top left tile y
- width in tiles
- height in tiles
- payload: (width in tiles * height in tiles) bytes
- 1 byte = 1 character
- each character is rendered into one tile (mono-spaced)
- characters are encoded using code page 437
3. Send bitmap window: set pixel states for a rectangular region
- command: `0x0013`
- top left tile x
- top left _pixel_ y
- width in tiles
- height in _pixels_
- payload: (width in tiles * height in pixels) bytes
- network byte order
- 1 bit = 1 pixel
There are other commands implemented as well, e.g. for changing the brightness.

View file

@ -35,7 +35,8 @@ include = []
exclude = []
[export.rename]
"TypedCommand" = "Command"
"SpBitVec" = "BitVec"
"SpByteSlice" = "ByteSlice"
[enum]
rename_variants = "QualifiedScreamingSnakeCase"

View file

@ -2,7 +2,7 @@ CC := gcc
CARGO := rustup run nightly cargo
THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
REPO_ROOT := $(THIS_DIR)/../../
REPO_ROOT := $(THIS_DIR)/..
RUST_TARGET_DIR := $(REPO_ROOT)/target/x86_64-unknown-linux-musl/size-optimized
RUSTFLAGS := -Zlocation-detail=none \
@ -41,29 +41,29 @@ CCFLAGS := -static -Os \
-fno-exceptions
#-Wl,--icf=all \
export SERVICEPOINT_HEADER_OUT := $(THIS_DIR)/include
export SERVICEPOINT_HEADER_OUT := $(REPO_ROOT)/include
build: out/lang_c
build: out/example
clean:
rm -r out || true
rm include/servicepoint.h || true
cargo clean
run: out/lang_c
out/lang_c
run: out/example
out/example
PHONY: build clean dependencies run
out/lang_c_unstripped: dependencies src/main.c
out/example_unstripped: dependencies main.c
mkdir -p out || true
${CC} src/main.c \
${CC} main.c \
-I $(SERVICEPOINT_HEADER_OUT) \
-L $(RUST_TARGET_DIR)\
$(CCFLAGS) \
-o out/lang_c_unstripped
out/lang_c: out/lang_c_unstripped
strip -s -R .comment -R .gnu.version --strip-unneeded out/lang_c_unstripped -o out/lang_c
-o out/example_unstripped
out/example: out/example_unstripped
strip -s -R .comment -R .gnu.version --strip-unneeded out/example_unstripped -o out/example
#strip -S --strip-unneeded --remove-section=.note.gnu.gold-version --remove-section=.comment --remove-section=.note --remove-section=.note.gnu.build-id --remove-section=.note.ABI-tag
dependencies: FORCE

View file

@ -12,7 +12,7 @@ int main(void) {
sp_bitmap_fill(pixels, true);
Command *command = sp_command_bitmap_linear_win(0, 0, pixels, COMPRESSION_CODE_UNCOMPRESSED);
TypedCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, COMPRESSION_CODE_UNCOMPRESSED);
if (command == NULL)
return 1;

View file

@ -1,14 +0,0 @@
[package]
name = "lang_c"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
test = false
[build-dependencies]
cc = "1.2"
[dependencies]
servicepoint_binding_c = { path = "../.." }

View file

@ -1,17 +0,0 @@
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}");
let sp_include =
std::env::var_os(SP_INCLUDE).unwrap().into_string().unwrap();
// this builds a lib, this is only to check that the example compiles
let mut cc = cc::Build::new();
cc.file("src/main.c");
cc.include(&sp_include);
cc.opt_level(2);
cc.compile("lang_c");
}

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@

6
generate-binding.sh Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
SERVICEPOINT_HEADER_OUT="$SCRIPT_PATH/include" cargo build --release

1174
include/servicepoint.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,16 @@
//! C functions for interacting with [SPBitmap]s
//!
//! prefix `sp_bitmap_`
//!
//! A grid of pixels.
//!
//! # Examples
//!
//! ```C
//! Cp437Grid grid = sp_bitmap_new(8, 3);
//! sp_bitmap_fill(grid, true);
//! sp_bitmap_set(grid, 0, 0, false);
//! sp_bitmap_free(grid);
//! ```
use servicepoint::{Bitmap, DataRef, Grid};
use std::ptr::NonNull;
use crate::byte_slice::SPByteSlice;
use crate::byte_slice::ByteSlice;
/// Creates a new [SPBitmap] with the specified dimensions.
/// Creates a new [Bitmap] with the specified dimensions.
///
/// # Arguments
///
/// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction
///
/// returns: [SPBitmap] initialized to all pixels off, or NULL in case of an error.
/// returns: [Bitmap] initialized to all pixels off, or NULL in case of an error.
///
/// # Errors
///
@ -33,12 +18,14 @@ use crate::byte_slice::SPByteSlice;
///
/// - when the width is not dividable by 8
///
/// # Safety
/// # Examples
///
/// 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_bitmap_free`.
/// ```C
/// Cp437Grid grid = sp_bitmap_new(8, 3);
/// sp_bitmap_fill(grid, true);
/// sp_bitmap_set(grid, 0, 0, false);
/// sp_bitmap_free(grid);
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_new(
width: usize,
@ -51,54 +38,28 @@ pub unsafe extern "C" fn sp_bitmap_new(
}
}
/// Creates a new [SPBitmap] with a size matching the screen.
/// Creates a new [Bitmap] with a size matching the screen.
///
/// returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
///
/// # 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_bitmap_free].
/// returns: [Bitmap] initialized to all pixels off.
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_new_screen_sized() -> NonNull<Bitmap> {
let result = Box::new(Bitmap::max_sized());
NonNull::from(Box::leak(result))
}
/// Loads a [SPBitmap] with the specified dimensions from the provided data.
/// Loads a [Bitmap] with the specified dimensions from the provided data.
///
/// # Arguments
///
/// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction
///
/// returns: [SPBitmap] that contains a copy of the provided data, or NULL in case of an error.
///
/// # Errors
///
/// In the following cases, this function will return NULL:
///
/// - when the dimensions and data size do not match exactly.
/// - when the width is not dividable by 8
///
/// # Panics
///
/// - when `data` is NULL
///
/// # 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_bitmap_free`.
/// returns: [Bitmap] that contains a copy of the provided data, or NULL in case of an error.
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_load(
width: usize,
height: usize,
data: SPByteSlice,
data: ByteSlice,
) -> *mut Bitmap {
let data = unsafe { data.as_slice() };
if let Ok(bitmap) = Bitmap::load(width, height, data) {
@ -108,22 +69,7 @@ pub unsafe extern "C" fn sp_bitmap_load(
}
}
/// Clones a [SPBitmap].
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bitmap_free`.
/// Clones a [Bitmap].
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_clone(
bitmap: NonNull<Bitmap>,
@ -132,25 +78,13 @@ pub unsafe extern "C" fn sp_bitmap_clone(
NonNull::from(Box::leak(result))
}
/// Deallocates a [SPBitmap].
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
///
/// [SPCommand]: [crate::SPCommand]
/// Deallocates a [Bitmap].
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_free(bitmap: NonNull<Bitmap>) {
_ = unsafe { Box::from_raw(bitmap.as_ptr()) };
}
/// Gets the current value at the specified position in the [SPBitmap].
/// Gets the current value at the specified position in the [Bitmap].
///
/// # Arguments
///
@ -159,15 +93,7 @@ pub unsafe extern "C" fn sp_bitmap_free(bitmap: NonNull<Bitmap>) {
///
/// # Panics
///
/// - when `bitmap` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_get(
bitmap: NonNull<Bitmap>,
@ -177,7 +103,7 @@ pub unsafe extern "C" fn sp_bitmap_get(
unsafe { bitmap.as_ref().get(x, y) }
}
/// Sets the value of the specified position in the [SPBitmap].
/// Sets the value of the specified position in the [Bitmap].
///
/// # Arguments
///
@ -185,19 +111,9 @@ pub unsafe extern "C" fn sp_bitmap_get(
/// - `x` and `y`: position of the cell
/// - `value`: the value to write to the cell
///
/// returns: old value of the cell
///
/// # Panics
///
/// - when `bitmap` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_set(
bitmap: NonNull<Bitmap>,
@ -208,29 +124,18 @@ pub unsafe extern "C" fn sp_bitmap_set(
unsafe { (*bitmap.as_ptr()).set(x, y, value) };
}
/// Sets the state of all pixels in the [SPBitmap].
/// Sets the state of all pixels in the [Bitmap].
///
/// # Arguments
///
/// - `bitmap`: instance to write to
/// - `value`: the value to set all pixels to
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_fill(bitmap: NonNull<Bitmap>, value: bool) {
unsafe { (*bitmap.as_ptr()).fill(value) };
}
/// Gets the width in pixels of the [SPBitmap] instance.
/// Gets the width in pixels of the [Bitmap] instance.
///
/// # Arguments
///
@ -244,48 +149,28 @@ pub unsafe extern "C" fn sp_bitmap_fill(bitmap: NonNull<Bitmap>, value: bool) {
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` points to a valid [Bitmap]
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_width(bitmap: NonNull<Bitmap>) -> usize {
unsafe { bitmap.as_ref().width() }
}
/// Gets the height in pixels of the [SPBitmap] instance.
/// Gets the height in pixels of the [Bitmap] instance.
///
/// # Arguments
///
/// - `bitmap`: instance to read from
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_height(bitmap: NonNull<Bitmap>) -> usize {
unsafe { bitmap.as_ref().height() }
}
/// Gets an unsafe reference to the data of the [SPBitmap] instance.
/// Gets an unsafe reference to the data of the [Bitmap] instance.
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - the returned memory range is never accessed after the passed [SPBitmap] has been freed
/// - the returned memory range is never accessed concurrently, either via the [SPBitmap] or directly
/// The returned memory is valid for the lifetime of the bitmap.
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_unsafe_data_ref(
mut bitmap: NonNull<Bitmap>,
) -> SPByteSlice {
unsafe { SPByteSlice::from_slice(bitmap.as_mut().data_ref_mut()) }
) -> ByteSlice {
unsafe { ByteSlice::from_slice(bitmap.as_mut().data_ref_mut()) }
}

View file

@ -1,9 +1,6 @@
//! C functions for interacting with [SPBitVec]s
//!
//! prefix `sp_bitvec_`
use crate::SPByteSlice;
use crate::ByteSlice;
use std::ptr::NonNull;
use servicepoint::BitVecU8Msb0;
/// A vector of bits
///
@ -13,7 +10,7 @@ use std::ptr::NonNull;
/// sp_bitvec_set(vec, 5, true);
/// sp_bitvec_free(vec);
/// ```
pub struct SPBitVec(pub(crate) servicepoint::BitVecU8Msb0);
pub struct SPBitVec(pub(crate) BitVecU8Msb0);
impl Clone for SPBitVec {
fn clone(&self) -> Self {
@ -27,67 +24,31 @@ impl Clone for SPBitVec {
///
/// - `size`: size in bits.
///
/// returns: [SPBitVec] with all bits set to false. Will never return NULL.
/// returns: [SPBitVec] 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_bitvec_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> {
let result =
Box::new(SPBitVec(servicepoint::BitVecU8Msb0::repeat(false, size)));
Box::new(SPBitVec(BitVecU8Msb0::repeat(false, size)));
NonNull::from(Box::leak(result))
}
/// Interpret the data as a series of bits and load then into a new [SPBitVec] instance.
///
/// returns: [SPBitVec] instance containing data. Will never return NULL.
///
/// # Panics
///
/// - when `data` is NULL
///
/// # 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_bitvec_free`.
/// returns: [SPBitVec] instance containing data.
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_load(
data: SPByteSlice,
data: ByteSlice,
) -> NonNull<SPBitVec> {
let data = unsafe { data.as_slice() };
let result =
Box::new(SPBitVec(servicepoint::BitVecU8Msb0::from_slice(data)));
let result = Box::new(SPBitVec(BitVecU8Msb0::from_slice(data)));
NonNull::from(Box::leak(result))
}
/// Clones a [SPBitVec].
///
/// returns: new [SPBitVec] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bitvec_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_clone(
bit_vec: NonNull<SPBitVec>,
@ -97,20 +58,6 @@ pub unsafe extern "C" fn sp_bitvec_clone(
}
/// Deallocates a [SPBitVec].
///
/// # Panics
///
/// - when `but_vec` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call
/// - `bit_vec` was not passed to another consuming function, e.g. to create a [SPCommand]
///
/// [SPCommand]: [crate::SPCommand]
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_free(bit_vec: NonNull<SPBitVec>) {
_ = unsafe { Box::from_raw(bit_vec.as_ptr()) };
@ -127,15 +74,7 @@ pub unsafe extern "C" fn sp_bitvec_free(bit_vec: NonNull<SPBitVec>) {
///
/// # Panics
///
/// - when `bit_vec` is NULL
/// - when accessing `index` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_get(
bit_vec: NonNull<SPBitVec>,
@ -154,15 +93,7 @@ pub unsafe extern "C" fn sp_bitvec_get(
///
/// # Panics
///
/// - when `bit_vec` is NULL
/// - when accessing `index` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_set(
bit_vec: NonNull<SPBitVec>,
@ -178,17 +109,6 @@ pub unsafe extern "C" fn sp_bitvec_set(
///
/// - `bit_vec`: instance to write to
/// - `value`: the value to set all bits to
///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_fill(
bit_vec: NonNull<SPBitVec>,
@ -202,16 +122,6 @@ pub unsafe extern "C" fn sp_bitvec_fill(
/// # Arguments
///
/// - `bit_vec`: instance to write to
///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_len(bit_vec: NonNull<SPBitVec>) -> usize {
unsafe { bit_vec.as_ref().0.len() }
@ -222,16 +132,6 @@ pub unsafe extern "C" fn sp_bitvec_len(bit_vec: NonNull<SPBitVec>) -> usize {
/// # Arguments
///
/// - `bit_vec`: instance to write to
///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_is_empty(
bit_vec: NonNull<SPBitVec>,
@ -241,24 +141,14 @@ pub unsafe extern "C" fn sp_bitvec_is_empty(
/// Gets an unsafe reference to the data of the [SPBitVec] instance.
///
/// The returned memory is valid for the lifetime of the bitvec.
///
/// # Arguments
///
/// - `bit_vec`: instance to write to
///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// ## Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid [SPBitVec]
/// - the returned memory range is never accessed after the passed [SPBitVec] has been freed
/// - the returned memory range is never accessed concurrently, either via the [SPBitVec] or directly
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_unsafe_data_ref(
bit_vec: NonNull<SPBitVec>,
) -> SPByteSlice {
unsafe { SPByteSlice::from_slice((*bit_vec.as_ptr()).0.as_raw_mut_slice()) }
) -> ByteSlice {
unsafe { ByteSlice::from_slice((*bit_vec.as_ptr()).0.as_raw_mut_slice()) }
}

View file

@ -1,47 +1,25 @@
//! C functions for interacting with [SPBrightnessGrid]s
//!
//! prefix `sp_brightness_grid_`
//!
//!
//! A grid containing brightness values.
//!
//! # Examples
//! ```C
//! SPConnection connection = sp_connection_open("127.0.0.1:2342");
//! if (connection == NULL)
//! return 1;
//!
//! SPBrightnessGrid grid = sp_brightness_grid_new(2, 2);
//! sp_brightness_grid_set(grid, 0, 0, 0);
//! sp_brightness_grid_set(grid, 1, 1, 10);
//!
//! SPCommand command = sp_command_char_brightness(grid);
//! sp_connection_free(connection);
//! ```
use crate::SPByteSlice;
use servicepoint::{BrightnessGrid, DataRef, Grid};
use std::convert::Into;
use crate::ByteSlice;
use servicepoint::{Brightness, BrightnessGrid, ByteGrid, DataRef, Grid};
use std::mem::transmute;
use std::ptr::NonNull;
/// see [servicepoint::Brightness::MIN]
pub const SP_BRIGHTNESS_MIN: u8 = 0;
/// see [servicepoint::Brightness::MAX]
pub const SP_BRIGHTNESS_MAX: u8 = 11;
/// Count of possible brightness values
pub const SP_BRIGHTNESS_LEVELS: u8 = 12;
/// Creates a new [SPBrightnessGrid] with the specified dimensions.
/// Creates a new [BrightnessGrid] with the specified dimensions.
///
/// returns: [SPBrightnessGrid] initialized to 0. Will never return NULL.
/// returns: [BrightnessGrid] initialized to 0.
///
/// # Safety
/// # Examples
/// ```C
/// UdpConnection connection = sp_connection_open("127.0.0.1:2342");
/// if (connection == NULL)
/// return 1;
///
/// The caller has to make sure that:
/// BrightnessGrid grid = sp_brightness_grid_new(2, 2);
/// sp_brightness_grid_set(grid, 0, 0, 0);
/// sp_brightness_grid_set(grid, 1, 1, 10);
///
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_brightness_grid_free`.
/// TypedCommand command = sp_command_char_brightness(grid);
/// sp_connection_free(connection);
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_new(
width: usize,
@ -51,31 +29,17 @@ pub unsafe extern "C" fn sp_brightness_grid_new(
NonNull::from(Box::leak(result))
}
/// Loads a [SPBrightnessGrid] with the specified dimensions from the provided data.
/// Loads a [BrightnessGrid] with the specified dimensions from the provided data.
///
/// returns: new [SPBrightnessGrid] instance. Will never return NULL.
///
/// # Panics
///
/// - when `data` is NULL
/// - when the provided `data_length` does not match `height` and `width`
///
/// # 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_brightness_grid_free`.
/// returns: new [BrightnessGrid] instance, or NULL in case of an error.
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_load(
width: usize,
height: usize,
data: SPByteSlice,
data: ByteSlice,
) -> *mut BrightnessGrid {
let data = unsafe { data.as_slice() };
let grid = match servicepoint::ByteGrid::load(width, height, data) {
let grid = match ByteGrid::load(width, height, data) {
None => return std::ptr::null_mut(),
Some(grid) => grid,
};
@ -86,26 +50,7 @@ pub unsafe extern "C" fn sp_brightness_grid_load(
}
}
/// Clones a [SPBrightnessGrid].
///
/// # Arguments
///
/// - `brightness_grid`: instance to read from
///
/// returns: new [SPBrightnessGrid] instance. Will never return NULL.
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_brightness_grid_free`.
/// Clones a [BrightnessGrid].
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_clone(
brightness_grid: NonNull<BrightnessGrid>,
@ -114,25 +59,7 @@ pub unsafe extern "C" fn sp_brightness_grid_clone(
NonNull::from(Box::leak(result))
}
/// Deallocates a [SPBrightnessGrid].
///
/// # Arguments
///
/// - `brightness_grid`: instance to read from
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not used concurrently or after this call
/// - `brightness_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
///
/// [SPCommand]: [crate::SPCommand]
/// Deallocates a [BrightnessGrid].
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_free(
brightness_grid: NonNull<BrightnessGrid>,
@ -150,26 +77,17 @@ pub unsafe extern "C" fn sp_brightness_grid_free(
/// returns: value at position
///
/// # Panics
///
/// - when `brightness_grid` is NULL
/// - When accessing `x` or `y` out of bounds.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_get(
brightness_grid: NonNull<BrightnessGrid>,
x: usize,
y: usize,
) -> u8 {
unsafe { brightness_grid.as_ref().get(x, y) }.into()
) -> Brightness {
unsafe { brightness_grid.as_ref().get(x, y) }
}
/// Sets the value of the specified position in the [SPBrightnessGrid].
/// Sets the value of the specified position in the [BrightnessGrid].
///
/// # Arguments
///
@ -181,73 +99,38 @@ pub unsafe extern "C" fn sp_brightness_grid_get(
///
/// # Panics
///
/// - when `brightness_grid` is NULL
/// - When accessing `x` or `y` out of bounds.
/// - When providing an invalid brightness value
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_set(
brightness_grid: NonNull<BrightnessGrid>,
x: usize,
y: usize,
value: u8,
value: Brightness,
) {
let brightness = servicepoint::Brightness::try_from(value)
.expect("invalid brightness value");
unsafe { (*brightness_grid.as_ptr()).set(x, y, brightness) };
unsafe { (*brightness_grid.as_ptr()).set(x, y, value) };
}
/// Sets the value of all cells in the [SPBrightnessGrid].
/// Sets the value of all cells in the [BrightnessGrid].
///
/// # Arguments
///
/// - `brightness_grid`: instance to write to
/// - `value`: the value to set all cells to
///
/// # Panics
///
/// - when `brightness_grid` is NULL
/// - When providing an invalid brightness value
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_fill(
brightness_grid: NonNull<BrightnessGrid>,
value: u8,
value: Brightness,
) {
let brightness = servicepoint::Brightness::try_from(value)
.expect("invalid brightness value");
unsafe { (*brightness_grid.as_ptr()).fill(brightness) };
unsafe { (*brightness_grid.as_ptr()).fill(value) };
}
/// Gets the width of the [SPBrightnessGrid] instance.
/// Gets the width of the [BrightnessGrid] instance.
///
/// # Arguments
///
/// - `brightness_grid`: instance to read from
///
/// returns: width
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_width(
brightness_grid: NonNull<BrightnessGrid>,
@ -255,23 +138,13 @@ pub unsafe extern "C" fn sp_brightness_grid_width(
unsafe { brightness_grid.as_ref().width() }
}
/// Gets the height of the [SPBrightnessGrid] instance.
/// Gets the height of the [BrightnessGrid] instance.
///
/// # Arguments
///
/// - `brightness_grid`: instance to read from
///
/// returns: height
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_height(
brightness_grid: NonNull<BrightnessGrid>,
@ -279,31 +152,22 @@ pub unsafe extern "C" fn sp_brightness_grid_height(
unsafe { brightness_grid.as_ref().height() }
}
/// Gets an unsafe reference to the data of the [SPBrightnessGrid] instance.
/// Gets an unsafe reference to the data of the [BrightnessGrid] instance.
///
/// The returned memory is valid for the lifetime of the brightness grid.
///
/// # Arguments
///
/// - `brightness_grid`: instance to read from
///
/// returns: slice of bytes underlying the `brightness_grid`.
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - the returned memory range is never accessed after the passed [SPBrightnessGrid] has been freed
/// - the returned memory range is never accessed concurrently, either via the [SPBrightnessGrid] or directly
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref(
brightness_grid: NonNull<BrightnessGrid>,
) -> SPByteSlice {
assert_eq!(size_of::<servicepoint::Brightness>(), 1);
) -> ByteSlice {
//noinspection RsAssertEqual
const _: () = assert!(size_of::<Brightness>() == 1);
let data = unsafe { (*brightness_grid.as_ptr()).data_ref_mut() };
// this assumes more about the memory layout than rust guarantees. yikes!
unsafe { SPByteSlice::from_slice(transmute(data)) }
unsafe { ByteSlice::from_slice(transmute(data)) }
}

View file

@ -2,9 +2,7 @@
use std::ptr::NonNull;
/// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
///
/// You should not create an instance of this type in your C code.
/// Represents a span of memory (`&mut [u8]` ) as a struct.
///
/// # Safety
///
@ -13,17 +11,15 @@ use std::ptr::NonNull;
/// - accesses to the memory pointed to with `start` is never accessed outside `length`
/// - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in
/// the function returning this type.
/// - an instance of this created from C is never passed to a consuming function, as the rust code
/// will try to free the memory of a potentially separate allocator.
#[repr(C)]
pub struct SPByteSlice {
/// The start address of the memory
pub struct ByteSlice {
/// The start address of the memory.
pub start: NonNull<u8>,
/// The amount of memory in bytes
/// The amount of memory in bytes.
pub length: usize,
}
impl SPByteSlice {
impl ByteSlice {
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.start.as_ptr(), self.length) }
}

View file

@ -1,37 +1,19 @@
//! C functions for interacting with [SPCharGrid]s
//!
//! prefix `sp_char_grid_`
//!
//! A C-wrapper for grid containing UTF-8 characters.
//!
//! As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers.
//!
//! The encoding is enforced in most cases by the rust standard library
//! and will panic when provided with illegal characters.
//!
//! # Examples
//!
//! ```C
//! CharGrid grid = sp_char_grid_new(4, 3);
//! sp_char_grid_fill(grid, '?');
//! sp_char_grid_set(grid, 0, 0, '!');
//! sp_char_grid_free(grid);
//! ```
use crate::SPByteSlice;
use crate::ByteSlice;
use servicepoint::{CharGrid, Grid};
use std::ptr::NonNull;
/// Creates a new [SPCharGrid] with the specified dimensions.
/// Creates a new [CharGrid] with the specified dimensions.
///
/// returns: [SPCharGrid] initialized to 0. Will never return NULL.
/// returns: [CharGrid] initialized to 0.
///
/// # Safety
/// # Examples
///
/// 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_char_grid_free`.
/// ```C
/// CharGrid grid = sp_char_grid_new(4, 3);
/// sp_char_grid_fill(grid, '?');
/// sp_char_grid_set(grid, 0, 0, '!');
/// sp_char_grid_free(grid);
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_new(
width: usize,
@ -41,82 +23,39 @@ pub unsafe extern "C" fn sp_char_grid_new(
NonNull::from(Box::leak(result))
}
/// Loads a [SPCharGrid] with the specified dimensions from the provided data.
/// Loads a [CharGrid] with the specified dimensions from the provided data.
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `data` is NULL
/// - when the provided `data_length` does not match `height` and `width`
/// - when `data` is not valid UTF-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_char_grid_free`.
/// returns: new CharGrid or NULL in case of an error
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_load(
width: usize,
height: usize,
data: SPByteSlice,
) -> NonNull<CharGrid> {
data: ByteSlice,
) -> *mut CharGrid {
let data = unsafe { data.as_slice() };
// TODO remove unwrap
let result =
Box::new(CharGrid::load_utf8(width, height, data.to_vec()).unwrap());
NonNull::from(Box::leak(result))
if let Ok(grid) = CharGrid::load_utf8(width, height, data.to_vec()) {
Box::leak(Box::new(grid))
} else {
std::ptr::null_mut()
}
}
/// Clones a [SPCharGrid].
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `char_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `char_grid` points to a valid [SPCharGrid]
/// - `char_grid` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_char_grid_free`.
/// Clones a [CharGrid].
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_clone(
char_grid: NonNull<CharGrid>,
) -> NonNull<CharGrid> {
let result = Box::new(unsafe { char_grid.as_ref().clone() });
NonNull::from(Box::leak(result))
let result = unsafe { char_grid.as_ref().clone() };
NonNull::from(Box::leak(Box::new(result)))
}
/// Deallocates a [SPCharGrid].
///
/// # Panics
///
/// - when `char_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `char_grid` points to a valid [SPCharGrid]
/// - `char_grid` is not used concurrently or after char_grid call
/// - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
///
/// [SPCommand]: [crate::SPCommand]
/// Deallocates a [CharGrid].
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_free(char_grid: NonNull<CharGrid>) {
_ = unsafe { Box::from_raw(char_grid.as_ptr()) };
}
/// Gets the current value at the specified position.
/// Returns the current value at the specified position.
///
/// # Arguments
///
@ -125,15 +64,7 @@ pub unsafe extern "C" fn sp_char_grid_free(char_grid: NonNull<CharGrid>) {
///
/// # Panics
///
/// - when `char_grid` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `char_grid` points to a valid [SPCharGrid]
/// - `char_grid` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_get(
char_grid: NonNull<CharGrid>,
@ -143,7 +74,7 @@ pub unsafe extern "C" fn sp_char_grid_get(
unsafe { char_grid.as_ref().get(x, y) as u32 }
}
/// Sets the value of the specified position in the [SPCharGrid].
/// Sets the value of the specified position in the [CharGrid].
///
/// # Arguments
///
@ -155,17 +86,7 @@ pub unsafe extern "C" fn sp_char_grid_get(
///
/// # Panics
///
/// - when `char_grid` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `char_grid` points to a valid [SPBitVec]
/// - `char_grid` is not written to or read from concurrently
///
/// [SPBitVec]: [crate::SPBitVec]
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_set(
char_grid: NonNull<CharGrid>,
@ -176,23 +97,12 @@ pub unsafe extern "C" fn sp_char_grid_set(
unsafe { (*char_grid.as_ptr()).set(x, y, char::from_u32(value).unwrap()) };
}
/// Sets the value of all cells in the [SPCharGrid].
/// Sets the value of all cells in the [CharGrid].
///
/// # Arguments
///
/// - `char_grid`: instance to write to
/// - `value`: the value to set all cells to
///
/// # Panics
///
/// - when `char_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `char_grid` points to a valid [SPCharGrid]
/// - `char_grid` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_fill(
char_grid: NonNull<CharGrid>,
@ -201,21 +111,11 @@ pub unsafe extern "C" fn sp_char_grid_fill(
unsafe { (*char_grid.as_ptr()).fill(char::from_u32(value).unwrap()) };
}
/// Gets the width of the [SPCharGrid] instance.
/// Gets the width of the [CharGrid] instance.
///
/// # Arguments
///
/// - `char_grid`: instance to read from
///
/// # Panics
///
/// - when `char_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `char_grid` points to a valid [SPCharGrid]
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_width(
char_grid: NonNull<CharGrid>,
@ -223,21 +123,11 @@ pub unsafe extern "C" fn sp_char_grid_width(
unsafe { char_grid.as_ref().width() }
}
/// Gets the height of the [SPCharGrid] instance.
/// Gets the height of the [CharGrid] instance.
///
/// # Arguments
///
/// - `char_grid`: instance to read from
///
/// # Panics
///
/// - when `char_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `char_grid` points to a valid [SPCharGrid]
#[no_mangle]
pub unsafe extern "C" fn sp_char_grid_height(
char_grid: NonNull<CharGrid>,

View file

@ -1,48 +1,12 @@
//! C functions for interacting with [SPCommand]s
//!
//! prefix `sp_command_`
use crate::SPBitVec;
use servicepoint::{
BinaryOperation, Bitmap, BrightnessGrid, CharGrid, CompressionCode,
Cp437Grid, GlobalBrightnessCommand, Packet, TypedCommand,
};
use servicepoint::{BinaryOperation, Bitmap, Brightness, BrightnessGrid, CharGrid, CompressionCode, Cp437Grid, GlobalBrightnessCommand, Packet, TypedCommand};
use std::ptr::NonNull;
/// A low-level display command.
///
/// This struct and associated functions implement the UDP protocol for the display.
///
/// To send a [SPCommand], use a [SPConnection].
///
/// # Examples
///
/// ```C
/// sp_connection_send_command(connection, sp_command_clear());
/// sp_connection_send_command(connection, sp_command_brightness(5));
/// ```
///
/// [SPConnection]: [crate::SPConnection]
/// Tries to turn a [SPPacket] into a [SPCommand].
/// Tries to turn a [Packet] into a [TypedCommand].
///
/// The packet is deallocated in the process.
///
/// Returns: pointer to new [SPCommand] instance or NULL if parsing failed.
///
/// # Panics
///
/// - when `packet` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - [SPPacket] points to a valid instance of [SPPacket]
/// - [SPPacket] is not used concurrently or after this call
/// - the result is checked for NULL
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: pointer to new [TypedCommand] instance or NULL if parsing failed.
#[no_mangle]
pub unsafe extern "C" fn sp_command_try_from_packet(
packet: NonNull<Packet>,
@ -54,22 +18,9 @@ pub unsafe extern "C" fn sp_command_try_from_packet(
}
}
/// Clones a [SPCommand] instance.
/// Clones a [TypedCommand] instance.
///
/// returns: new [SPCommand] instance. Will never return NULL.
///
/// # Panics
///
/// - when `command` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `command` points to a valid instance of [SPCommand]
/// - `command` is not written to concurrently
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// returns: new [TypedCommand] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_clone(
command: NonNull<TypedCommand>,
@ -82,20 +33,13 @@ pub unsafe extern "C" fn sp_command_clone(
///
/// Does not affect brightness.
///
/// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::Clear] instance.
///
/// # Examples
///
/// ```C
/// sp_connection_send_command(connection, sp_command_clear());
/// ```
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_command_clear() -> NonNull<TypedCommand> {
let result = Box::new(servicepoint::ClearCommand.into());
@ -106,14 +50,7 @@ pub unsafe extern "C" fn sp_command_clear() -> NonNull<TypedCommand> {
///
/// Please do not send this in your normal program flow.
///
/// Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::HardReset] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<TypedCommand> {
let result = Box::new(servicepoint::HardResetCommand.into());
@ -122,14 +59,7 @@ pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<TypedCommand> {
/// A yet-to-be-tested command.
///
/// Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::FadeOut] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<TypedCommand> {
let result = Box::new(servicepoint::FadeOutCommand.into());
@ -138,46 +68,20 @@ pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<TypedCommand> {
/// Set the brightness of all tiles to the same value.
///
/// Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL.
///
/// # Panics
///
/// - When the provided brightness value is out of range (0-11).
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::Brightness] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_brightness(
brightness: u8,
brightness: Brightness,
) -> NonNull<TypedCommand> {
let brightness = servicepoint::Brightness::try_from(brightness)
.expect("invalid brightness");
let result = Box::new(GlobalBrightnessCommand::from(brightness).into());
NonNull::from(Box::leak(result))
}
/// Set the brightness of individual tiles in a rectangular area of the display.
///
/// The passed [SPBrightnessGrid] gets consumed.
/// The passed [BrightnessGrid] gets consumed.
///
/// Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL.
///
/// # Panics
///
/// - when `grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `grid` points to a valid instance of [SPBrightnessGrid]
/// - `grid` is not used concurrently or after this call
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::CharBrightness] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_char_brightness(
x: usize,
@ -204,22 +108,7 @@ pub unsafe extern "C" fn sp_command_char_brightness(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::BitmapLinear] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear(
offset: usize,
@ -245,22 +134,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_and(
offset: usize,
@ -286,22 +160,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::BitmapLinearOr] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_or(
offset: usize,
@ -327,22 +186,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::BitmapLinearXor] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
offset: usize,
@ -359,7 +203,6 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
}
}
#[inline]
unsafe fn sp_command_bitmap_linear_internal(
offset: usize,
bit_vec: NonNull<SPBitVec>,
@ -383,22 +226,9 @@ unsafe fn sp_command_bitmap_linear_internal(
/// Show codepage 437 encoded text on the screen.
///
/// The passed [SPCp437Grid] gets consumed.
/// The passed [Cp437Grid] gets consumed.
///
/// Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL.
///
/// # Panics
///
/// - when `grid` is null
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `grid` points to a valid instance of [SPCp437Grid]
/// - `grid` is not used concurrently or after this call
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::Cp437Data] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_cp437_data(
x: usize,
@ -418,22 +248,9 @@ pub unsafe extern "C" fn sp_command_cp437_data(
/// Show UTF-8 encoded text on the screen.
///
/// The passed [SPCharGrid] gets consumed.
/// The passed [CharGrid] gets consumed.
///
/// Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL.
///
/// # Panics
///
/// - when `grid` is null
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `grid` points to a valid instance of [SPCharGrid]
/// - `grid` is not used concurrently or after this call
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::Utf8Data] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_utf8_data(
x: usize,
@ -453,24 +270,9 @@ pub unsafe extern "C" fn sp_command_utf8_data(
/// Sets a window of pixels to the specified values.
///
/// The passed [SPBitmap] gets consumed.
/// The passed [Bitmap] gets consumed.
///
/// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bitmap` is null
/// - when `compression_code` is not a valid value
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid instance of [SPBitmap]
/// - `bitmap` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`.
/// Returns: a new [servicepoint::Command::BitmapLinearWin] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_win(
x: usize,
@ -492,26 +294,14 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win(
Box::leak(Box::new(command))
}
/// Deallocates a [SPCommand].
/// Deallocates a [TypedCommand].
///
/// # Examples
///
/// ```C
/// SPCommand c = sp_command_clear();
/// TypedCommand c = sp_command_clear();
/// sp_command_free(c);
/// ```
///
/// # Panics
///
/// - when `command` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `command` points to a valid [SPCommand]
/// - `command` is not used concurrently or after this call
/// - `command` was not passed to another consuming function, e.g. to create a [SPPacket]
#[no_mangle]
pub unsafe extern "C" fn sp_command_free(command: NonNull<TypedCommand>) {
_ = unsafe { Box::from_raw(command.as_ptr()) };

View file

@ -1,35 +1,18 @@
//! C functions for interacting with [SPConnection]s
//!
//! prefix `sp_connection_`
//!
//! A connection to the display.
//!
//! # Examples
//!
//! ```C
//! CConnection connection = sp_connection_open("172.23.42.29:2342");
//! if (connection != NULL)
//! sp_connection_send_command(connection, sp_command_clear());
//! ```
use servicepoint::{Connection, Packet, TypedCommand, UdpConnection};
use std::ffi::{c_char, CStr};
use std::ptr::NonNull;
/// Creates a new instance of [SPConnection].
/// Creates a new instance of [UdpConnection].
///
/// returns: NULL if connection fails, or connected instance
///
/// # Panics
/// # Examples
///
/// - when `host` is null or an invalid host
///
/// # 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_free`.
/// ```C
/// CConnection connection = sp_connection_open("172.23.42.29:2342");
/// if (connection != NULL)
/// sp_connection_send_command(connection, sp_command_clear());
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_connection_open(
host: NonNull<c_char>,
@ -48,18 +31,18 @@ pub unsafe extern "C" fn sp_connection_open(
//#[no_mangle]
//pub unsafe extern "C" fn sp_connection_open_ipv4(
// host: SocketAddrV4,
//) -> *mut SPConnection {
//) -> *mut UdpConnection {
// let connection = match servicepoint::UdpConnection::open(host) {
// Err(_) => return std::ptr::null_mut(),
// Ok(value) => value,
// };
//
// Box::into_raw(Box::new(SPConnection(connection)))
// Box::into_raw(Box::new(UdpConnection(connection)))
//}
// /// Creates a new instance of [SPUdpConnection] for testing that does not actually send anything.
// ///
// /// returns: a new instance. Will never return NULL.
// /// returns: a new instance.
// ///
// /// # Safety
// ///
@ -73,24 +56,11 @@ pub unsafe extern "C" fn sp_connection_open(
// NonNull::from(Box::leak(result))
// }
/// Sends a [SPPacket] to the display using the [SPConnection].
/// Sends a [Packet] to the display using the [UdpConnection].
///
/// The passed `packet` gets consumed.
///
/// returns: true in case of success
///
/// # Panics
///
/// - when `connection` is NULL
/// - when `packet` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `connection` points to a valid instance of [SPConnection]
/// - `packet` points to a valid instance of [SPPacket]
/// - `packet` is not used concurrently or after this call
#[no_mangle]
pub unsafe extern "C" fn sp_connection_send_packet(
connection: NonNull<UdpConnection>,
@ -100,24 +70,18 @@ pub unsafe extern "C" fn sp_connection_send_packet(
unsafe { connection.as_ref().send(*packet) }.is_ok()
}
/// Sends a [SPCommand] to the display using the [SPConnection].
/// Sends a [TypedCommand] to the display using the [UdpConnection].
///
/// The passed `command` gets consumed.
///
/// returns: true in case of success
///
/// # Panics
/// # Examples
///
/// - when `connection` is NULL
/// - when `command` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `connection` points to a valid instance of [SPConnection]
/// - `command` points to a valid instance of [SPPacket]
/// - `command` is not used concurrently or after this call
/// ```C
/// sp_connection_send_command(connection, sp_command_clear());
/// sp_connection_send_command(connection, sp_command_brightness(5));
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_connection_send_command(
connection: NonNull<UdpConnection>,
@ -127,18 +91,7 @@ pub unsafe extern "C" fn sp_connection_send_command(
unsafe { connection.as_ref().send(command) }.is_ok()
}
/// Closes and deallocates a [SPConnection].
///
/// # Panics
///
/// - when `connection` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `connection` points to a valid [SPConnection]
/// - `connection` is not used concurrently or after this call
/// Closes and deallocates a [UdpConnection].
#[no_mangle]
pub unsafe extern "C" fn sp_connection_free(
connection: NonNull<UdpConnection>,

View file

@ -1,35 +1,10 @@
//! C functions for interacting with [SPCp437Grid]s
//!
//! prefix `sp_cp437_grid_`
//!
//!
//! A C-wrapper for grid containing codepage 437 characters.
//!
//! The encoding is currently not enforced.
//!
//! # Examples
//!
//! ```C
//! Cp437Grid grid = sp_cp437_grid_new(4, 3);
//! sp_cp437_grid_fill(grid, '?');
//! sp_cp437_grid_set(grid, 0, 0, '!');
//! sp_cp437_grid_free(grid);
//! ```
use crate::SPByteSlice;
use crate::ByteSlice;
use servicepoint::{Cp437Grid, DataRef, Grid};
use std::ptr::NonNull;
/// Creates a new [SPCp437Grid] with the specified dimensions.
/// Creates a new [Cp437Grid] with the specified dimensions.
///
/// returns: [SPCp437Grid] initialized to 0. Will never return NULL.
///
/// # 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_cp437_grid_free`.
/// returns: [Cp437Grid] initialized to 0.
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_new(
width: usize,
@ -39,28 +14,12 @@ pub unsafe extern "C" fn sp_cp437_grid_new(
NonNull::from(Box::leak(result))
}
/// Loads a [SPCp437Grid] with the specified dimensions from the provided data.
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `data` is NULL
/// - when the provided `data_length` does not match `height` and `width`
///
/// # 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_cp437_grid_free`.
/// Loads a [Cp437Grid] with the specified dimensions from the provided data.
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_load(
width: usize,
height: usize,
data: SPByteSlice,
data: ByteSlice,
) -> *mut Cp437Grid {
let data = unsafe { data.as_slice() };
let grid = Cp437Grid::load(width, height, data);
@ -71,22 +30,7 @@ pub unsafe extern "C" fn sp_cp437_grid_load(
}
}
/// Clones a [SPCp437Grid].
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_cp437_grid_free`.
/// Clones a [Cp437Grid].
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_clone(
cp437_grid: NonNull<Cp437Grid>,
@ -95,21 +39,7 @@ pub unsafe extern "C" fn sp_cp437_grid_clone(
NonNull::from(Box::leak(result))
}
/// Deallocates a [SPCp437Grid].
///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not used concurrently or after cp437_grid call
/// - `cp437_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
///
/// [SPCommand]: [crate::SPCommand]
/// Deallocates a [Cp437Grid].
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: NonNull<Cp437Grid>) {
_ = unsafe { Box::from_raw(cp437_grid.as_ptr()) };
@ -124,15 +54,7 @@ pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: NonNull<Cp437Grid>) {
///
/// # Panics
///
/// - when `cp437_grid` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_get(
cp437_grid: NonNull<Cp437Grid>,
@ -142,7 +64,7 @@ pub unsafe extern "C" fn sp_cp437_grid_get(
unsafe { cp437_grid.as_ref().get(x, y) }
}
/// Sets the value of the specified position in the [SPCp437Grid].
/// Sets the value of the specified position in the [Cp437Grid].
///
/// # Arguments
///
@ -154,17 +76,7 @@ pub unsafe extern "C" fn sp_cp437_grid_get(
///
/// # Panics
///
/// - when `cp437_grid` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPBitVec]
/// - `cp437_grid` is not written to or read from concurrently
///
/// [SPBitVec]: [crate::SPBitVec]
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_set(
cp437_grid: NonNull<Cp437Grid>,
@ -175,23 +87,12 @@ pub unsafe extern "C" fn sp_cp437_grid_set(
unsafe { (*cp437_grid.as_ptr()).set(x, y, value) };
}
/// Sets the value of all cells in the [SPCp437Grid].
/// Sets the value of all cells in the [Cp437Grid].
///
/// # Arguments
///
/// - `cp437_grid`: instance to write to
/// - `value`: the value to set all cells to
///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_fill(
cp437_grid: NonNull<Cp437Grid>,
@ -200,21 +101,11 @@ pub unsafe extern "C" fn sp_cp437_grid_fill(
unsafe { (*cp437_grid.as_ptr()).fill(value) };
}
/// Gets the width of the [SPCp437Grid] instance.
/// Gets the width of the [Cp437Grid] instance.
///
/// # Arguments
///
/// - `cp437_grid`: instance to read from
///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPCp437Grid]
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_width(
cp437_grid: NonNull<Cp437Grid>,
@ -222,21 +113,11 @@ pub unsafe extern "C" fn sp_cp437_grid_width(
unsafe { cp437_grid.as_ref().width() }
}
/// Gets the height of the [SPCp437Grid] instance.
/// Gets the height of the [Cp437Grid] instance.
///
/// # Arguments
///
/// - `cp437_grid`: instance to read from
///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPCp437Grid]
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_height(
cp437_grid: NonNull<Cp437Grid>,
@ -244,24 +125,12 @@ pub unsafe extern "C" fn sp_cp437_grid_height(
unsafe { cp437_grid.as_ref().height() }
}
/// Gets an unsafe reference to the data of the [SPCp437Grid] instance.
/// Gets an unsafe reference to the data of the [Cp437Grid] instance.
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// ## Safety
///
/// The caller has to make sure that:
///
/// - `cp437_grid` points to a valid [SPCp437Grid]
/// - the returned memory range is never accessed after the passed [SPCp437Grid] has been freed
/// - the returned memory range is never accessed concurrently, either via the [SPCp437Grid] or directly
/// The returned memory is valid for the lifetime of the grid.
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref(
cp437_grid: NonNull<Cp437Grid>,
) -> SPByteSlice {
unsafe { SPByteSlice::from_slice((*cp437_grid.as_ptr()).data_ref_mut()) }
) -> ByteSlice {
unsafe { ByteSlice::from_slice((*cp437_grid.as_ptr()).data_ref_mut()) }
}

View file

@ -9,14 +9,14 @@
//! #include "servicepoint.h"
//!
//! int main(void) {
//! SPConnection *connection = sp_connection_open("172.23.42.29:2342");
//! UdpConnection *connection = sp_connection_open("172.23.42.29:2342");
//! if (connection == NULL)
//! return 1;
//!
//! SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
//! Bitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
//! sp_bitmap_fill(pixels, true);
//!
//! SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
//! TypedCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
//! while (sp_connection_send_command(connection, sp_command_clone(command)));
//!
//! sp_command_free(command);

View file

@ -1,31 +1,11 @@
//! C functions for interacting with [SPPacket]s
//!
//! prefix `sp_packet_`
//!
//!
//! The raw packet
use crate::SPByteSlice;
use crate::ByteSlice;
use servicepoint::{Header, Packet, TypedCommand};
use std::ptr::NonNull;
/// Turns a [SPCommand] into a [SPPacket].
/// The [SPCommand] gets consumed.
/// Turns a [TypedCommand] into a [Packet].
/// The [TypedCommand] gets consumed.
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `command` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - [SPCommand] points to a valid instance of [SPCommand]
/// - [SPCommand] is not used concurrently or after this call
/// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_packet_free`.
/// Returns NULL in case of an error.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_from_command(
command: NonNull<TypedCommand>,
@ -38,24 +18,11 @@ pub unsafe extern "C" fn sp_packet_from_command(
}
}
/// Tries to load a [SPPacket] from the passed array with the specified length.
/// 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
///
/// # Panics
///
/// - when `data` is NULL
///
/// # 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 [SPPacket] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_packet_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_try_load(data: SPByteSlice) -> *mut Packet {
pub unsafe extern "C" fn sp_packet_try_load(data: ByteSlice) -> *mut Packet {
let data = unsafe { data.as_slice() };
match servicepoint::Packet::try_from(data) {
Err(_) => std::ptr::null_mut(),
@ -63,34 +30,13 @@ pub unsafe extern "C" fn sp_packet_try_load(data: SPByteSlice) -> *mut Packet {
}
}
/// Creates a raw [SPPacket] from parts.
///
/// # Arguments
///
/// - `command_code` specifies which command this packet contains
/// - `a`, `b`, `c` and `d` are command-specific header values
/// - `payload` is the optional data that is part of the command
/// - `payload_len` is the size of the payload
/// Creates a raw [Packet] from parts.
///
/// returns: new instance. Will never return null.
///
/// # Panics
///
/// - when `payload` is null, but `payload_len` is not zero
/// - when `payload_len` is zero, but `payload` is nonnull
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `payload` points to a valid memory region of at least `payload_len` bytes
/// - `payload` is not written to concurrently
/// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
/// by explicitly calling [sp_packet_free].
#[no_mangle]
pub unsafe extern "C" fn sp_packet_from_parts(
header: Header,
payload: *const SPByteSlice,
payload: *const ByteSlice,
) -> NonNull<Packet> {
let payload = if payload.is_null() {
vec![]
@ -103,6 +49,9 @@ pub unsafe extern "C" fn sp_packet_from_parts(
NonNull::from(Box::leak(packet))
}
/// Returns a pointer to the header field of the provided packet.
///
/// The returned header can be changed and will be valid for the lifetime of the packet.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_get_header(
packet: NonNull<Packet>,
@ -110,47 +59,43 @@ pub unsafe extern "C" fn sp_packet_get_header(
NonNull::from(&mut unsafe { (*packet.as_ptr()).header })
}
/// Returns a pointer to the current payload of the provided packet.
///
/// The returned memory can be changed and will be valid until a new payload is set.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_get_payload(
packet: NonNull<Packet>,
) -> SPByteSlice {
unsafe { SPByteSlice::from_slice(&mut *(*packet.as_ptr()).payload) }
) -> ByteSlice {
unsafe { ByteSlice::from_slice(&mut *(*packet.as_ptr()).payload) }
}
/// Sets the payload of the provided packet to the provided data.
///
/// This makes previous payload pointers invalid.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_set_payload(
packet: NonNull<Packet>,
data: SPByteSlice,
data: ByteSlice,
) {
unsafe { (*packet.as_ptr()).payload = data.as_slice().to_vec() }
}
/// Serialize the packet into the provided buffer.
///
/// # Panics
///
/// - if the buffer is not big enough to hold header+payload.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_write_to(
pub unsafe extern "C" fn sp_packet_serialize_to(
packet: NonNull<Packet>,
buffer: SPByteSlice,
buffer: ByteSlice,
) {
unsafe {
packet.as_ref().serialize_to(buffer.as_slice_mut());
}
}
/// Clones a [SPPacket].
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `packet` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `packet` points to a valid [SPPacket]
/// - `packet` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_packet_free`.
/// Clones a [Packet].
#[no_mangle]
pub unsafe extern "C" fn sp_packet_clone(
packet: NonNull<Packet>,
@ -159,18 +104,7 @@ pub unsafe extern "C" fn sp_packet_clone(
NonNull::from(Box::leak(result))
}
/// Deallocates a [SPPacket].
///
/// # Panics
///
/// - when `packet` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `packet` points to a valid [SPPacket]
/// - `packet` is not used concurrently or after this call
/// Deallocates a [Packet].
#[no_mangle]
pub unsafe extern "C" fn sp_packet_free(packet: NonNull<Packet>) {
_ = unsafe { Box::from_raw(packet.as_ptr()) }