diff --git a/Cargo.lock b/Cargo.lock index 78e2169..fcd4aef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -415,8 +415,7 @@ dependencies = [ [[package]] name = "servicepoint" version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" +source = "git+https://git.berlin.ccc.de/servicepoint/servicepoint.git?branch=next#d979d46d3e770222c5a7ca549e601b45eff48299" dependencies = [ "bitvec", "bzip2", diff --git a/Cargo.toml b/Cargo.toml index 0eba069..fcb4eb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,10 @@ crate-type = ["staticlib", "cdylib", "rlib"] cbindgen = "0.28.0" [dependencies.servicepoint] -package = "servicepoint" -version = "0.14.1" +# version = "0.14.1" default-features = false +git = "https://git.berlin.ccc.de/servicepoint/servicepoint.git" +branch = "next" [features] all_compressions = ["servicepoint/all_compressions"] diff --git a/cbindgen.toml b/cbindgen.toml index 7841755..9749726 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -22,10 +22,12 @@ usize_is_size_t = true # this is needed because otherwise the order in the C bindings is different on different machines sort_by = "Name" +include_guard = "SERVICEPOINT_BINDINGS_C" + [parse] parse_deps = true include = ["servicepoint", "std"] -extra_bindings = ["servicepoint"] +extra_bindings = ["servicepoint", "servicepoint_binding_c"] [parse.expand] features = ["full"] diff --git a/example/src/announce.c b/example/src/announce.c index d256cff..bfdc48f 100644 --- a/example/src/announce.c +++ b/example/src/announce.c @@ -1,16 +1,9 @@ -#include -#include "servicepoint.h" - +#include "helpers.h" int main(void) { - printf("test\n"); + sock_init(); - UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); - //UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); - if (connection == NULL) - return 1; - - sp_udp_send_header(connection, (Header) {.command_code = COMMAND_CODE_CLEAR}); + sp_udp_send_header(sock, (Header) {.command_code = COMMAND_CODE_CLEAR}); CharGrid *grid = sp_char_grid_new(5, 2); if (grid == NULL) @@ -30,8 +23,7 @@ int main(void) { Packet *packet = sp_char_grid_into_packet(grid, 0, 0); if (packet == NULL) return 1; - sp_udp_send_packet(connection, packet); + sp_udp_send_packet(sock, packet); - sp_udp_free(connection); return 0; } diff --git a/example/src/brightness_tester.c b/example/src/brightness_tester.c index 21d814d..51790cf 100644 --- a/example/src/brightness_tester.c +++ b/example/src/brightness_tester.c @@ -1,6 +1,5 @@ #include "servicepoint.h" - -static UdpSocket *connection = NULL; +#include "helpers.h" void enable_all_pixels(void) { Bitmap *all_on = sp_bitmap_new_max_sized(); @@ -9,26 +8,18 @@ void enable_all_pixels(void) { BitmapCommand *bitmapCommand = sp_cmd_bitmap_from_bitmap(all_on); Packet *packet = sp_cmd_bitmap_try_into_packet(bitmapCommand); if (packet != NULL) - sp_udp_send_packet(connection, packet); + sp_udp_send_packet(sock, packet); } void make_brightness_pattern(BrightnessGrid *grid) { ByteSlice slice = sp_brightness_grid_unsafe_data_ref(grid); for (size_t index = 0; index < slice.length; index++) { - slice.start[index] = (uint8_t) (index % ((size_t) Brightness_MAX)); + slice.start[index] = (uint8_t)(index % ((size_t) Brightness_MAX)); } } -void run_at_exit() { - sp_udp_free(connection); -} - int main(void) { - //UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); - connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); - if (connection == NULL) - return -1; - atexit(run_at_exit); + sock_init(); enable_all_pixels(); @@ -37,8 +28,8 @@ int main(void) { Packet *packet = sp_cmd_brightness_grid_into_packet(sp_cmd_brightness_grid_from_grid(grid)); if (packet == NULL) - return -2; + return -2; - sp_udp_send_packet(connection, packet); + sp_udp_send_packet(sock, packet); return 0; } diff --git a/example/src/helpers.h b/example/src/helpers.h new file mode 100644 index 0000000..904e86b --- /dev/null +++ b/example/src/helpers.h @@ -0,0 +1,45 @@ +#pragma once +#ifndef SERVICEPOINT_BINDING_C_MSLEEP_H +#define SERVICEPOINT_BINDING_C_MSLEEP_H + +#include +#include +#include +#include "servicepoint.h" + +static UdpSocket *sock = NULL; + +void sock_free() { + sp_udp_free(sock); +} + +void sock_init() { + //sock = sp_udp_open_ipv4(127, 0, 0, 1, 2342); + sock = sp_udp_open_ipv4(172, 23, 42, 29, 2342); + if (sock == NULL) + exit(-1); + atexit(sock_free); +} + +/// TODO: all of this for sleeping n ms? There should be a better way! +int msleep(long msec) { + int res; + + if (msec < 0) { + errno = EINVAL; + return -1; + } + + struct timespec ts = { + .tv_sec = msec / 1000, + .tv_nsec = (msec % 1000) * 1000000, + }; + + do { + res = nanosleep(&ts, &ts); + } while (res && errno == EINTR); + + return res; +} + +#endif //SERVICEPOINT_BINDING_C_MSLEEP_H diff --git a/example/src/moving_line.c b/example/src/moving_line.c new file mode 100644 index 0000000..688f249 --- /dev/null +++ b/example/src/moving_line.c @@ -0,0 +1,34 @@ +#include "servicepoint.h" +#include "helpers.h" + +int main() { + sock_init(); + + int result = 0; + Bitmap *bitmap = sp_bitmap_new_max_sized(); + for (int x = 0; x < sp_bitmap_width(bitmap); x++) { + sp_bitmap_fill(bitmap, false); + + for (int y = 0; y < sp_bitmap_height(bitmap); y++) { + sp_bitmap_set(bitmap, (y + x) % PIXEL_WIDTH, y, true); + } + + BitmapCommand *command = sp_cmd_bitmap_from_bitmap(sp_bitmap_clone(bitmap)); + Packet *packet = sp_cmd_bitmap_try_into_packet(command); + if (packet == NULL) { + result = -2; + goto exit; + } + + if (!sp_udp_send_packet(sock, packet)) { + result = -3; + goto exit; + } + + msleep(SP_FRAME_PACING_MS); + } + + exit: + sp_bitmap_free(bitmap); + return result; +} diff --git a/example/src/random_stuff.c b/example/src/random_stuff.c index 2ebd485..2d818e3 100644 --- a/example/src/random_stuff.c +++ b/example/src/random_stuff.c @@ -1,10 +1,9 @@ #include #include "servicepoint.h" +#include "helpers.h" int main(void) { - UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); - if (connection == NULL) - return 1; + sock_init(); Bitmap *pixels = sp_bitmap_new(PIXEL_WIDTH, PIXEL_HEIGHT); if (pixels == NULL) @@ -19,8 +18,6 @@ int main(void) { Header *header = sp_packet_get_header(packet); printf("[%d, %d, %d, %d, %d]\n", header->command_code, header->a, header->b, header->c, header->d); - sp_udp_send_packet(connection, packet); - - sp_udp_free(connection); + sp_udp_send_packet(sock, packet); return 0; } diff --git a/example/src/wiping_clear.c b/example/src/wiping_clear.c new file mode 100644 index 0000000..a3f7017 --- /dev/null +++ b/example/src/wiping_clear.c @@ -0,0 +1,34 @@ +#include "servicepoint.h" +#include "helpers.h" + +int main() { + sock_init(); + + Bitmap *enabled_pixels = sp_bitmap_new_max_sized(); + sp_bitmap_fill(enabled_pixels, true); + + int result = 0; + for (int x = 0; x < PIXEL_WIDTH; x++) { + for (int y = 0; y < PIXEL_HEIGHT; y++) { + sp_bitmap_set(enabled_pixels, x, y, false); + } + + BitVec *bitvec = sp_bitmap_into_bitvec(sp_bitmap_clone(enabled_pixels)); + BitVecCommand *command = sp_cmd_bitvec_new(bitvec, 0, BINARY_OPERATION_AND, COMPRESSION_CODE_LZMA); + Packet *packet = sp_cmd_bitvec_try_into_packet(command); + if (packet == NULL) { + result = -2; + goto exit; + } + if (!sp_udp_send_packet(sock, packet)) { + result = -3; + goto exit; + } + + msleep(SP_FRAME_PACING_MS); + } + + exit: + sp_bitmap_free(enabled_pixels); + return result; +} diff --git a/include/servicepoint.h b/include/servicepoint.h index d5b0199..1a4d023 100644 --- a/include/servicepoint.h +++ b/include/servicepoint.h @@ -1,3 +1,6 @@ +#ifndef SERVICEPOINT_BINDINGS_C +#define SERVICEPOINT_BINDINGS_C + /* Generated with cbindgen:0.28.0 */ /* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ @@ -446,12 +449,19 @@ typedef struct ValueGrid_u8 ValueGrid_u8; * - 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. + * - if `start` is NULL or `length` is 0, do not dereference `start`. + * + * # Examples + * + * ```c + * ByteSlice empty = {.start: NULL, .length = 0}; + * ``` */ typedef struct { /** * The start address of the memory. */ - uint8_t */*notnull*/ start; + uint8_t *start; /** * The amount of memory in bytes. */ @@ -606,6 +616,13 @@ typedef struct { +/** + * Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. + */ +#define SP_FRAME_PACING_MS 30 + + + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -1703,8 +1720,7 @@ void sp_packet_free(Packet */*notnull*/ packet); * * returns: new instance. Will never return null. */ -Packet */*notnull*/ sp_packet_from_parts(Header header, - const ByteSlice *payload); +Packet */*notnull*/ sp_packet_from_parts(Header header, ByteSlice payload); /** * Returns a pointer to the header field of the provided packet. @@ -1716,6 +1732,8 @@ Header */*notnull*/ sp_packet_get_header(Packet */*notnull*/ packet); /** * Returns a pointer to the current payload of the provided packet. * + * Returns an [ByteSlice::INVALID] instance in case the packet does not have any payload. + * * The returned memory can be changed and will be valid until a new payload is set. */ ByteSlice sp_packet_get_payload(Packet */*notnull*/ packet); @@ -1831,3 +1849,5 @@ bool sp_udp_send_packet(UdpSocket */*notnull*/ connection, #ifdef __cplusplus } // extern "C" #endif // __cplusplus + +#endif /* SERVICEPOINT_BINDINGS_C */ diff --git a/src/commands/bitvec_command.rs b/src/commands/bitvec_command.rs index 617e3af..f2f667c 100644 --- a/src/commands/bitvec_command.rs +++ b/src/commands/bitvec_command.rs @@ -103,7 +103,7 @@ pub unsafe extern "C" fn sp_cmd_bitvec_set_offset( pub unsafe extern "C" fn sp_cmd_bitvec_get_operation( command: NonNull, ) -> BinaryOperation { - unsafe { command.as_ref().operation.clone() } // TODO remove clone + unsafe { command.as_ref().operation } } /// Overwrites the [BinaryOperation] of the command. diff --git a/src/containers/byte_slice.rs b/src/containers/byte_slice.rs index de7d0df..0668318 100644 --- a/src/containers/byte_slice.rs +++ b/src/containers/byte_slice.rs @@ -1,7 +1,5 @@ //! FFI slice helper -use std::ptr::NonNull; - /// Represents a span of memory (`&mut [u8]` ) as a struct. /// /// # Safety @@ -11,28 +9,39 @@ 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. +/// - if `start` is NULL or `length` is 0, do not dereference `start`. +/// +/// # Examples +/// +/// ```c +/// ByteSlice empty = {.start: NULL, .length = 0}; +/// ``` #[repr(C)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct ByteSlice { /// The start address of the memory. - pub start: NonNull, + pub start: *mut u8, /// The amount of memory in bytes. pub length: usize, } impl ByteSlice { + pub(crate) const INVALID: ByteSlice = ByteSlice { + start: std::ptr::null_mut(), + length: 0, + }; + pub(crate) unsafe fn as_slice(&self) -> &[u8] { - unsafe { std::slice::from_raw_parts(self.start.as_ptr(), self.length) } + unsafe { std::slice::from_raw_parts(self.start, self.length) } } pub(crate) unsafe fn as_slice_mut(&mut self) -> &mut [u8] { - unsafe { - std::slice::from_raw_parts_mut(self.start.as_ptr(), self.length) - } + unsafe { std::slice::from_raw_parts_mut(self.start, self.length) } } pub(crate) unsafe fn from_slice(slice: &mut [u8]) -> Self { Self { - start: NonNull::new(slice.as_mut_ptr()).unwrap(), + start: slice.as_mut_ptr(), length: slice.len(), } } diff --git a/src/lib.rs b/src/lib.rs index 377b2a0..ea3f916 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,10 +35,8 @@ pub mod packet; /// Functions related to [UdpSocket]. pub mod udp; -use std::time::Duration; - /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. -pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); +pub const SP_FRAME_PACING_MS: u128 = 30; /// This is a type only used by cbindgen to have a type for pointers. pub struct UdpSocket; diff --git a/src/packet.rs b/src/packet.rs index e9593c9..2fdbefc 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -20,13 +20,12 @@ pub unsafe extern "C" fn sp_packet_try_load(data: ByteSlice) -> *mut Packet { #[no_mangle] pub unsafe extern "C" fn sp_packet_from_parts( header: Header, - payload: *const ByteSlice, + payload: ByteSlice, ) -> NonNull { - let payload = if payload.is_null() { - vec![] + let payload = if payload == ByteSlice::INVALID { + None } else { - let payload = unsafe { (*payload).as_slice() }; - Vec::from(payload) + Some(Vec::from(unsafe { payload.as_slice() })) }; heap_move_nonnull(Packet { header, payload }) @@ -44,12 +43,19 @@ pub unsafe extern "C" fn sp_packet_get_header( /// Returns a pointer to the current payload of the provided packet. /// +/// Returns an [ByteSlice::INVALID] instance in case the packet does not have any payload. +/// /// 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) } + unsafe { + match &mut (*packet.as_ptr()).payload { + None => ByteSlice::INVALID, + Some(payload) => ByteSlice::from_slice(payload), + } + } } /// Sets the payload of the provided packet to the provided data. @@ -60,7 +66,13 @@ pub unsafe extern "C" fn sp_packet_set_payload( packet: NonNull, data: ByteSlice, ) { - unsafe { (*packet.as_ptr()).payload = data.as_slice().to_vec() } + unsafe { + (*packet.as_ptr()).payload = if data == ByteSlice::INVALID { + None + } else { + Some(data.as_slice().to_vec()) + } + } } /// Serialize the packet into the provided buffer. diff --git a/src/udp.rs b/src/udp.rs index 7c2ab2f..fe9f876 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -136,7 +136,7 @@ pub unsafe extern "C" fn sp_udp_send_header( ) -> bool { let packet = Packet { header, - payload: vec![], + payload: None, }; unsafe { udp_connection.as_ref() } .send(&Vec::from(packet))