diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9089694..b3749c3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,20 +25,10 @@ jobs: - name: Install rust toolchain run: sudo apt-get install -qy cargo rust-clippy - name: install lzma - run: sudo apt-get install -qy liblzma-dev - - name: install gcc - run: sudo apt-get install -qy gcc make + run: sudo apt-get update && sudo apt-get install -y liblzma-dev - 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..36f8920 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# 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. diff --git a/README.md b/README.md index 2f089d3..5204572 100644 --- a/README.md +++ b/README.md @@ -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](https://git.berlin.ccc.de/servicepoint/servicepoint) library, enabling users to parse, encode and send packets to this display via UDP. +This crate contains C bindings for the `servicepoint` library, enabling users to parse, encode and send packets to this display via UDP. ## Examples @@ -43,61 +43,21 @@ Please specify the full version including patch in your Cargo.toml until 1.0 is ## Installation -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 - ``` +Copy the header to your project and compile against. 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_` \ \. -- 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 or Result turn into nullable return values - check for NULL! -- There are no specifics for C++ here yet. You might get a nicer header when generating directly for C++, but it should - be usable. +- 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/) - -## 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. +- documentation is included in the header and available [online](https://docs.rs/servicepoint_binding_c/latest/servicepoint_binding_c/) ## 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. diff --git a/about_display.md b/about_display.md new file mode 100644 index 0000000..4fe7911 --- /dev/null +++ b/about_display.md @@ -0,0 +1,41 @@ +# 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. diff --git a/cbindgen.toml b/cbindgen.toml index 363d001..48ef574 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -35,8 +35,7 @@ include = [] exclude = [] [export.rename] -"SpBitVec" = "BitVec" -"SpByteSlice" = "ByteSlice" +"TypedCommand" = "Command" [enum] rename_variants = "QualifiedScreamingSnakeCase" diff --git a/examples/lang_c/Cargo.toml b/examples/lang_c/Cargo.toml new file mode 100644 index 0000000..2231f3c --- /dev/null +++ b/examples/lang_c/Cargo.toml @@ -0,0 +1,14 @@ +[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 = "../.." } diff --git a/example/Makefile b/examples/lang_c/Makefile similarity index 82% rename from example/Makefile rename to examples/lang_c/Makefile index e7d858f..aa31733 100644 --- a/example/Makefile +++ b/examples/lang_c/Makefile @@ -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 := $(REPO_ROOT)/include +export SERVICEPOINT_HEADER_OUT := $(THIS_DIR)/include -build: out/example +build: out/lang_c clean: rm -r out || true rm include/servicepoint.h || true cargo clean -run: out/example - out/example +run: out/lang_c + out/lang_c PHONY: build clean dependencies run -out/example_unstripped: dependencies main.c +out/lang_c_unstripped: dependencies src/main.c mkdir -p out || true - ${CC} main.c \ + ${CC} src/main.c \ -I $(SERVICEPOINT_HEADER_OUT) \ -L $(RUST_TARGET_DIR)\ $(CCFLAGS) \ - -o out/example_unstripped -out/example: out/example_unstripped - strip -s -R .comment -R .gnu.version --strip-unneeded out/example_unstripped -o out/example + -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 #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 diff --git a/examples/lang_c/build.rs b/examples/lang_c/build.rs new file mode 100644 index 0000000..4f92e1d --- /dev/null +++ b/examples/lang_c/build.rs @@ -0,0 +1,17 @@ +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"); +} diff --git a/examples/lang_c/include/servicepoint.h b/examples/lang_c/include/servicepoint.h new file mode 100644 index 0000000..4a0af95 --- /dev/null +++ b/examples/lang_c/include/servicepoint.h @@ -0,0 +1,1973 @@ +/* Generated with cbindgen:0.28.0 */ + +/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ + +#include +#include +#include +#include +#include + +/** + * pixel count on whole screen + */ +#define PIXEL_COUNT (PIXEL_WIDTH * PIXEL_HEIGHT) + +/** + * Display height in pixels + * + * # Examples + * + * ```rust + * # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; + * let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); + * ``` + */ +#define PIXEL_HEIGHT (TILE_HEIGHT * TILE_SIZE) + +/** + * Display width in pixels + * + * # Examples + * + * ```rust + * # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; + * let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); + * ``` + */ +#define PIXEL_WIDTH (TILE_WIDTH * TILE_SIZE) + +/** + * Count of possible brightness values + */ +#define SP_BRIGHTNESS_LEVELS 12 + +/** + * see [servicepoint::Brightness::MAX] + */ +#define SP_BRIGHTNESS_MAX 11 + +/** + * see [servicepoint::Brightness::MIN] + */ +#define SP_BRIGHTNESS_MIN 0 + +/** + * Display tile count in the y-direction + * + * # Examples + * + * ```rust + * # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; + * let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); + * ``` + */ +#define TILE_HEIGHT 20 + +/** + * size of a single tile in one dimension + */ +#define TILE_SIZE 8 + +/** + * Display tile count in the x-direction + * + * # Examples + * + * ```rust + * # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; + * let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); + * ``` + */ +#define TILE_WIDTH 56 + +/** + * Specifies the kind of compression to use. Availability depends on features. + * + * # Examples + * + * ```rust + * # use servicepoint::*; + * // create command without payload compression + * # let pixels = Bitmap::max_sized(); + * _ = BitmapCommand { + * origin: Origin::ZERO, + * bitmap: pixels, + * compression: CompressionCode::Uncompressed + * }; + * + * // create command with payload compressed with lzma and appropriate header flags + * # let pixels = Bitmap::max_sized(); + * _ = BitmapCommand { + * origin: Origin::ZERO, + * bitmap: pixels, + * compression: CompressionCode::Lzma + * }; + * ``` + */ +enum CompressionCode +#ifdef __cplusplus + : uint16_t +#endif // __cplusplus + { + /** + * no compression + */ + COMPRESSION_CODE_UNCOMPRESSED = 0, + /** + * compress using flate2 with zlib header + */ + COMPRESSION_CODE_ZLIB = 26490, + /** + * compress using bzip2 + */ + COMPRESSION_CODE_BZIP2 = 25210, + /** + * compress using lzma + */ + COMPRESSION_CODE_LZMA = 27770, + /** + * compress using Zstandard + */ + COMPRESSION_CODE_ZSTD = 31347, +}; +#ifndef __cplusplus +typedef uint16_t CompressionCode; +#endif // __cplusplus + +/** + * A fixed-size 2D grid of booleans. + * + * The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels. + * This means that no conversion is necessary for sending the data to the display. + * The downside is that the width has to be a multiple of 8. + * + * # Examples + * + * ```rust + * use servicepoint::Bitmap; + * let mut bitmap = Bitmap::new(8, 2); + * + * ``` + */ +typedef struct Bitmap Bitmap; + +/** + * The raw packet. + * + * Contents should probably only be used directly to use features not exposed by the library. + * + * You may want to use [`crate::Command`] or [`crate::TypedCommand`] instead. + */ +typedef struct Packet Packet; + +/** + * A vector of bits + * + * # Examples + * ```C + * SPBitVec vec = sp_bitvec_new(8); + * sp_bitvec_set(vec, 5, true); + * sp_bitvec_free(vec); + * ``` + */ +typedef struct SPBitVec SPBitVec; + +/** + * This enum contains all commands provided by the library. + * This is useful in case you want one data type for all kinds of commands without using `dyn`. + * + * Please look at the contained structs for documentation per command. + */ +typedef struct Command Command; + +/** + * A connection using the UDP protocol. + * + * Use this when sending commands directly to the display. + * + * Requires the feature "`protocol_udp`" which is enabled by default. + */ +typedef struct UdpConnection UdpConnection; + +/** + * A 2D grid of values. + * + * The memory layout is the one the display expects in [`crate::Command`]s. + * + * This structure can be used with any type that implements the [Value] trait. + * You can also use the concrete type aliases provided in this crate, e.g. [`crate::CharGrid`] and [`crate::ByteGrid`]. + */ +typedef struct ValueGrid_Brightness ValueGrid_Brightness; + +/** + * A 2D grid of values. + * + * The memory layout is the one the display expects in [`crate::Command`]s. + * + * This structure can be used with any type that implements the [Value] trait. + * You can also use the concrete type aliases provided in this crate, e.g. [`crate::CharGrid`] and [`crate::ByteGrid`]. + */ +typedef struct ValueGrid_char ValueGrid_char; + +/** + * A 2D grid of values. + * + * The memory layout is the one the display expects in [`crate::Command`]s. + * + * This structure can be used with any type that implements the [Value] trait. + * You can also use the concrete type aliases provided in this crate, e.g. [`crate::CharGrid`] and [`crate::ByteGrid`]. + */ +typedef struct ValueGrid_u8 ValueGrid_u8; + +/** + * 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. + * + * # Safety + * + * The caller has to make sure that: + * + * - 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. + */ +typedef struct { + /** + * The start address of the memory + */ + uint8_t */*notnull*/ start; + /** + * The amount of memory in bytes + */ + size_t length; +} SPByteSlice; + +/** + * A grid containing brightness values. + * + * # Examples + * + * ```rust + * # use servicepoint::*; + * let mut grid = BrightnessGrid::new(2,2); + * grid.set(0, 0, Brightness::MIN); + * grid.set(1, 1, Brightness::MIN); + * + * # let connection = FakeConnection; + * connection.send(BrightnessGridCommand { + * origin: Origin::new(3, 7), + * grid + * }).unwrap() + * ``` + */ +typedef ValueGrid_Brightness BrightnessGrid; + +/** + * A grid containing UTF-8 characters. + * + * To send a `CharGrid` to the display, use a [`crate::CharGridCommand`]. + * + * Also see [`ValueGrid`] for the non-specialized operations and examples. + * + * # Examples + * + * ```rust + * # use servicepoint::*; + * let grid = CharGrid::from("You can\nload multiline\nstrings directly"); + * assert_eq!(grid.get_row_str(1), Some("load multiline\0\0".to_string())); + * + * # let connection = FakeConnection; + * let command = CharGridCommand { origin: Origin::ZERO, grid }; + * connection.send(command).unwrap() + * ``` + */ +typedef ValueGrid_char CharGrid; + +/** + * A grid containing codepage 437 characters. + * + * The encoding is currently not enforced. + */ +typedef ValueGrid_u8 Cp437Grid; + +/** + * A raw header. + * + * The header specifies the kind of command, the size of the payload and where to display the + * payload, where applicable. + * + * Because the meaning of most fields depend on the command, there are no speaking names for them. + * + * The contained values are in platform endian-ness and may need to be converted before sending. + */ +typedef struct { + /** + * The first two bytes specify which command this packet represents. + */ + uint16_t command_code; + /** + * First command-specific value + */ + uint16_t a; + /** + * Second command-specific value + */ + uint16_t b; + /** + * Third command-specific value + */ + uint16_t c; + /** + * Fourth command-specific value + */ + uint16_t d; +} Header; + + + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * 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`. + */ +Bitmap */*notnull*/ sp_bitmap_clone(Bitmap */*notnull*/ bitmap); + +/** + * Sets the state of all pixels in the [SPBitmap]. + * + * # 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 + */ +void sp_bitmap_fill(Bitmap */*notnull*/ bitmap, bool value); + +/** + * 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] + */ +void sp_bitmap_free(Bitmap */*notnull*/ bitmap); + +/** + * Gets the current value at the specified position in the [SPBitmap]. + * + * # Arguments + * + * - `bitmap`: instance to read from + * - `x` and `y`: position of the cell to read + * + * # 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 + */ +bool sp_bitmap_get(Bitmap */*notnull*/ bitmap, size_t x, size_t y); + +/** + * Gets the height in pixels of the [SPBitmap] 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] + */ +size_t sp_bitmap_height(Bitmap */*notnull*/ bitmap); + +/** + * Loads a [SPBitmap] 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`. + */ +Bitmap *sp_bitmap_load(size_t width, + size_t height, + SPByteSlice data); + +/** + * Creates a new [SPBitmap] 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. + * + * # Errors + * + * In the following cases, this function will return NULL: + * + * - when the width is not dividable by 8 + * + * # Safety + * + * The caller has to make sure that: + * + * - the returned instance is freed in some way, either by using a consuming function or + * by explicitly calling `sp_bitmap_free`. + */ +Bitmap *sp_bitmap_new(size_t width, + size_t height); + +/** + * Creates a new [SPBitmap] 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]. + */ +Bitmap */*notnull*/ sp_bitmap_new_screen_sized(void); + +/** + * Sets the value of the specified position in the [SPBitmap]. + * + * # Arguments + * + * - `bitmap`: instance to write to + * - `x` and `y`: position of the cell + * - `value`: the value to write to the cell + * + * returns: old value of the cell + * + * # Panics + * + * - when `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 + */ +void sp_bitmap_set(Bitmap */*notnull*/ bitmap, size_t x, size_t y, bool value); + +/** + * Gets an unsafe reference to the data of the [SPBitmap] 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 + */ +SPByteSlice sp_bitmap_unsafe_data_ref(Bitmap */*notnull*/ bitmap); + +/** + * Gets the width in pixels of the [SPBitmap] 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] + */ +size_t sp_bitmap_width(Bitmap */*notnull*/ bitmap); + +/** + * 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`. + */ +SPBitVec */*notnull*/ sp_bitvec_clone(SPBitVec */*notnull*/ bit_vec); + +/** + * Sets the value of all bits in the [SPBitVec]. + * + * # Arguments + * + * - `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 + */ +void sp_bitvec_fill(SPBitVec */*notnull*/ bit_vec, bool value); + +/** + * 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] + */ +void sp_bitvec_free(SPBitVec */*notnull*/ bit_vec); + +/** + * Gets the value of a bit from the [SPBitVec]. + * + * # Arguments + * + * - `bit_vec`: instance to read from + * - `index`: the bit index to read + * + * returns: value of the bit + * + * # 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 + */ +bool sp_bitvec_get(SPBitVec */*notnull*/ bit_vec, size_t index); + +/** + * Returns true if length is 0. + * + * # 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] + */ +bool sp_bitvec_is_empty(SPBitVec */*notnull*/ bit_vec); + +/** + * Gets the length of the [SPBitVec] in bits. + * + * # 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] + */ +size_t sp_bitvec_len(SPBitVec */*notnull*/ bit_vec); + +/** + * 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`. + */ +SPBitVec */*notnull*/ sp_bitvec_load(SPByteSlice data); + +/** + * Creates a new [SPBitVec] instance. + * + * # Arguments + * + * - `size`: size in bits. + * + * returns: [SPBitVec] with all bits set to false. Will never return NULL. + * + * # 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`. + */ +SPBitVec */*notnull*/ sp_bitvec_new(size_t size); + +/** + * Sets the value of a bit in the [SPBitVec]. + * + * # Arguments + * + * - `bit_vec`: instance to write to + * - `index`: the bit index to edit + * - `value`: the value to set the bit to + * + * # 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 + */ +void sp_bitvec_set(SPBitVec */*notnull*/ bit_vec, size_t index, bool value); + +/** + * Gets an unsafe reference to the data of the [SPBitVec] instance. + * + * # 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 + */ +SPByteSlice sp_bitvec_unsafe_data_ref(SPBitVec */*notnull*/ bit_vec); + +/** + * 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`. + */ +BrightnessGrid */*notnull*/ sp_brightness_grid_clone(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * Sets the value of all cells in the [SPBrightnessGrid]. + * + * # 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 + */ +void sp_brightness_grid_fill(BrightnessGrid */*notnull*/ brightness_grid, + uint8_t value); + +/** + * 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] + */ +void sp_brightness_grid_free(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * Gets the current value at the specified position. + * + * # Arguments + * + * - `brightness_grid`: instance to read from + * - `x` and `y`: position of the cell to read + * + * 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 + */ +uint8_t sp_brightness_grid_get(BrightnessGrid */*notnull*/ brightness_grid, + size_t x, + size_t y); + +/** + * Gets the height of the [SPBrightnessGrid] 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] + */ +size_t sp_brightness_grid_height(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * Loads a [SPBrightnessGrid] 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`. + */ +BrightnessGrid *sp_brightness_grid_load(size_t width, + size_t height, + SPByteSlice data); + +/** + * Creates a new [SPBrightnessGrid] with the specified dimensions. + * + * returns: [SPBrightnessGrid] 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_brightness_grid_free`. + */ +BrightnessGrid */*notnull*/ sp_brightness_grid_new(size_t width, + size_t height); + +/** + * Sets the value of the specified position in the [SPBrightnessGrid]. + * + * # Arguments + * + * - `brightness_grid`: instance to write to + * - `x` and `y`: position of the cell + * - `value`: the value to write to the cell + * + * returns: old value of the cell + * + * # Panics + * + * - when `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 + */ +void sp_brightness_grid_set(BrightnessGrid */*notnull*/ brightness_grid, + size_t x, + size_t y, + uint8_t value); + +/** + * Gets an unsafe reference to the data of the [SPBrightnessGrid] instance. + * + * # 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 + */ +SPByteSlice sp_brightness_grid_unsafe_data_ref(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * Gets the width of the [SPBrightnessGrid] 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] + */ +size_t sp_brightness_grid_width(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * 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`. + */ +CharGrid */*notnull*/ sp_char_grid_clone(CharGrid */*notnull*/ char_grid); + +/** + * Sets the value of all cells in the [SPCharGrid]. + * + * # 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 + */ +void sp_char_grid_fill(CharGrid */*notnull*/ char_grid, uint32_t value); + +/** + * 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] + */ +void sp_char_grid_free(CharGrid */*notnull*/ char_grid); + +/** + * Gets the current value at the specified position. + * + * # Arguments + * + * - `char_grid`: instance to read from + * - `x` and `y`: position of the cell to read + * + * # 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 + */ +uint32_t sp_char_grid_get(CharGrid */*notnull*/ char_grid, size_t x, size_t y); + +/** + * Gets the height of the [SPCharGrid] 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] + */ +size_t sp_char_grid_height(CharGrid */*notnull*/ char_grid); + +/** + * Loads a [SPCharGrid] 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`. + */ +CharGrid */*notnull*/ sp_char_grid_load(size_t width, + size_t height, + SPByteSlice data); + +/** + * Creates a new [SPCharGrid] with the specified dimensions. + * + * returns: [SPCharGrid] 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_char_grid_free`. + */ +CharGrid */*notnull*/ sp_char_grid_new(size_t width, + size_t height); + +/** + * Sets the value of the specified position in the [SPCharGrid]. + * + * # Arguments + * + * - `char_grid`: instance to write to + * - `x` and `y`: position of the cell + * - `value`: the value to write to the cell + * + * returns: old value of the cell + * + * # Panics + * + * - when `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] + */ +void sp_char_grid_set(CharGrid */*notnull*/ char_grid, + size_t x, + size_t y, + uint32_t value); + +/** + * Gets the width of the [SPCharGrid] 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] + */ +size_t sp_char_grid_width(CharGrid */*notnull*/ char_grid); + +/** + * Set pixel data starting at the pixel offset on screen. + * + * The screen will continuously overwrite more pixel data without regarding the offset, meaning + * once the starting row is full, overwriting will continue on column 0. + * + * The contained [SPBitVec] is always uncompressed. + * + * 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`. + */ +Command *sp_command_bitmap_linear(size_t offset, + SPBitVec */*notnull*/ bit_vec, + CompressionCode compression); + +/** + * Set pixel data according to an and-mask starting at the offset. + * + * The screen will continuously overwrite more pixel data without regarding the offset, meaning + * once the starting row is full, overwriting will continue on column 0. + * + * The contained [SPBitVec] is always uncompressed. + * + * 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`. + */ +Command *sp_command_bitmap_linear_and(size_t offset, + SPBitVec */*notnull*/ bit_vec, + CompressionCode compression); + +/** + * Set pixel data according to an or-mask starting at the offset. + * + * The screen will continuously overwrite more pixel data without regarding the offset, meaning + * once the starting row is full, overwriting will continue on column 0. + * + * The contained [SPBitVec] is always uncompressed. + * + * 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`. + */ +Command *sp_command_bitmap_linear_or(size_t offset, + SPBitVec */*notnull*/ bit_vec, + CompressionCode compression); + +/** + * Sets a window of pixels to the specified values. + * + * The passed [SPBitmap] 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`. + */ +Command *sp_command_bitmap_linear_win(size_t x, + size_t y, + Bitmap */*notnull*/ bitmap, + CompressionCode compression); + +/** + * Set pixel data according to a xor-mask starting at the offset. + * + * The screen will continuously overwrite more pixel data without regarding the offset, meaning + * once the starting row is full, overwriting will continue on column 0. + * + * The contained [SPBitVec] is always uncompressed. + * + * 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`. + */ +Command *sp_command_bitmap_linear_xor(size_t offset, + SPBitVec */*notnull*/ bit_vec, + CompressionCode compression); + +/** + * 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`. + */ +Command */*notnull*/ sp_command_brightness(uint8_t brightness); + +/** + * Set the brightness of individual tiles in a rectangular area of the display. + * + * The passed [SPBrightnessGrid] 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`. + */ +Command */*notnull*/ sp_command_char_brightness(size_t x, + size_t y, + BrightnessGrid */*notnull*/ grid); + +/** + * Set all pixels to the off state. + * + * Does not affect brightness. + * + * Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL. + * + * # 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`. + */ +Command */*notnull*/ sp_command_clear(void); + +/** + * Clones a [SPCommand] 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`. + */ +Command */*notnull*/ sp_command_clone(Command */*notnull*/ command); + +/** + * Show codepage 437 encoded text on the screen. + * + * The passed [SPCp437Grid] 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`. + */ +Command */*notnull*/ sp_command_cp437_data(size_t x, + size_t y, + Cp437Grid */*notnull*/ grid); + +/** + * 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`. + */ +Command */*notnull*/ sp_command_fade_out(void); + +/** + * Deallocates a [SPCommand]. + * + * # Examples + * + * ```C + * SPCommand 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] + */ +void sp_command_free(Command */*notnull*/ command); + +/** + * Kills the udp daemon on the display, which usually results in a restart. + * + * 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`. + */ +Command */*notnull*/ sp_command_hard_reset(void); + +/** + * 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]. + * + * 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`. + */ +Command *sp_command_try_from_packet(Packet */*notnull*/ packet); + +/** + * Show UTF-8 encoded text on the screen. + * + * The passed [SPCharGrid] 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`. + */ +Command */*notnull*/ sp_command_utf8_data(size_t x, + size_t y, + CharGrid */*notnull*/ grid); + +/** + * 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 + */ +void sp_connection_free(UdpConnection */*notnull*/ connection); + +/** + * Creates a new instance of [SPConnection]. + * + * returns: NULL if connection fails, or connected instance + * + * # Panics + * + * - 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`. + */ +UdpConnection *sp_connection_open(char */*notnull*/ host); + +/** + * Sends a [SPCommand] to the display using the [SPConnection]. + * + * The passed `command` gets consumed. + * + * returns: true in case of success + * + * # Panics + * + * - 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 + */ +bool sp_connection_send_command(UdpConnection */*notnull*/ connection, + Command */*notnull*/ command); + +/** + * Sends a [SPPacket] to the display using the [SPConnection]. + * + * 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 + */ +bool sp_connection_send_packet(UdpConnection */*notnull*/ connection, + Packet */*notnull*/ packet); + +/** + * 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`. + */ +Cp437Grid */*notnull*/ sp_cp437_grid_clone(Cp437Grid */*notnull*/ cp437_grid); + +/** + * Sets the value of all cells in the [SPCp437Grid]. + * + * # 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 + */ +void sp_cp437_grid_fill(Cp437Grid */*notnull*/ cp437_grid, uint8_t value); + +/** + * 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] + */ +void sp_cp437_grid_free(Cp437Grid */*notnull*/ cp437_grid); + +/** + * Gets the current value at the specified position. + * + * # Arguments + * + * - `cp437_grid`: instance to read from + * - `x` and `y`: position of the cell to read + * + * # 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 + */ +uint8_t sp_cp437_grid_get(Cp437Grid */*notnull*/ cp437_grid, + size_t x, + size_t y); + +/** + * Gets the height of the [SPCp437Grid] 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] + */ +size_t sp_cp437_grid_height(Cp437Grid */*notnull*/ cp437_grid); + +/** + * 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`. + */ +Cp437Grid *sp_cp437_grid_load(size_t width, + size_t height, + SPByteSlice data); + +/** + * Creates a new [SPCp437Grid] 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`. + */ +Cp437Grid */*notnull*/ sp_cp437_grid_new(size_t width, + size_t height); + +/** + * Sets the value of the specified position in the [SPCp437Grid]. + * + * # Arguments + * + * - `cp437_grid`: instance to write to + * - `x` and `y`: position of the cell + * - `value`: the value to write to the cell + * + * returns: old value of the cell + * + * # Panics + * + * - when `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] + */ +void sp_cp437_grid_set(Cp437Grid */*notnull*/ cp437_grid, + size_t x, + size_t y, + uint8_t value); + +/** + * Gets an unsafe reference to the data of the [SPCp437Grid] 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 + */ +SPByteSlice sp_cp437_grid_unsafe_data_ref(Cp437Grid */*notnull*/ cp437_grid); + +/** + * Gets the width of the [SPCp437Grid] 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] + */ +size_t sp_cp437_grid_width(Cp437Grid */*notnull*/ cp437_grid); + +/** + * 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`. + */ +Packet */*notnull*/ sp_packet_clone(Packet */*notnull*/ packet); + +/** + * 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 + */ +void sp_packet_free(Packet */*notnull*/ packet); + +/** + * Turns a [SPCommand] into a [SPPacket]. + * The [SPCommand] 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`. + */ +Packet *sp_packet_from_command(Command */*notnull*/ command); + +/** + * 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 + * + * 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]. + */ +Packet */*notnull*/ sp_packet_from_parts(Header header, + const SPByteSlice *payload); + +Header */*notnull*/ sp_packet_get_header(Packet */*notnull*/ packet); + +SPByteSlice sp_packet_get_payload(Packet */*notnull*/ packet); + +void sp_packet_set_payload(Packet */*notnull*/ packet, SPByteSlice data); + +/** + * Tries to load a [SPPacket] 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`. + */ +Packet *sp_packet_try_load(SPByteSlice data); + +void sp_packet_write_to(Packet */*notnull*/ packet, SPByteSlice buffer); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/examples/lang_c/src/lib.rs b/examples/lang_c/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/lang_c/src/lib.rs @@ -0,0 +1 @@ + diff --git a/example/main.c b/examples/lang_c/src/main.c similarity index 86% rename from example/main.c rename to examples/lang_c/src/main.c index 4e63dec..80a1756 100644 --- a/example/main.c +++ b/examples/lang_c/src/main.c @@ -12,7 +12,7 @@ int main(void) { sp_bitmap_fill(pixels, true); - TypedCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, COMPRESSION_CODE_UNCOMPRESSED); + Command *command = sp_command_bitmap_linear_win(0, 0, pixels, COMPRESSION_CODE_UNCOMPRESSED); if (command == NULL) return 1; diff --git a/generate-binding.sh b/generate-binding.sh deleted file mode 100755 index 45301bd..0000000 --- a/generate-binding.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/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 diff --git a/include/servicepoint.h b/include/servicepoint.h deleted file mode 100644 index dbce14d..0000000 --- a/include/servicepoint.h +++ /dev/null @@ -1,1174 +0,0 @@ -/* Generated with cbindgen:0.28.0 */ - -/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ - -#include -#include -#include -#include -#include - -/** - * pixel count on whole screen - */ -#define PIXEL_COUNT (PIXEL_WIDTH * PIXEL_HEIGHT) - -/** - * Display height in pixels - * - * # Examples - * - * ```rust - * # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; - * let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); - * ``` - */ -#define PIXEL_HEIGHT (TILE_HEIGHT * TILE_SIZE) - -/** - * Display width in pixels - * - * # Examples - * - * ```rust - * # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; - * let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); - * ``` - */ -#define PIXEL_WIDTH (TILE_WIDTH * TILE_SIZE) - -/** - * Display tile count in the y-direction - * - * # Examples - * - * ```rust - * # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; - * let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); - * ``` - */ -#define TILE_HEIGHT 20 - -/** - * size of a single tile in one dimension - */ -#define TILE_SIZE 8 - -/** - * Display tile count in the x-direction - * - * # Examples - * - * ```rust - * # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; - * let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); - * ``` - */ -#define TILE_WIDTH 56 - -/** - * Specifies the kind of compression to use. Availability depends on features. - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * // create command without payload compression - * # let pixels = Bitmap::max_sized(); - * _ = BitmapCommand { - * origin: Origin::ZERO, - * bitmap: pixels, - * compression: CompressionCode::Uncompressed - * }; - * - * // create command with payload compressed with lzma and appropriate header flags - * # let pixels = Bitmap::max_sized(); - * _ = BitmapCommand { - * origin: Origin::ZERO, - * bitmap: pixels, - * compression: CompressionCode::Lzma - * }; - * ``` - */ -enum CompressionCode -#ifdef __cplusplus - : uint16_t -#endif // __cplusplus - { - /** - * no compression - */ - COMPRESSION_CODE_UNCOMPRESSED = 0, - /** - * compress using flate2 with zlib header - */ - COMPRESSION_CODE_ZLIB = 26490, - /** - * compress using bzip2 - */ - COMPRESSION_CODE_BZIP2 = 25210, - /** - * compress using lzma - */ - COMPRESSION_CODE_LZMA = 27770, - /** - * compress using Zstandard - */ - COMPRESSION_CODE_ZSTD = 31347, -}; -#ifndef __cplusplus -typedef uint16_t CompressionCode; -#endif // __cplusplus - -/** - * A fixed-size 2D grid of booleans. - * - * The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels. - * This means that no conversion is necessary for sending the data to the display. - * The downside is that the width has to be a multiple of 8. - * - * # Examples - * - * ```rust - * use servicepoint::Bitmap; - * let mut bitmap = Bitmap::new(8, 2); - * - * ``` - */ -typedef struct Bitmap Bitmap; - -/** - * The raw packet. - * - * Contents should probably only be used directly to use features not exposed by the library. - * - * You may want to use [`crate::Command`] or [`crate::TypedCommand`] instead. - */ -typedef struct Packet Packet; - -/** - * A vector of bits - * - * # Examples - * ```C - * SPBitVec vec = sp_bitvec_new(8); - * sp_bitvec_set(vec, 5, true); - * sp_bitvec_free(vec); - * ``` - */ -typedef struct SPBitVec SPBitVec; - -/** - * This enum contains all commands provided by the library. - * This is useful in case you want one data type for all kinds of commands without using `dyn`. - * - * Please look at the contained structs for documentation per command. - */ -typedef struct TypedCommand TypedCommand; - -/** - * A connection using the UDP protocol. - * - * Use this when sending commands directly to the display. - * - * Requires the feature "`protocol_udp`" which is enabled by default. - */ -typedef struct UdpConnection UdpConnection; - -/** - * A 2D grid of values. - * - * The memory layout is the one the display expects in [`crate::Command`]s. - * - * This structure can be used with any type that implements the [Value] trait. - * You can also use the concrete type aliases provided in this crate, e.g. [`crate::CharGrid`] and [`crate::ByteGrid`]. - */ -typedef struct ValueGrid_Brightness ValueGrid_Brightness; - -/** - * A 2D grid of values. - * - * The memory layout is the one the display expects in [`crate::Command`]s. - * - * This structure can be used with any type that implements the [Value] trait. - * You can also use the concrete type aliases provided in this crate, e.g. [`crate::CharGrid`] and [`crate::ByteGrid`]. - */ -typedef struct ValueGrid_char ValueGrid_char; - -/** - * A 2D grid of values. - * - * The memory layout is the one the display expects in [`crate::Command`]s. - * - * This structure can be used with any type that implements the [Value] trait. - * You can also use the concrete type aliases provided in this crate, e.g. [`crate::CharGrid`] and [`crate::ByteGrid`]. - */ -typedef struct ValueGrid_u8 ValueGrid_u8; - -/** - * Represents a span of memory (`&mut [u8]` ) as a struct. - * - * # Safety - * - * The caller has to make sure that: - * - * - 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. - */ -typedef struct { - /** - * The start address of the memory. - */ - uint8_t */*notnull*/ start; - /** - * The amount of memory in bytes. - */ - size_t length; -} ByteSlice; - -/** - * A grid containing brightness values. - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * let mut grid = BrightnessGrid::new(2,2); - * grid.set(0, 0, Brightness::MIN); - * grid.set(1, 1, Brightness::MIN); - * - * # let connection = FakeConnection; - * connection.send(BrightnessGridCommand { - * origin: Origin::new(3, 7), - * grid - * }).unwrap() - * ``` - */ -typedef ValueGrid_Brightness BrightnessGrid; - -/** - * A display brightness value, checked for correct value range - * - * # Examples - * - * ``` - * # use servicepoint::*; - * let b = Brightness::MAX; - * let val: u8 = b.into(); - * - * let b = Brightness::try_from(7).unwrap(); - * # let connection = FakeConnection; - * let result = connection.send(GlobalBrightnessCommand::from(b)); - * ``` - */ -typedef uint8_t Brightness; -/** - * highest possible brightness value, 11 - */ -#define Brightness_MAX 11 -/** - * lowest possible brightness value, 0 - */ -#define Brightness_MIN 0 - -/** - * A grid containing UTF-8 characters. - * - * To send a `CharGrid` to the display, use a [`crate::CharGridCommand`]. - * - * Also see [`ValueGrid`] for the non-specialized operations and examples. - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * let grid = CharGrid::from("You can\nload multiline\nstrings directly"); - * assert_eq!(grid.get_row_str(1), Some("load multiline\0\0".to_string())); - * - * # let connection = FakeConnection; - * let command = CharGridCommand { origin: Origin::ZERO, grid }; - * connection.send(command).unwrap() - * ``` - */ -typedef ValueGrid_char CharGrid; - -/** - * A grid containing codepage 437 characters. - * - * The encoding is currently not enforced. - */ -typedef ValueGrid_u8 Cp437Grid; - -/** - * A raw header. - * - * The header specifies the kind of command, the size of the payload and where to display the - * payload, where applicable. - * - * Because the meaning of most fields depend on the command, there are no speaking names for them. - * - * The contained values are in platform endian-ness and may need to be converted before sending. - */ -typedef struct { - /** - * The first two bytes specify which command this packet represents. - */ - uint16_t command_code; - /** - * First command-specific value - */ - uint16_t a; - /** - * Second command-specific value - */ - uint16_t b; - /** - * Third command-specific value - */ - uint16_t c; - /** - * Fourth command-specific value - */ - uint16_t d; -} Header; - - - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -/** - * Clones a [Bitmap]. - */ -Bitmap */*notnull*/ sp_bitmap_clone(Bitmap */*notnull*/ bitmap); - -/** - * Sets the state of all pixels in the [Bitmap]. - * - * # Arguments - * - * - `bitmap`: instance to write to - * - `value`: the value to set all pixels to - */ -void sp_bitmap_fill(Bitmap */*notnull*/ bitmap, bool value); - -/** - * Deallocates a [Bitmap]. - */ -void sp_bitmap_free(Bitmap */*notnull*/ bitmap); - -/** - * Gets the current value at the specified position in the [Bitmap]. - * - * # Arguments - * - * - `bitmap`: instance to read from - * - `x` and `y`: position of the cell to read - * - * # Panics - * - * - when accessing `x` or `y` out of bounds - */ -bool sp_bitmap_get(Bitmap */*notnull*/ bitmap, size_t x, size_t y); - -/** - * Gets the height in pixels of the [Bitmap] instance. - * - * # Arguments - * - * - `bitmap`: instance to read from - */ -size_t sp_bitmap_height(Bitmap */*notnull*/ bitmap); - -/** - * 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: [Bitmap] that contains a copy of the provided data, or NULL in case of an error. - */ -Bitmap *sp_bitmap_load(size_t width, - size_t height, - ByteSlice data); - -/** - * Creates a new [Bitmap] with the specified dimensions. - * - * # Arguments - * - * - `width`: size in pixels in x-direction - * - `height`: size in pixels in y-direction - * - * returns: [Bitmap] initialized to all pixels off, or NULL in case of an error. - * - * # Errors - * - * In the following cases, this function will return NULL: - * - * - when the width is not dividable by 8 - * - * # 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); - * ``` - */ -Bitmap *sp_bitmap_new(size_t width, size_t height); - -/** - * Creates a new [Bitmap] with a size matching the screen. - * - * returns: [Bitmap] initialized to all pixels off. - */ -Bitmap */*notnull*/ sp_bitmap_new_screen_sized(void); - -/** - * Sets the value of the specified position in the [Bitmap]. - * - * # Arguments - * - * - `bitmap`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * # Panics - * - * - when accessing `x` or `y` out of bounds - */ -void sp_bitmap_set(Bitmap */*notnull*/ bitmap, size_t x, size_t y, bool value); - -/** - * Gets an unsafe reference to the data of the [Bitmap] instance. - * - * The returned memory is valid for the lifetime of the bitmap. - */ -ByteSlice sp_bitmap_unsafe_data_ref(Bitmap */*notnull*/ bitmap); - -/** - * Gets the width 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 [Bitmap] - */ -size_t sp_bitmap_width(Bitmap */*notnull*/ bitmap); - -/** - * Clones a [SPBitVec]. - */ -SPBitVec */*notnull*/ sp_bitvec_clone(SPBitVec */*notnull*/ bit_vec); - -/** - * Sets the value of all bits in the [SPBitVec]. - * - * # Arguments - * - * - `bit_vec`: instance to write to - * - `value`: the value to set all bits to - */ -void sp_bitvec_fill(SPBitVec */*notnull*/ bit_vec, bool value); - -/** - * Deallocates a [SPBitVec]. - */ -void sp_bitvec_free(SPBitVec */*notnull*/ bit_vec); - -/** - * Gets the value of a bit from the [SPBitVec]. - * - * # Arguments - * - * - `bit_vec`: instance to read from - * - `index`: the bit index to read - * - * returns: value of the bit - * - * # Panics - * - * - when accessing `index` out of bounds - */ -bool sp_bitvec_get(SPBitVec */*notnull*/ bit_vec, size_t index); - -/** - * Returns true if length is 0. - * - * # Arguments - * - * - `bit_vec`: instance to write to - */ -bool sp_bitvec_is_empty(SPBitVec */*notnull*/ bit_vec); - -/** - * Gets the length of the [SPBitVec] in bits. - * - * # Arguments - * - * - `bit_vec`: instance to write to - */ -size_t sp_bitvec_len(SPBitVec */*notnull*/ bit_vec); - -/** - * Interpret the data as a series of bits and load then into a new [SPBitVec] instance. - * - * returns: [SPBitVec] instance containing data. - */ -SPBitVec */*notnull*/ sp_bitvec_load(ByteSlice data); - -/** - * Creates a new [SPBitVec] instance. - * - * # Arguments - * - * - `size`: size in bits. - * - * returns: [SPBitVec] with all bits set to false. - * - * # Panics - * - * - when `size` is not divisible by 8. - */ -SPBitVec */*notnull*/ sp_bitvec_new(size_t size); - -/** - * Sets the value of a bit in the [SPBitVec]. - * - * # Arguments - * - * - `bit_vec`: instance to write to - * - `index`: the bit index to edit - * - `value`: the value to set the bit to - * - * # Panics - * - * - when accessing `index` out of bounds - */ -void sp_bitvec_set(SPBitVec */*notnull*/ bit_vec, size_t index, bool value); - -/** - * 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 - */ -ByteSlice sp_bitvec_unsafe_data_ref(SPBitVec */*notnull*/ bit_vec); - -/** - * Clones a [BrightnessGrid]. - */ -BrightnessGrid */*notnull*/ sp_brightness_grid_clone(BrightnessGrid */*notnull*/ brightness_grid); - -/** - * Sets the value of all cells in the [BrightnessGrid]. - * - * # Arguments - * - * - `brightness_grid`: instance to write to - * - `value`: the value to set all cells to - */ -void sp_brightness_grid_fill(BrightnessGrid */*notnull*/ brightness_grid, - Brightness value); - -/** - * Deallocates a [BrightnessGrid]. - */ -void sp_brightness_grid_free(BrightnessGrid */*notnull*/ brightness_grid); - -/** - * Gets the current value at the specified position. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - `x` and `y`: position of the cell to read - * - * returns: value at position - * - * # Panics - * - When accessing `x` or `y` out of bounds. - */ -Brightness sp_brightness_grid_get(BrightnessGrid */*notnull*/ brightness_grid, - size_t x, - size_t y); - -/** - * Gets the height of the [BrightnessGrid] instance. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - * returns: height - */ -size_t sp_brightness_grid_height(BrightnessGrid */*notnull*/ brightness_grid); - -/** - * Loads a [BrightnessGrid] with the specified dimensions from the provided data. - * - * returns: new [BrightnessGrid] instance, or NULL in case of an error. - */ -BrightnessGrid *sp_brightness_grid_load(size_t width, - size_t height, - ByteSlice data); - -/** - * Creates a new [BrightnessGrid] with the specified dimensions. - * - * returns: [BrightnessGrid] initialized to 0. - * - * # Examples - * ```C - * UdpConnection connection = sp_connection_open("127.0.0.1:2342"); - * if (connection == NULL) - * return 1; - * - * BrightnessGrid grid = sp_brightness_grid_new(2, 2); - * sp_brightness_grid_set(grid, 0, 0, 0); - * sp_brightness_grid_set(grid, 1, 1, 10); - * - * TypedCommand command = sp_command_char_brightness(grid); - * sp_connection_free(connection); - * ``` - */ -BrightnessGrid */*notnull*/ sp_brightness_grid_new(size_t width, size_t height); - -/** - * Sets the value of the specified position in the [BrightnessGrid]. - * - * # Arguments - * - * - `brightness_grid`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * returns: old value of the cell - * - * # Panics - * - * - When accessing `x` or `y` out of bounds. - */ -void sp_brightness_grid_set(BrightnessGrid */*notnull*/ brightness_grid, - size_t x, - size_t y, - Brightness value); - -/** - * 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`. - */ -ByteSlice sp_brightness_grid_unsafe_data_ref(BrightnessGrid */*notnull*/ brightness_grid); - -/** - * Gets the width of the [BrightnessGrid] instance. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - * returns: width - */ -size_t sp_brightness_grid_width(BrightnessGrid */*notnull*/ brightness_grid); - -/** - * Clones a [CharGrid]. - */ -CharGrid */*notnull*/ sp_char_grid_clone(CharGrid */*notnull*/ char_grid); - -/** - * Sets the value of all cells in the [CharGrid]. - * - * # Arguments - * - * - `char_grid`: instance to write to - * - `value`: the value to set all cells to - */ -void sp_char_grid_fill(CharGrid */*notnull*/ char_grid, uint32_t value); - -/** - * Deallocates a [CharGrid]. - */ -void sp_char_grid_free(CharGrid */*notnull*/ char_grid); - -/** - * Returns the current value at the specified position. - * - * # Arguments - * - * - `char_grid`: instance to read from - * - `x` and `y`: position of the cell to read - * - * # Panics - * - * - when accessing `x` or `y` out of bounds - */ -uint32_t sp_char_grid_get(CharGrid */*notnull*/ char_grid, size_t x, size_t y); - -/** - * Gets the height of the [CharGrid] instance. - * - * # Arguments - * - * - `char_grid`: instance to read from - */ -size_t sp_char_grid_height(CharGrid */*notnull*/ char_grid); - -/** - * Loads a [CharGrid] with the specified dimensions from the provided data. - * - * returns: new CharGrid or NULL in case of an error - */ -CharGrid *sp_char_grid_load(size_t width, size_t height, ByteSlice data); - -/** - * Creates a new [CharGrid] with the specified dimensions. - * - * returns: [CharGrid] initialized to 0. - * - * # 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); - * ``` - */ -CharGrid */*notnull*/ sp_char_grid_new(size_t width, size_t height); - -/** - * Sets the value of the specified position in the [CharGrid]. - * - * # Arguments - * - * - `char_grid`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * returns: old value of the cell - * - * # Panics - * - * - when accessing `x` or `y` out of bounds - */ -void sp_char_grid_set(CharGrid */*notnull*/ char_grid, - size_t x, - size_t y, - uint32_t value); - -/** - * Gets the width of the [CharGrid] instance. - * - * # Arguments - * - * - `char_grid`: instance to read from - */ -size_t sp_char_grid_width(CharGrid */*notnull*/ char_grid); - -/** - * Set pixel data starting at the pixel offset on screen. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinear] instance. - */ -TypedCommand *sp_command_bitmap_linear(size_t offset, - SPBitVec */*notnull*/ bit_vec, - CompressionCode compression); - -/** - * Set pixel data according to an and-mask starting at the offset. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. - */ -TypedCommand *sp_command_bitmap_linear_and(size_t offset, - SPBitVec */*notnull*/ bit_vec, - CompressionCode compression); - -/** - * Set pixel data according to an or-mask starting at the offset. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearOr] instance. - */ -TypedCommand *sp_command_bitmap_linear_or(size_t offset, - SPBitVec */*notnull*/ bit_vec, - CompressionCode compression); - -/** - * Sets a window of pixels to the specified values. - * - * The passed [Bitmap] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearWin] instance. - */ -TypedCommand *sp_command_bitmap_linear_win(size_t x, - size_t y, - Bitmap */*notnull*/ bitmap, - CompressionCode compression); - -/** - * Set pixel data according to a xor-mask starting at the offset. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearXor] instance. - */ -TypedCommand *sp_command_bitmap_linear_xor(size_t offset, - SPBitVec */*notnull*/ bit_vec, - CompressionCode compression); - -/** - * Set the brightness of all tiles to the same value. - * - * Returns: a new [servicepoint::Command::Brightness] instance. - */ -TypedCommand */*notnull*/ sp_command_brightness(Brightness brightness); - -/** - * Set the brightness of individual tiles in a rectangular area of the display. - * - * The passed [BrightnessGrid] gets consumed. - * - * Returns: a new [servicepoint::Command::CharBrightness] instance. - */ -TypedCommand */*notnull*/ sp_command_char_brightness(size_t x, - size_t y, - BrightnessGrid */*notnull*/ grid); - -/** - * Set all pixels to the off state. - * - * Does not affect brightness. - * - * Returns: a new [servicepoint::Command::Clear] instance. - * - * # Examples - * - * ```C - * sp_connection_send_command(connection, sp_command_clear()); - * ``` - */ -TypedCommand */*notnull*/ sp_command_clear(void); - -/** - * Clones a [TypedCommand] instance. - * - * returns: new [TypedCommand] instance. - */ -TypedCommand */*notnull*/ sp_command_clone(TypedCommand */*notnull*/ command); - -/** - * Show codepage 437 encoded text on the screen. - * - * The passed [Cp437Grid] gets consumed. - * - * Returns: a new [servicepoint::Command::Cp437Data] instance. - */ -TypedCommand */*notnull*/ sp_command_cp437_data(size_t x, - size_t y, - Cp437Grid */*notnull*/ grid); - -/** - * A yet-to-be-tested command. - * - * Returns: a new [servicepoint::Command::FadeOut] instance. - */ -TypedCommand */*notnull*/ sp_command_fade_out(void); - -/** - * Deallocates a [TypedCommand]. - * - * # Examples - * - * ```C - * TypedCommand c = sp_command_clear(); - * sp_command_free(c); - * ``` - */ -void sp_command_free(TypedCommand */*notnull*/ command); - -/** - * Kills the udp daemon on the display, which usually results in a restart. - * - * Please do not send this in your normal program flow. - * - * Returns: a new [servicepoint::Command::HardReset] instance. - */ -TypedCommand */*notnull*/ sp_command_hard_reset(void); - -/** - * Tries to turn a [Packet] into a [TypedCommand]. - * - * The packet is deallocated in the process. - * - * Returns: pointer to new [TypedCommand] instance or NULL if parsing failed. - */ -TypedCommand *sp_command_try_from_packet(Packet */*notnull*/ packet); - -/** - * Show UTF-8 encoded text on the screen. - * - * The passed [CharGrid] gets consumed. - * - * Returns: a new [servicepoint::Command::Utf8Data] instance. - */ -TypedCommand */*notnull*/ sp_command_utf8_data(size_t x, - size_t y, - CharGrid */*notnull*/ grid); - -/** - * Closes and deallocates a [UdpConnection]. - */ -void sp_connection_free(UdpConnection */*notnull*/ connection); - -/** - * Creates a new instance of [UdpConnection]. - * - * returns: NULL if connection fails, or connected instance - * - * # Examples - * - * ```C - * CConnection connection = sp_connection_open("172.23.42.29:2342"); - * if (connection != NULL) - * sp_connection_send_command(connection, sp_command_clear()); - * ``` - */ -UdpConnection *sp_connection_open(char */*notnull*/ host); - -/** - * Sends a [TypedCommand] to the display using the [UdpConnection]. - * - * The passed `command` gets consumed. - * - * returns: true in case of success - * - * # Examples - * - * ```C - * sp_connection_send_command(connection, sp_command_clear()); - * sp_connection_send_command(connection, sp_command_brightness(5)); - * ``` - */ -bool sp_connection_send_command(UdpConnection */*notnull*/ connection, - TypedCommand */*notnull*/ command); - -/** - * Sends a [Packet] to the display using the [UdpConnection]. - * - * The passed `packet` gets consumed. - * - * returns: true in case of success - */ -bool sp_connection_send_packet(UdpConnection */*notnull*/ connection, - Packet */*notnull*/ packet); - -/** - * Clones a [Cp437Grid]. - */ -Cp437Grid */*notnull*/ sp_cp437_grid_clone(Cp437Grid */*notnull*/ cp437_grid); - -/** - * Sets the value of all cells in the [Cp437Grid]. - * - * # Arguments - * - * - `cp437_grid`: instance to write to - * - `value`: the value to set all cells to - */ -void sp_cp437_grid_fill(Cp437Grid */*notnull*/ cp437_grid, uint8_t value); - -/** - * Deallocates a [Cp437Grid]. - */ -void sp_cp437_grid_free(Cp437Grid */*notnull*/ cp437_grid); - -/** - * Gets the current value at the specified position. - * - * # Arguments - * - * - `cp437_grid`: instance to read from - * - `x` and `y`: position of the cell to read - * - * # Panics - * - * - when accessing `x` or `y` out of bounds - */ -uint8_t sp_cp437_grid_get(Cp437Grid */*notnull*/ cp437_grid, - size_t x, - size_t y); - -/** - * Gets the height of the [Cp437Grid] instance. - * - * # Arguments - * - * - `cp437_grid`: instance to read from - */ -size_t sp_cp437_grid_height(Cp437Grid */*notnull*/ cp437_grid); - -/** - * Loads a [Cp437Grid] with the specified dimensions from the provided data. - */ -Cp437Grid *sp_cp437_grid_load(size_t width, size_t height, ByteSlice data); - -/** - * Creates a new [Cp437Grid] with the specified dimensions. - * - * returns: [Cp437Grid] initialized to 0. - */ -Cp437Grid */*notnull*/ sp_cp437_grid_new(size_t width, size_t height); - -/** - * Sets the value of the specified position in the [Cp437Grid]. - * - * # Arguments - * - * - `cp437_grid`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * returns: old value of the cell - * - * # Panics - * - * - when accessing `x` or `y` out of bounds - */ -void sp_cp437_grid_set(Cp437Grid */*notnull*/ cp437_grid, - size_t x, - size_t y, - uint8_t value); - -/** - * Gets an unsafe reference to the data of the [Cp437Grid] instance. - * - * The returned memory is valid for the lifetime of the grid. - */ -ByteSlice sp_cp437_grid_unsafe_data_ref(Cp437Grid */*notnull*/ cp437_grid); - -/** - * Gets the width of the [Cp437Grid] instance. - * - * # Arguments - * - * - `cp437_grid`: instance to read from - */ -size_t sp_cp437_grid_width(Cp437Grid */*notnull*/ cp437_grid); - -/** - * Clones a [Packet]. - */ -Packet */*notnull*/ sp_packet_clone(Packet */*notnull*/ packet); - -/** - * Deallocates a [Packet]. - */ -void sp_packet_free(Packet */*notnull*/ packet); - -/** - * Turns a [TypedCommand] into a [Packet]. - * The [TypedCommand] gets consumed. - * - * Returns NULL in case of an error. - */ -Packet *sp_packet_from_command(TypedCommand */*notnull*/ command); - -/** - * Creates a raw [Packet] from parts. - * - * returns: new instance. Will never return null. - */ -Packet */*notnull*/ sp_packet_from_parts(Header header, - const ByteSlice *payload); - -/** - * 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. - */ -Header */*notnull*/ sp_packet_get_header(Packet */*notnull*/ packet); - -/** - * 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. - */ -ByteSlice sp_packet_get_payload(Packet */*notnull*/ packet); - -/** - * Serialize the packet into the provided buffer. - * - * # Panics - * - * - if the buffer is not big enough to hold header+payload. - */ -void sp_packet_serialize_to(Packet */*notnull*/ packet, ByteSlice buffer); - -/** - * Sets the payload of the provided packet to the provided data. - * - * This makes previous payload pointers invalid. - */ -void sp_packet_set_payload(Packet */*notnull*/ packet, ByteSlice data); - -/** - * 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 - */ -Packet *sp_packet_try_load(ByteSlice data); - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus diff --git a/src/bitmap.rs b/src/bitmap.rs index f9b6882..2e70efc 100644 --- a/src/bitmap.rs +++ b/src/bitmap.rs @@ -1,16 +1,31 @@ +//! 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::ByteSlice; +use crate::byte_slice::SPByteSlice; -/// Creates a new [Bitmap] with the specified dimensions. +/// Creates a new [SPBitmap] with the specified dimensions. /// /// # Arguments /// /// - `width`: size in pixels in x-direction /// - `height`: size in pixels in y-direction /// -/// returns: [Bitmap] initialized to all pixels off, or NULL in case of an error. +/// returns: [SPBitmap] initialized to all pixels off, or NULL in case of an error. /// /// # Errors /// @@ -18,14 +33,12 @@ use crate::byte_slice::ByteSlice; /// /// - when the width is not dividable by 8 /// -/// # Examples +/// # Safety /// -/// ```C -/// Cp437Grid grid = sp_bitmap_new(8, 3); -/// sp_bitmap_fill(grid, true); -/// sp_bitmap_set(grid, 0, 0, false); -/// sp_bitmap_free(grid); -/// ``` +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_bitmap_new( width: usize, @@ -38,28 +51,54 @@ pub unsafe extern "C" fn sp_bitmap_new( } } -/// Creates a new [Bitmap] with a size matching the screen. +/// Creates a new [SPBitmap] with a size matching the screen. /// -/// returns: [Bitmap] initialized to all pixels off. +/// 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]. #[no_mangle] pub unsafe extern "C" fn sp_bitmap_new_screen_sized() -> NonNull { let result = Box::new(Bitmap::max_sized()); NonNull::from(Box::leak(result)) } -/// Loads a [Bitmap] with the specified dimensions from the provided data. +/// Loads a [SPBitmap] with the specified dimensions from the provided data. /// /// # Arguments /// /// - `width`: size in pixels in x-direction /// - `height`: size in pixels in y-direction /// -/// returns: [Bitmap] that contains a copy of the provided data, or NULL in case of an error. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_bitmap_load( width: usize, height: usize, - data: ByteSlice, + data: SPByteSlice, ) -> *mut Bitmap { let data = unsafe { data.as_slice() }; if let Ok(bitmap) = Bitmap::load(width, height, data) { @@ -69,7 +108,22 @@ pub unsafe extern "C" fn sp_bitmap_load( } } -/// Clones a [Bitmap]. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_bitmap_clone( bitmap: NonNull, @@ -78,13 +132,25 @@ pub unsafe extern "C" fn sp_bitmap_clone( NonNull::from(Box::leak(result)) } -/// Deallocates a [Bitmap]. +/// 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] #[no_mangle] pub unsafe extern "C" fn sp_bitmap_free(bitmap: NonNull) { _ = unsafe { Box::from_raw(bitmap.as_ptr()) }; } -/// Gets the current value at the specified position in the [Bitmap]. +/// Gets the current value at the specified position in the [SPBitmap]. /// /// # Arguments /// @@ -93,7 +159,15 @@ pub unsafe extern "C" fn sp_bitmap_free(bitmap: NonNull) { /// /// # 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, @@ -103,7 +177,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 [Bitmap]. +/// Sets the value of the specified position in the [SPBitmap]. /// /// # Arguments /// @@ -111,9 +185,19 @@ 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, @@ -124,18 +208,29 @@ pub unsafe extern "C" fn sp_bitmap_set( unsafe { (*bitmap.as_ptr()).set(x, y, value) }; } -/// Sets the state of all pixels in the [Bitmap]. +/// Sets the state of all pixels in the [SPBitmap]. /// /// # 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, value: bool) { unsafe { (*bitmap.as_ptr()).fill(value) }; } -/// Gets the width in pixels of the [Bitmap] instance. +/// Gets the width in pixels of the [SPBitmap] instance. /// /// # Arguments /// @@ -149,28 +244,48 @@ pub unsafe extern "C" fn sp_bitmap_fill(bitmap: NonNull, value: bool) { /// /// The caller has to make sure that: /// -/// - `bitmap` points to a valid [Bitmap] +/// - `bitmap` points to a valid [SPBitmap] #[no_mangle] pub unsafe extern "C" fn sp_bitmap_width(bitmap: NonNull) -> usize { unsafe { bitmap.as_ref().width() } } -/// Gets the height in pixels of the [Bitmap] instance. +/// Gets the height in pixels of the [SPBitmap] 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) -> usize { unsafe { bitmap.as_ref().height() } } -/// Gets an unsafe reference to the data of the [Bitmap] instance. +/// Gets an unsafe reference to the data of the [SPBitmap] instance. /// -/// The returned memory is valid for the lifetime of the bitmap. +/// # 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 #[no_mangle] pub unsafe extern "C" fn sp_bitmap_unsafe_data_ref( mut bitmap: NonNull, -) -> ByteSlice { - unsafe { ByteSlice::from_slice(bitmap.as_mut().data_ref_mut()) } +) -> SPByteSlice { + unsafe { SPByteSlice::from_slice(bitmap.as_mut().data_ref_mut()) } } diff --git a/src/bitvec.rs b/src/bitvec.rs index f87423d..c32ce78 100644 --- a/src/bitvec.rs +++ b/src/bitvec.rs @@ -1,6 +1,9 @@ -use crate::ByteSlice; +//! C functions for interacting with [SPBitVec]s +//! +//! prefix `sp_bitvec_` + +use crate::SPByteSlice; use std::ptr::NonNull; -use servicepoint::BitVecU8Msb0; /// A vector of bits /// @@ -10,7 +13,7 @@ use servicepoint::BitVecU8Msb0; /// sp_bitvec_set(vec, 5, true); /// sp_bitvec_free(vec); /// ``` -pub struct SPBitVec(pub(crate) BitVecU8Msb0); +pub struct SPBitVec(pub(crate) servicepoint::BitVecU8Msb0); impl Clone for SPBitVec { fn clone(&self) -> Self { @@ -24,31 +27,67 @@ impl Clone for SPBitVec { /// /// - `size`: size in bits. /// -/// returns: [SPBitVec] with all bits set to false. +/// returns: [SPBitVec] with all bits set to false. Will never return NULL. /// /// # 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 { let result = - Box::new(SPBitVec(BitVecU8Msb0::repeat(false, size))); + Box::new(SPBitVec(servicepoint::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. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_bitvec_load( - data: ByteSlice, + data: SPByteSlice, ) -> NonNull { let data = unsafe { data.as_slice() }; - let result = Box::new(SPBitVec(BitVecU8Msb0::from_slice(data))); + let result = + Box::new(SPBitVec(servicepoint::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, @@ -58,6 +97,20 @@ 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) { _ = unsafe { Box::from_raw(bit_vec.as_ptr()) }; @@ -74,7 +127,15 @@ pub unsafe extern "C" fn sp_bitvec_free(bit_vec: NonNull) { /// /// # 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, @@ -93,7 +154,15 @@ 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, @@ -109,6 +178,17 @@ 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, @@ -122,6 +202,16 @@ 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) -> usize { unsafe { bit_vec.as_ref().0.len() } @@ -132,6 +222,16 @@ pub unsafe extern "C" fn sp_bitvec_len(bit_vec: NonNull) -> 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, @@ -141,14 +241,24 @@ 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, -) -> ByteSlice { - unsafe { ByteSlice::from_slice((*bit_vec.as_ptr()).0.as_raw_mut_slice()) } +) -> SPByteSlice { + unsafe { SPByteSlice::from_slice((*bit_vec.as_ptr()).0.as_raw_mut_slice()) } } diff --git a/src/brightness_grid.rs b/src/brightness_grid.rs index 5cd7a16..c6abbb4 100644 --- a/src/brightness_grid.rs +++ b/src/brightness_grid.rs @@ -1,25 +1,47 @@ -use crate::ByteSlice; -use servicepoint::{Brightness, BrightnessGrid, ByteGrid, DataRef, Grid}; +//! 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 std::mem::transmute; use std::ptr::NonNull; -/// Creates a new [BrightnessGrid] with the specified dimensions. +/// 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. /// -/// returns: [BrightnessGrid] initialized to 0. +/// returns: [SPBrightnessGrid] initialized to 0. Will never return NULL. /// -/// # Examples -/// ```C -/// UdpConnection connection = sp_connection_open("127.0.0.1:2342"); -/// if (connection == NULL) -/// return 1; +/// # Safety /// -/// 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 caller has to make sure that: /// -/// TypedCommand command = sp_command_char_brightness(grid); -/// sp_connection_free(connection); -/// ``` +/// - the returned instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_brightness_grid_free`. #[no_mangle] pub unsafe extern "C" fn sp_brightness_grid_new( width: usize, @@ -29,17 +51,31 @@ pub unsafe extern "C" fn sp_brightness_grid_new( NonNull::from(Box::leak(result)) } -/// Loads a [BrightnessGrid] with the specified dimensions from the provided data. +/// Loads a [SPBrightnessGrid] with the specified dimensions from the provided data. /// -/// returns: new [BrightnessGrid] instance, or NULL in case of an error. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_brightness_grid_load( width: usize, height: usize, - data: ByteSlice, + data: SPByteSlice, ) -> *mut BrightnessGrid { let data = unsafe { data.as_slice() }; - let grid = match ByteGrid::load(width, height, data) { + let grid = match servicepoint::ByteGrid::load(width, height, data) { None => return std::ptr::null_mut(), Some(grid) => grid, }; @@ -50,7 +86,26 @@ pub unsafe extern "C" fn sp_brightness_grid_load( } } -/// Clones a [BrightnessGrid]. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_brightness_grid_clone( brightness_grid: NonNull, @@ -59,7 +114,25 @@ pub unsafe extern "C" fn sp_brightness_grid_clone( NonNull::from(Box::leak(result)) } -/// Deallocates a [BrightnessGrid]. +/// 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] #[no_mangle] pub unsafe extern "C" fn sp_brightness_grid_free( brightness_grid: NonNull, @@ -77,17 +150,26 @@ 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, x: usize, y: usize, -) -> Brightness { - unsafe { brightness_grid.as_ref().get(x, y) } +) -> u8 { + unsafe { brightness_grid.as_ref().get(x, y) }.into() } -/// Sets the value of the specified position in the [BrightnessGrid]. +/// Sets the value of the specified position in the [SPBrightnessGrid]. /// /// # Arguments /// @@ -99,38 +181,73 @@ 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, x: usize, y: usize, - value: Brightness, + value: u8, ) { - unsafe { (*brightness_grid.as_ptr()).set(x, y, value) }; + let brightness = servicepoint::Brightness::try_from(value) + .expect("invalid brightness value"); + unsafe { (*brightness_grid.as_ptr()).set(x, y, brightness) }; } -/// Sets the value of all cells in the [BrightnessGrid]. +/// Sets the value of all cells in the [SPBrightnessGrid]. /// /// # 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, - value: Brightness, + value: u8, ) { - unsafe { (*brightness_grid.as_ptr()).fill(value) }; + let brightness = servicepoint::Brightness::try_from(value) + .expect("invalid brightness value"); + unsafe { (*brightness_grid.as_ptr()).fill(brightness) }; } -/// Gets the width of the [BrightnessGrid] instance. +/// Gets the width of the [SPBrightnessGrid] 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, @@ -138,13 +255,23 @@ pub unsafe extern "C" fn sp_brightness_grid_width( unsafe { brightness_grid.as_ref().width() } } -/// Gets the height of the [BrightnessGrid] instance. +/// Gets the height of the [SPBrightnessGrid] 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, @@ -152,22 +279,31 @@ pub unsafe extern "C" fn sp_brightness_grid_height( unsafe { brightness_grid.as_ref().height() } } -/// Gets an unsafe reference to the data of the [BrightnessGrid] instance. -/// -/// The returned memory is valid for the lifetime of the brightness grid. +/// Gets an unsafe reference to the data of the [SPBrightnessGrid] instance. /// /// # 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, -) -> ByteSlice { - //noinspection RsAssertEqual - const _: () = assert!(size_of::() == 1); - +) -> SPByteSlice { + assert_eq!(size_of::(), 1); let data = unsafe { (*brightness_grid.as_ptr()).data_ref_mut() }; - unsafe { ByteSlice::from_slice(transmute(data)) } + // this assumes more about the memory layout than rust guarantees. yikes! + unsafe { SPByteSlice::from_slice(transmute(data)) } } diff --git a/src/byte_slice.rs b/src/byte_slice.rs index 001a616..066a405 100644 --- a/src/byte_slice.rs +++ b/src/byte_slice.rs @@ -2,7 +2,9 @@ use std::ptr::NonNull; -/// Represents a span of memory (`&mut [u8]` ) as a struct. +/// 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. /// /// # Safety /// @@ -11,15 +13,17 @@ 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 ByteSlice { - /// The start address of the memory. +pub struct SPByteSlice { + /// The start address of the memory pub start: NonNull, - /// The amount of memory in bytes. + /// The amount of memory in bytes pub length: usize, } -impl ByteSlice { +impl SPByteSlice { pub(crate) unsafe fn as_slice(&self) -> &[u8] { unsafe { std::slice::from_raw_parts(self.start.as_ptr(), self.length) } } diff --git a/src/char_grid.rs b/src/char_grid.rs index a287d70..a845f4e 100644 --- a/src/char_grid.rs +++ b/src/char_grid.rs @@ -1,19 +1,37 @@ -use crate::ByteSlice; +//! 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 servicepoint::{CharGrid, Grid}; use std::ptr::NonNull; -/// Creates a new [CharGrid] with the specified dimensions. +/// Creates a new [SPCharGrid] with the specified dimensions. /// -/// returns: [CharGrid] initialized to 0. +/// returns: [SPCharGrid] initialized to 0. Will never return NULL. /// -/// # Examples +/// # Safety /// -/// ```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); -/// ``` +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_char_grid_new( width: usize, @@ -23,39 +41,82 @@ pub unsafe extern "C" fn sp_char_grid_new( NonNull::from(Box::leak(result)) } -/// Loads a [CharGrid] with the specified dimensions from the provided data. +/// Loads a [SPCharGrid] with the specified dimensions from the provided data. /// -/// returns: new CharGrid or NULL in case of an error +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_char_grid_load( width: usize, height: usize, - data: ByteSlice, -) -> *mut CharGrid { + data: SPByteSlice, +) -> NonNull { let data = unsafe { data.as_slice() }; - if let Ok(grid) = CharGrid::load_utf8(width, height, data.to_vec()) { - Box::leak(Box::new(grid)) - } else { - std::ptr::null_mut() - } + // TODO remove unwrap + let result = + Box::new(CharGrid::load_utf8(width, height, data.to_vec()).unwrap()); + NonNull::from(Box::leak(result)) } -/// Clones a [CharGrid]. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_char_grid_clone( char_grid: NonNull, ) -> NonNull { - let result = unsafe { char_grid.as_ref().clone() }; - NonNull::from(Box::leak(Box::new(result))) + let result = Box::new(unsafe { char_grid.as_ref().clone() }); + NonNull::from(Box::leak(result)) } -/// Deallocates a [CharGrid]. +/// 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] #[no_mangle] pub unsafe extern "C" fn sp_char_grid_free(char_grid: NonNull) { _ = unsafe { Box::from_raw(char_grid.as_ptr()) }; } -/// Returns the current value at the specified position. +/// Gets the current value at the specified position. /// /// # Arguments /// @@ -64,7 +125,15 @@ pub unsafe extern "C" fn sp_char_grid_free(char_grid: NonNull) { /// /// # 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, @@ -74,7 +143,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 [CharGrid]. +/// Sets the value of the specified position in the [SPCharGrid]. /// /// # Arguments /// @@ -86,7 +155,17 @@ 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, @@ -97,12 +176,23 @@ 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 [CharGrid]. +/// Sets the value of all cells in the [SPCharGrid]. /// /// # 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, @@ -111,11 +201,21 @@ 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 [CharGrid] instance. +/// Gets the width of the [SPCharGrid] 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, @@ -123,11 +223,21 @@ pub unsafe extern "C" fn sp_char_grid_width( unsafe { char_grid.as_ref().width() } } -/// Gets the height of the [CharGrid] instance. +/// Gets the height of the [SPCharGrid] 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, diff --git a/src/command.rs b/src/command.rs index b867eba..eccac1b 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,12 +1,48 @@ +//! C functions for interacting with [SPCommand]s +//! +//! prefix `sp_command_` + use crate::SPBitVec; -use servicepoint::{BinaryOperation, Bitmap, Brightness, BrightnessGrid, CharGrid, CompressionCode, Cp437Grid, GlobalBrightnessCommand, Packet, TypedCommand}; +use servicepoint::{ + BinaryOperation, Bitmap, BrightnessGrid, CharGrid, CompressionCode, + Cp437Grid, GlobalBrightnessCommand, Packet, TypedCommand, +}; use std::ptr::NonNull; -/// Tries to turn a [Packet] into a [TypedCommand]. +/// 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]. /// /// The packet is deallocated in the process. /// -/// Returns: pointer to new [TypedCommand] instance or NULL if parsing failed. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_try_from_packet( packet: NonNull, @@ -18,9 +54,22 @@ pub unsafe extern "C" fn sp_command_try_from_packet( } } -/// Clones a [TypedCommand] instance. +/// Clones a [SPCommand] instance. /// -/// returns: new [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`. #[no_mangle] pub unsafe extern "C" fn sp_command_clone( command: NonNull, @@ -33,13 +82,20 @@ pub unsafe extern "C" fn sp_command_clone( /// /// Does not affect brightness. /// -/// Returns: a new [servicepoint::Command::Clear] instance. +/// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL. /// /// # 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 { let result = Box::new(servicepoint::ClearCommand.into()); @@ -50,7 +106,14 @@ pub unsafe extern "C" fn sp_command_clear() -> NonNull { /// /// Please do not send this in your normal program flow. /// -/// Returns: a new [servicepoint::Command::HardReset] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull { let result = Box::new(servicepoint::HardResetCommand.into()); @@ -59,7 +122,14 @@ pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull { /// A yet-to-be-tested command. /// -/// Returns: a new [servicepoint::Command::FadeOut] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_fade_out() -> NonNull { let result = Box::new(servicepoint::FadeOutCommand.into()); @@ -68,20 +138,46 @@ pub unsafe extern "C" fn sp_command_fade_out() -> NonNull { /// Set the brightness of all tiles to the same value. /// -/// Returns: a new [servicepoint::Command::Brightness] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_brightness( - brightness: Brightness, + brightness: u8, ) -> NonNull { + 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 [BrightnessGrid] gets consumed. +/// The passed [SPBrightnessGrid] gets consumed. /// -/// Returns: a new [servicepoint::Command::CharBrightness] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_char_brightness( x: usize, @@ -108,7 +204,22 @@ pub unsafe extern "C" fn sp_command_char_brightness( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [servicepoint::Command::BitmapLinear] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear( offset: usize, @@ -134,7 +245,22 @@ pub unsafe extern "C" fn sp_command_bitmap_linear( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_and( offset: usize, @@ -160,7 +286,22 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_or( offset: usize, @@ -186,7 +327,22 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_xor( offset: usize, @@ -203,6 +359,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor( } } +#[inline] unsafe fn sp_command_bitmap_linear_internal( offset: usize, bit_vec: NonNull, @@ -226,9 +383,22 @@ unsafe fn sp_command_bitmap_linear_internal( /// Show codepage 437 encoded text on the screen. /// -/// The passed [Cp437Grid] gets consumed. +/// The passed [SPCp437Grid] gets consumed. /// -/// Returns: a new [servicepoint::Command::Cp437Data] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_cp437_data( x: usize, @@ -248,9 +418,22 @@ pub unsafe extern "C" fn sp_command_cp437_data( /// Show UTF-8 encoded text on the screen. /// -/// The passed [CharGrid] gets consumed. +/// The passed [SPCharGrid] gets consumed. /// -/// Returns: a new [servicepoint::Command::Utf8Data] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_utf8_data( x: usize, @@ -270,9 +453,24 @@ pub unsafe extern "C" fn sp_command_utf8_data( /// Sets a window of pixels to the specified values. /// -/// The passed [Bitmap] gets consumed. +/// The passed [SPBitmap] gets consumed. /// -/// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_command_bitmap_linear_win( x: usize, @@ -294,14 +492,26 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( Box::leak(Box::new(command)) } -/// Deallocates a [TypedCommand]. +/// Deallocates a [SPCommand]. /// /// # Examples /// /// ```C -/// TypedCommand c = sp_command_clear(); +/// SPCommand 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) { _ = unsafe { Box::from_raw(command.as_ptr()) }; diff --git a/src/connection.rs b/src/connection.rs index 5feb528..1705538 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,18 +1,35 @@ +//! 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 [UdpConnection]. +/// Creates a new instance of [SPConnection]. /// /// returns: NULL if connection fails, or connected instance /// -/// # Examples +/// # Panics /// -/// ```C -/// CConnection connection = sp_connection_open("172.23.42.29:2342"); -/// if (connection != NULL) -/// sp_connection_send_command(connection, sp_command_clear()); -/// ``` +/// - 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`. #[no_mangle] pub unsafe extern "C" fn sp_connection_open( host: NonNull, @@ -31,18 +48,18 @@ pub unsafe extern "C" fn sp_connection_open( //#[no_mangle] //pub unsafe extern "C" fn sp_connection_open_ipv4( // host: SocketAddrV4, -//) -> *mut UdpConnection { +//) -> *mut SPConnection { // let connection = match servicepoint::UdpConnection::open(host) { // Err(_) => return std::ptr::null_mut(), // Ok(value) => value, // }; // -// Box::into_raw(Box::new(UdpConnection(connection))) +// Box::into_raw(Box::new(SPConnection(connection))) //} // /// Creates a new instance of [SPUdpConnection] for testing that does not actually send anything. // /// -// /// returns: a new instance. +// /// returns: a new instance. Will never return NULL. // /// // /// # Safety // /// @@ -56,11 +73,24 @@ pub unsafe extern "C" fn sp_connection_open( // NonNull::from(Box::leak(result)) // } -/// Sends a [Packet] to the display using the [UdpConnection]. +/// Sends a [SPPacket] to the display using the [SPConnection]. /// /// 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, @@ -70,18 +100,24 @@ pub unsafe extern "C" fn sp_connection_send_packet( unsafe { connection.as_ref().send(*packet) }.is_ok() } -/// Sends a [TypedCommand] to the display using the [UdpConnection]. +/// Sends a [SPCommand] to the display using the [SPConnection]. /// /// The passed `command` gets consumed. /// /// returns: true in case of success /// -/// # Examples +/// # Panics /// -/// ```C -/// sp_connection_send_command(connection, sp_command_clear()); -/// sp_connection_send_command(connection, sp_command_brightness(5)); -/// ``` +/// - 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 #[no_mangle] pub unsafe extern "C" fn sp_connection_send_command( connection: NonNull, @@ -91,7 +127,18 @@ pub unsafe extern "C" fn sp_connection_send_command( unsafe { connection.as_ref().send(command) }.is_ok() } -/// Closes and deallocates a [UdpConnection]. +/// 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 #[no_mangle] pub unsafe extern "C" fn sp_connection_free( connection: NonNull, diff --git a/src/cp437_grid.rs b/src/cp437_grid.rs index 3904354..e635247 100644 --- a/src/cp437_grid.rs +++ b/src/cp437_grid.rs @@ -1,10 +1,35 @@ -use crate::ByteSlice; +//! 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 servicepoint::{Cp437Grid, DataRef, Grid}; use std::ptr::NonNull; -/// Creates a new [Cp437Grid] with the specified dimensions. +/// Creates a new [SPCp437Grid] with the specified dimensions. /// -/// returns: [Cp437Grid] initialized to 0. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_cp437_grid_new( width: usize, @@ -14,12 +39,28 @@ pub unsafe extern "C" fn sp_cp437_grid_new( NonNull::from(Box::leak(result)) } -/// Loads a [Cp437Grid] with the specified dimensions from the provided data. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_cp437_grid_load( width: usize, height: usize, - data: ByteSlice, + data: SPByteSlice, ) -> *mut Cp437Grid { let data = unsafe { data.as_slice() }; let grid = Cp437Grid::load(width, height, data); @@ -30,7 +71,22 @@ pub unsafe extern "C" fn sp_cp437_grid_load( } } -/// Clones a [Cp437Grid]. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_cp437_grid_clone( cp437_grid: NonNull, @@ -39,7 +95,21 @@ pub unsafe extern "C" fn sp_cp437_grid_clone( NonNull::from(Box::leak(result)) } -/// Deallocates a [Cp437Grid]. +/// 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] #[no_mangle] pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: NonNull) { _ = unsafe { Box::from_raw(cp437_grid.as_ptr()) }; @@ -54,7 +124,15 @@ pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: NonNull) { /// /// # 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, @@ -64,7 +142,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 [Cp437Grid]. +/// Sets the value of the specified position in the [SPCp437Grid]. /// /// # Arguments /// @@ -76,7 +154,17 @@ 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, @@ -87,12 +175,23 @@ 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 [Cp437Grid]. +/// Sets the value of all cells in the [SPCp437Grid]. /// /// # 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, @@ -101,11 +200,21 @@ pub unsafe extern "C" fn sp_cp437_grid_fill( unsafe { (*cp437_grid.as_ptr()).fill(value) }; } -/// Gets the width of the [Cp437Grid] instance. +/// Gets the width of the [SPCp437Grid] 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, @@ -113,11 +222,21 @@ pub unsafe extern "C" fn sp_cp437_grid_width( unsafe { cp437_grid.as_ref().width() } } -/// Gets the height of the [Cp437Grid] instance. +/// Gets the height of the [SPCp437Grid] 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, @@ -125,12 +244,24 @@ pub unsafe extern "C" fn sp_cp437_grid_height( unsafe { cp437_grid.as_ref().height() } } -/// Gets an unsafe reference to the data of the [Cp437Grid] instance. +/// Gets an unsafe reference to the data of the [SPCp437Grid] instance. /// -/// The returned memory is valid for the lifetime of the grid. +/// 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 #[no_mangle] pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( cp437_grid: NonNull, -) -> ByteSlice { - unsafe { ByteSlice::from_slice((*cp437_grid.as_ptr()).data_ref_mut()) } +) -> SPByteSlice { + unsafe { SPByteSlice::from_slice((*cp437_grid.as_ptr()).data_ref_mut()) } } diff --git a/src/lib.rs b/src/lib.rs index 4d4e386..831daea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,14 +9,14 @@ //! #include "servicepoint.h" //! //! int main(void) { -//! UdpConnection *connection = sp_connection_open("172.23.42.29:2342"); +//! SPConnection *connection = sp_connection_open("172.23.42.29:2342"); //! if (connection == NULL) //! return 1; //! -//! Bitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); +//! SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); //! sp_bitmap_fill(pixels, true); //! -//! TypedCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); +//! SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); //! while (sp_connection_send_command(connection, sp_command_clone(command))); //! //! sp_command_free(command); diff --git a/src/packet.rs b/src/packet.rs index 996c83d..f8235ab 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,11 +1,31 @@ -use crate::ByteSlice; +//! C functions for interacting with [SPPacket]s +//! +//! prefix `sp_packet_` +//! +//! +//! The raw packet + +use crate::SPByteSlice; use servicepoint::{Header, Packet, TypedCommand}; use std::ptr::NonNull; -/// Turns a [TypedCommand] into a [Packet]. -/// The [TypedCommand] gets consumed. +/// Turns a [SPCommand] into a [SPPacket]. +/// The [SPCommand] gets consumed. /// -/// Returns NULL in case of an error. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_packet_from_command( command: NonNull, @@ -18,11 +38,24 @@ pub unsafe extern "C" fn sp_packet_from_command( } } -/// Tries to load a [Packet] from the passed array with the specified length. +/// Tries to load a [SPPacket] 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: ByteSlice) -> *mut Packet { +pub unsafe extern "C" fn sp_packet_try_load(data: SPByteSlice) -> *mut Packet { let data = unsafe { data.as_slice() }; match servicepoint::Packet::try_from(data) { Err(_) => std::ptr::null_mut(), @@ -30,13 +63,34 @@ pub unsafe extern "C" fn sp_packet_try_load(data: ByteSlice) -> *mut Packet { } } -/// Creates a raw [Packet] from parts. +/// 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 /// /// 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 ByteSlice, + payload: *const SPByteSlice, ) -> NonNull { let payload = if payload.is_null() { vec![] @@ -49,9 +103,6 @@ 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, @@ -59,43 +110,47 @@ 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, -) -> ByteSlice { - unsafe { ByteSlice::from_slice(&mut *(*packet.as_ptr()).payload) } +) -> SPByteSlice { + unsafe { SPByteSlice::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, - data: ByteSlice, + data: SPByteSlice, ) { 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_serialize_to( +pub unsafe extern "C" fn sp_packet_write_to( packet: NonNull, - buffer: ByteSlice, + buffer: SPByteSlice, ) { unsafe { packet.as_ref().serialize_to(buffer.as_slice_mut()); } } -/// Clones a [Packet]. +/// 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`. #[no_mangle] pub unsafe extern "C" fn sp_packet_clone( packet: NonNull, @@ -104,7 +159,18 @@ pub unsafe extern "C" fn sp_packet_clone( NonNull::from(Box::leak(result)) } -/// Deallocates a [Packet]. +/// 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 #[no_mangle] pub unsafe extern "C" fn sp_packet_free(packet: NonNull) { _ = unsafe { Box::from_raw(packet.as_ptr()) }