diff --git a/include/servicepoint.h b/include/servicepoint.h index 1e0182e..68d4c57 100644 --- a/include/servicepoint.h +++ b/include/servicepoint.h @@ -633,12 +633,6 @@ typedef struct Header { extern "C" { #endif // __cplusplus -/** - * Call this function at the beginning of main to enable rust logging controlled by the - * `RUST_LOG` environment variable. See [env_logger](https://docs.rs/env_logger/latest/env_logger/). - */ -void init_env_logger(void); - /** *Clones a [`Bitmap`] instance. * @@ -1655,7 +1649,7 @@ void sp_cmd_clear_free(struct ClearCommand */*notnull*/ instance); * * Does not affect brightness. * - * Returns: a new [ClearCommand] instance. + * Returns: a new [`ClearCommand`] instance. * * This function is part of the sp_cmd_clear module. */ @@ -1752,7 +1746,7 @@ void sp_cmd_fade_out_free(struct FadeOutCommand */*notnull*/ instance); /** * A yet-to-be-tested command. * - * Returns: a new [FadeOutCommand] instance. + * Returns: a new [`FadeOutCommand`] instance. * * This function is part of the sp_cmd_fade_out module. */ @@ -1762,6 +1756,8 @@ struct FadeOutCommand */*notnull*/ sp_cmd_fade_out_new(void); * Clones an [SPCommand] instance. * * returns: a new [SPCommand] instance. + * + * This function is part of the sp_cmd_generic module. */ struct Command sp_cmd_generic_clone(struct Command command); @@ -1776,6 +1772,8 @@ struct Command sp_cmd_generic_clone(struct Command command); * SPCommand c = sp_cmd_clear_into_generic(sp_cmd_clear_new()); * sp_command_free(c); * ``` + * + * This function is part of the sp_cmd_generic module. */ void sp_cmd_generic_free(struct Command command); @@ -1784,6 +1782,8 @@ void sp_cmd_generic_free(struct Command command); * The [SPCommand] gets consumed. * * Returns tag [CommandTag::Invalid] in case of an error. + * + * This function is part of the sp_cmd_generic module. */ struct Packet *sp_cmd_generic_into_packet(struct Command command); @@ -1793,6 +1793,8 @@ struct Packet *sp_cmd_generic_into_packet(struct Command command); * The packet is dropped in the process. * * Returns: pointer to new [SPCommand] instance or NULL if parsing failed. + * + * This function is part of the sp_cmd_generic module. */ struct Command sp_cmd_generic_try_from_packet(struct Packet */*notnull*/ packet); @@ -1808,7 +1810,7 @@ void sp_cmd_hard_reset_free(struct HardResetCommand */*notnull*/ instance); * * Please do not send this in your normal program flow. * - * Returns: a new [HardResetCommand] instance. + * Returns: a new [`HardResetCommand`] instance. * * This function is part of the sp_cmd_hard_reset module. */ @@ -1942,6 +1944,14 @@ void sp_cp437_grid_set(Cp437Grid */*notnull*/ instance, */ size_t sp_cp437_grid_width(Cp437Grid */*notnull*/ instance); +/** + * Call this function at the beginning of main to enable rust logging controlled by the + * `RUST_LOG` environment variable. See [env_logger](https://docs.rs/env_logger/latest/env_logger/). + * + * This function is part of the sp_envlogger module. + */ +void sp_envlogger_init(void); + /** *Clones a [`Packet`] instance. * @@ -1960,6 +1970,8 @@ void sp_packet_free(struct Packet */*notnull*/ instance); * Creates a raw [Packet] from parts. * * returns: new instance. Will never return null. + * + * This function is part of the sp_packet module. */ struct Packet */*notnull*/ sp_packet_from_parts(struct Header header, struct ByteSlice payload); @@ -1972,11 +1984,14 @@ struct Packet */*notnull*/ sp_packet_from_parts(struct Header header, struct Header sp_packet_get_header(struct Packet */*notnull*/ instance); /** - * Returns a pointer to the header field of the provided packet. + * Gets a reference to the field `header` of the [`servicepoint::Packet`]. * - * The returned header can be changed and will be valid for the lifetime of the packet. + * - The returned reference inherits the lifetime of object in which it is contained. + * - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. + * + * This function is part of the sp_packet module. */ -struct Header */*notnull*/ sp_packet_get_header_mut(struct Packet */*notnull*/ packet); +struct Header */*notnull*/ sp_packet_get_header_mut(struct Packet */*notnull*/ instance); /** * Returns a pointer to the current payload of the provided packet. @@ -1984,6 +1999,8 @@ struct Header */*notnull*/ sp_packet_get_header_mut(struct Packet */*notnull*/ p * 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. + * + * This function is part of the sp_packet module. */ struct ByteSlice sp_packet_get_payload(struct Packet */*notnull*/ packet); @@ -1993,9 +2010,11 @@ struct ByteSlice sp_packet_get_payload(struct Packet */*notnull*/ packet); * # Panics * * - if the buffer is not big enough to hold header+payload. + * + * This function is part of the sp_packet module. */ -void sp_packet_serialize_to(struct Packet */*notnull*/ packet, - struct ByteSlice buffer); +size_t sp_packet_serialize_to(struct Packet */*notnull*/ packet, + struct ByteSlice buffer); /** * Sets the value of field `header` of the [`servicepoint::Packet`]. @@ -2009,6 +2028,8 @@ void sp_packet_set_header(struct Packet */*notnull*/ instance, * Sets the payload of the provided packet to the provided data. * * This makes previous payload pointers invalid. + * + * This function is part of the sp_packet module. */ void sp_packet_set_payload(struct Packet */*notnull*/ packet, struct ByteSlice data); @@ -2017,6 +2038,8 @@ void sp_packet_set_payload(struct Packet */*notnull*/ packet, * Tries to load a [Packet] from the passed array with the specified length. * * returns: NULL in case of an error, pointer to the allocated packet otherwise + * + * This function is part of the sp_packet module. */ struct Packet *sp_packet_try_load(struct ByteSlice data); @@ -2024,6 +2047,8 @@ struct Packet *sp_packet_try_load(struct ByteSlice data); * Converts u16 into [CommandCode]. * * If the provided value is not valid, false is returned and result is not changed. + * + * This function is part of the sp module. */ bool sp_u16_to_command_code(uint16_t code, CommandCode *result); @@ -2047,6 +2072,8 @@ void sp_udp_free(struct UdpSocket */*notnull*/ instance); * if (connection != NULL) * sp_udp_send_command(connection, sp_command_clear()); * ``` + * + * This function is part of the sp_udp module. */ struct UdpSocket *sp_udp_open(char */*notnull*/ host); @@ -2062,6 +2089,8 @@ struct UdpSocket *sp_udp_open(char */*notnull*/ host); * if (connection != NULL) * sp_udp_send_command(connection, sp_command_clear()); * ``` + * + * This function is part of the sp_udp module. */ struct UdpSocket *sp_udp_open_ipv4(uint8_t ip1, uint8_t ip2, @@ -2081,6 +2110,8 @@ struct UdpSocket *sp_udp_open_ipv4(uint8_t ip1, * ```C * sp_udp_send_command(connection, sp_command_brightness(5)); * ``` + * + * This function is part of the sp_udp module. */ bool sp_udp_send_command(struct UdpSocket */*notnull*/ connection, struct Command command); @@ -2095,6 +2126,8 @@ bool sp_udp_send_command(struct UdpSocket */*notnull*/ connection, * ```C * sp_udp_send_header(connection, sp_command_brightness(5)); * ``` + * + * This function is part of the sp_udp module. */ bool sp_udp_send_header(struct UdpSocket */*notnull*/ udp_connection, struct Header header); @@ -2105,6 +2138,8 @@ bool sp_udp_send_header(struct UdpSocket */*notnull*/ udp_connection, * The passed `packet` gets consumed. * * returns: true in case of success + * + * This function is part of the sp_udp module. */ bool sp_udp_send_packet(struct UdpSocket */*notnull*/ connection, struct Packet */*notnull*/ packet); diff --git a/src/commands/cc_only_commands.rs b/src/commands/cc_only_commands.rs index 234b781..f4162a0 100644 --- a/src/commands/cc_only_commands.rs +++ b/src/commands/cc_only_commands.rs @@ -5,39 +5,33 @@ use crate::{ use servicepoint::{ClearCommand, FadeOutCommand, HardResetCommand}; use std::ptr::NonNull; -wrap_functions!(sp_cmd_clear; +macro_rules! wrap_cc_only { + ($prefix:ident :: $typ:ident ; $(#[$meta:meta])*) => { + wrap_functions!($prefix; + $(#[$meta])* + /// + #[doc = concat!(" Returns: a new [`",stringify!($typ),"`] instance.")] + fn new() -> NonNull<$typ> { + heap_move_nonnull($typ) + } + ); + + wrap_free!($prefix :: $typ); + }; +} + +wrap_cc_only!(sp_cmd_clear::ClearCommand; /// Set all pixels to the off state. /// /// Does not affect brightness. - /// - /// Returns: a new [ClearCommand] instance. - fn new() -> NonNull { - heap_move_nonnull(ClearCommand) - } ); -wrap_free!(sp_cmd_clear::ClearCommand); - -wrap_functions!(sp_cmd_hard_reset; +wrap_cc_only!(sp_cmd_hard_reset::HardResetCommand; /// 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 [HardResetCommand] instance. - fn new() -> NonNull { - heap_move_nonnull(HardResetCommand) - } ); -wrap_free!(sp_cmd_hard_reset::HardResetCommand); - -wrap_functions!(sp_cmd_fade_out; +wrap_cc_only!(sp_cmd_fade_out::FadeOutCommand; /// A yet-to-be-tested command. - /// - /// Returns: a new [FadeOutCommand] instance. - fn new() -> NonNull { - heap_move_nonnull(FadeOutCommand) - } ); - -wrap_free!(sp_cmd_fade_out::FadeOutCommand); diff --git a/src/commands/generic_command.rs b/src/commands/generic_command.rs index 840900f..9734d30 100644 --- a/src/commands/generic_command.rs +++ b/src/commands/generic_command.rs @@ -1,6 +1,9 @@ -use crate::mem::{ - heap_clone, heap_drop, heap_move, heap_move_nonnull, heap_move_ok, - heap_remove, +use crate::{ + macros::wrap_functions, + mem::{ + heap_clone, heap_drop, heap_move, heap_move_nonnull, heap_move_ok, + heap_remove, + }, }; use servicepoint::{ BitVecCommand, BitmapCommand, BrightnessGridCommand, CharGridCommand, @@ -66,233 +69,233 @@ impl SPCommand { }; } -/// Tries to turn a [Packet] into a [SPCommand]. -/// -/// The packet is dropped in the process. -/// -/// Returns: pointer to new [SPCommand] instance or NULL if parsing failed. -#[no_mangle] -pub unsafe extern "C" fn sp_cmd_generic_try_from_packet( - packet: NonNull, -) -> SPCommand { - let packet = *unsafe { Box::from_raw(packet.as_ptr()) }; - servicepoint::TypedCommand::try_from(packet) - .map(|value| match value { - TypedCommand::Clear(clear) => SPCommand { - tag: CommandTag::Clear, - data: CommandUnion { - clear: heap_move_nonnull(clear), - }, - }, - TypedCommand::CharGrid(char_grid) => SPCommand { - tag: CommandTag::CharGrid, - data: CommandUnion { - char_grid: heap_move_nonnull(char_grid), - }, - }, - TypedCommand::Cp437Grid(cp437_grid) => SPCommand { - tag: CommandTag::Cp437Grid, - data: CommandUnion { - cp437_grid: heap_move_nonnull(cp437_grid), - }, - }, - TypedCommand::Bitmap(bitmap) => SPCommand { - tag: CommandTag::Bitmap, - data: CommandUnion { - bitmap: heap_move_nonnull(bitmap), - }, - }, - TypedCommand::Brightness(global_brightness) => SPCommand { - tag: CommandTag::GlobalBrightness, - data: CommandUnion { - global_brightness: heap_move_nonnull(global_brightness), - }, - }, - TypedCommand::BrightnessGrid(brightness_grid) => SPCommand { - tag: CommandTag::BrightnessGrid, - data: CommandUnion { - brightness_grid: heap_move_nonnull(brightness_grid), - }, - }, - TypedCommand::BitVec(bitvec) => SPCommand { - tag: CommandTag::BitVec, - data: CommandUnion { - bitvec: heap_move_nonnull(bitvec), - }, - }, - TypedCommand::HardReset(hard_reset) => SPCommand { - tag: CommandTag::HardReset, - data: CommandUnion { - hard_reset: heap_move_nonnull(hard_reset), - }, - }, - TypedCommand::FadeOut(fade_out) => SPCommand { - tag: CommandTag::FadeOut, - data: CommandUnion { - fade_out: heap_move_nonnull(fade_out), - }, - }, - #[allow(deprecated)] - TypedCommand::BitmapLegacy(bitmap_legacy) => SPCommand { - tag: CommandTag::BitmapLegacy, - data: CommandUnion { - bitmap_legacy: heap_move_nonnull(bitmap_legacy), - }, - }, - }) - .unwrap_or_else(move |_| SPCommand { - tag: CommandTag::Invalid, - data: CommandUnion { null: null_mut() }, - }) -} +wrap_functions!(sp_cmd_generic; -/// Clones an [SPCommand] instance. -/// -/// returns: a new [SPCommand] instance. -#[no_mangle] -pub unsafe extern "C" fn sp_cmd_generic_clone(command: SPCommand) -> SPCommand { - unsafe { - match command.tag { - CommandTag::Clear => SPCommand { - tag: CommandTag::Clear, - data: CommandUnion { - clear: heap_clone(command.data.clear), + /// Tries to turn a [Packet] into a [SPCommand]. + /// + /// The packet is dropped in the process. + /// + /// Returns: pointer to new [SPCommand] instance or NULL if parsing failed. + fn try_from_packet( + packet: NonNull, + ) -> SPCommand { + let packet = *unsafe { Box::from_raw(packet.as_ptr()) }; + servicepoint::TypedCommand::try_from(packet) + .map(|value| match value { + TypedCommand::Clear(clear) => SPCommand { + tag: CommandTag::Clear, + data: CommandUnion { + clear: heap_move_nonnull(clear), + }, }, - }, - CommandTag::CharGrid => SPCommand { - tag: CommandTag::CharGrid, - data: CommandUnion { - char_grid: heap_clone(command.data.char_grid), + TypedCommand::CharGrid(char_grid) => SPCommand { + tag: CommandTag::CharGrid, + data: CommandUnion { + char_grid: heap_move_nonnull(char_grid), + }, }, - }, - CommandTag::Cp437Grid => SPCommand { - tag: CommandTag::Cp437Grid, - data: CommandUnion { - cp437_grid: heap_clone(command.data.cp437_grid), + TypedCommand::Cp437Grid(cp437_grid) => SPCommand { + tag: CommandTag::Cp437Grid, + data: CommandUnion { + cp437_grid: heap_move_nonnull(cp437_grid), + }, }, - }, - CommandTag::Bitmap => SPCommand { - tag: CommandTag::Bitmap, - data: CommandUnion { - bitmap: heap_clone(command.data.bitmap), + TypedCommand::Bitmap(bitmap) => SPCommand { + tag: CommandTag::Bitmap, + data: CommandUnion { + bitmap: heap_move_nonnull(bitmap), + }, }, - }, - CommandTag::GlobalBrightness => SPCommand { - tag: CommandTag::GlobalBrightness, - data: CommandUnion { - global_brightness: heap_clone( - command.data.global_brightness, - ), + TypedCommand::Brightness(global_brightness) => SPCommand { + tag: CommandTag::GlobalBrightness, + data: CommandUnion { + global_brightness: heap_move_nonnull(global_brightness), + }, }, - }, - CommandTag::BrightnessGrid => SPCommand { - tag: CommandTag::BrightnessGrid, - data: CommandUnion { - brightness_grid: heap_clone(command.data.brightness_grid), + TypedCommand::BrightnessGrid(brightness_grid) => SPCommand { + tag: CommandTag::BrightnessGrid, + data: CommandUnion { + brightness_grid: heap_move_nonnull(brightness_grid), + }, }, - }, - CommandTag::BitVec => SPCommand { - tag: CommandTag::BitVec, - data: CommandUnion { - bitvec: heap_clone(command.data.bitvec), + TypedCommand::BitVec(bitvec) => SPCommand { + tag: CommandTag::BitVec, + data: CommandUnion { + bitvec: heap_move_nonnull(bitvec), + }, }, - }, - CommandTag::HardReset => SPCommand { - tag: CommandTag::HardReset, - data: CommandUnion { - hard_reset: heap_clone(command.data.hard_reset), + TypedCommand::HardReset(hard_reset) => SPCommand { + tag: CommandTag::HardReset, + data: CommandUnion { + hard_reset: heap_move_nonnull(hard_reset), + }, }, - }, - CommandTag::FadeOut => SPCommand { - tag: CommandTag::FadeOut, - data: CommandUnion { - fade_out: heap_clone(command.data.fade_out), + TypedCommand::FadeOut(fade_out) => SPCommand { + tag: CommandTag::FadeOut, + data: CommandUnion { + fade_out: heap_move_nonnull(fade_out), + }, }, - }, - #[allow(deprecated)] - CommandTag::BitmapLegacy => SPCommand { - tag: CommandTag::BitmapLegacy, - data: CommandUnion { - bitmap_legacy: heap_clone(command.data.bitmap_legacy), + #[allow(deprecated)] + TypedCommand::BitmapLegacy(bitmap_legacy) => SPCommand { + tag: CommandTag::BitmapLegacy, + data: CommandUnion { + bitmap_legacy: heap_move_nonnull(bitmap_legacy), + }, }, - }, - CommandTag::Invalid => SPCommand::INVALID, - } + }) + .unwrap_or_else(move |_| SPCommand { + tag: CommandTag::Invalid, + data: CommandUnion { null: null_mut() }, + }) } -} -/// Deallocates an [SPCommand]. -/// -/// Commands with an invalid `tag` do not have to be freed as the `data` pointer should be null. -/// -/// # Examples -/// -/// ```C -/// SPCommand c = sp_cmd_clear_into_generic(sp_cmd_clear_new()); -/// sp_command_free(c); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn sp_cmd_generic_free(command: SPCommand) { - unsafe { - match command.tag { - CommandTag::Invalid => (), - CommandTag::Bitmap => heap_drop(command.data.bitmap), - CommandTag::BitVec => heap_drop(command.data.bitvec), - CommandTag::BrightnessGrid => { - heap_drop(command.data.brightness_grid) + /// Clones an [SPCommand] instance. + /// + /// returns: a new [SPCommand] instance. + fn clone(command: SPCommand) -> SPCommand { + unsafe { + match command.tag { + CommandTag::Clear => SPCommand { + tag: CommandTag::Clear, + data: CommandUnion { + clear: heap_clone(command.data.clear), + }, + }, + CommandTag::CharGrid => SPCommand { + tag: CommandTag::CharGrid, + data: CommandUnion { + char_grid: heap_clone(command.data.char_grid), + }, + }, + CommandTag::Cp437Grid => SPCommand { + tag: CommandTag::Cp437Grid, + data: CommandUnion { + cp437_grid: heap_clone(command.data.cp437_grid), + }, + }, + CommandTag::Bitmap => SPCommand { + tag: CommandTag::Bitmap, + data: CommandUnion { + bitmap: heap_clone(command.data.bitmap), + }, + }, + CommandTag::GlobalBrightness => SPCommand { + tag: CommandTag::GlobalBrightness, + data: CommandUnion { + global_brightness: heap_clone( + command.data.global_brightness, + ), + }, + }, + CommandTag::BrightnessGrid => SPCommand { + tag: CommandTag::BrightnessGrid, + data: CommandUnion { + brightness_grid: heap_clone(command.data.brightness_grid), + }, + }, + CommandTag::BitVec => SPCommand { + tag: CommandTag::BitVec, + data: CommandUnion { + bitvec: heap_clone(command.data.bitvec), + }, + }, + CommandTag::HardReset => SPCommand { + tag: CommandTag::HardReset, + data: CommandUnion { + hard_reset: heap_clone(command.data.hard_reset), + }, + }, + CommandTag::FadeOut => SPCommand { + tag: CommandTag::FadeOut, + data: CommandUnion { + fade_out: heap_clone(command.data.fade_out), + }, + }, + #[allow(deprecated)] + CommandTag::BitmapLegacy => SPCommand { + tag: CommandTag::BitmapLegacy, + data: CommandUnion { + bitmap_legacy: heap_clone(command.data.bitmap_legacy), + }, + }, + CommandTag::Invalid => SPCommand::INVALID, } - CommandTag::CharGrid => heap_drop(command.data.char_grid), - CommandTag::Cp437Grid => heap_drop(command.data.cp437_grid), - CommandTag::GlobalBrightness => { - heap_drop(command.data.global_brightness) - } - CommandTag::Clear => heap_drop(command.data.clear), - CommandTag::HardReset => heap_drop(command.data.hard_reset), - CommandTag::FadeOut => heap_drop(command.data.fade_out), - CommandTag::BitmapLegacy => heap_drop(command.data.bitmap_legacy), } } -} -/// Tries to turn a [SPCommand] into a [Packet]. -/// The [SPCommand] gets consumed. -/// -/// Returns tag [CommandTag::Invalid] in case of an error. -#[no_mangle] -pub unsafe extern "C" fn sp_cmd_generic_into_packet( - command: SPCommand, -) -> *mut Packet { - match command.tag { - CommandTag::Invalid => null_mut(), - CommandTag::Bitmap => { - heap_move_ok(unsafe { heap_remove(command.data.bitmap).try_into() }) - } - CommandTag::BitVec => { - heap_move_ok(unsafe { heap_remove(command.data.bitvec).try_into() }) - } - CommandTag::BrightnessGrid => heap_move_ok(unsafe { - heap_remove(command.data.brightness_grid).try_into() - }), - CommandTag::CharGrid => heap_move_ok(unsafe { - heap_remove(command.data.char_grid).try_into() - }), - CommandTag::Cp437Grid => heap_move_ok(unsafe { - heap_remove(command.data.cp437_grid).try_into() - }), - CommandTag::GlobalBrightness => heap_move(unsafe { - heap_remove(command.data.global_brightness).into() - }), - CommandTag::Clear => { - heap_move(unsafe { heap_remove(command.data.clear).into() }) - } - CommandTag::HardReset => { - heap_move(unsafe { heap_remove(command.data.hard_reset).into() }) - } - CommandTag::FadeOut => { - heap_move(unsafe { heap_remove(command.data.fade_out).into() }) - } - CommandTag::BitmapLegacy => { - heap_move(unsafe { heap_remove(command.data.bitmap_legacy).into() }) + /// Deallocates an [SPCommand]. + /// + /// Commands with an invalid `tag` do not have to be freed as the `data` pointer should be null. + /// + /// # Examples + /// + /// ```C + /// SPCommand c = sp_cmd_clear_into_generic(sp_cmd_clear_new()); + /// sp_command_free(c); + /// ``` + fn free(command: SPCommand) { + unsafe { + match command.tag { + CommandTag::Invalid => (), + CommandTag::Bitmap => heap_drop(command.data.bitmap), + CommandTag::BitVec => heap_drop(command.data.bitvec), + CommandTag::BrightnessGrid => { + heap_drop(command.data.brightness_grid) + } + CommandTag::CharGrid => heap_drop(command.data.char_grid), + CommandTag::Cp437Grid => heap_drop(command.data.cp437_grid), + CommandTag::GlobalBrightness => { + heap_drop(command.data.global_brightness) + } + CommandTag::Clear => heap_drop(command.data.clear), + CommandTag::HardReset => heap_drop(command.data.hard_reset), + CommandTag::FadeOut => heap_drop(command.data.fade_out), + CommandTag::BitmapLegacy => heap_drop(command.data.bitmap_legacy), + } } } -} + + /// Tries to turn a [SPCommand] into a [Packet]. + /// The [SPCommand] gets consumed. + /// + /// Returns tag [CommandTag::Invalid] in case of an error. + fn into_packet( + command: SPCommand, + ) -> *mut Packet { + match command.tag { + CommandTag::Invalid => null_mut(), + CommandTag::Bitmap => { + heap_move_ok(unsafe { heap_remove(command.data.bitmap).try_into() }) + } + CommandTag::BitVec => { + heap_move_ok(unsafe { heap_remove(command.data.bitvec).try_into() }) + } + CommandTag::BrightnessGrid => heap_move_ok(unsafe { + heap_remove(command.data.brightness_grid).try_into() + }), + CommandTag::CharGrid => heap_move_ok(unsafe { + heap_remove(command.data.char_grid).try_into() + }), + CommandTag::Cp437Grid => heap_move_ok(unsafe { + heap_remove(command.data.cp437_grid).try_into() + }), + CommandTag::GlobalBrightness => heap_move(unsafe { + heap_remove(command.data.global_brightness).into() + }), + CommandTag::Clear => { + heap_move(unsafe { heap_remove(command.data.clear).into() }) + } + CommandTag::HardReset => { + heap_move(unsafe { heap_remove(command.data.hard_reset).into() }) + } + CommandTag::FadeOut => { + heap_move(unsafe { heap_remove(command.data.fade_out).into() }) + } + CommandTag::BitmapLegacy => { + heap_move(unsafe { heap_remove(command.data.bitmap_legacy).into() }) + } + } + } + +); diff --git a/src/containers/byte_slice.rs b/src/containers/byte_slice.rs index 0642cfc..10ee22f 100644 --- a/src/containers/byte_slice.rs +++ b/src/containers/byte_slice.rs @@ -35,7 +35,7 @@ impl ByteSlice { unsafe { std::slice::from_raw_parts(self.start, self.length) } } - pub(crate) unsafe fn as_slice_mut(&mut self) -> &mut [u8] { + pub(crate) unsafe fn as_slice_mut(&self) -> &mut [u8] { unsafe { std::slice::from_raw_parts_mut(self.start, self.length) } } diff --git a/src/lib.rs b/src/lib.rs index e08fbd7..344a1a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,10 +47,13 @@ pub struct DisplayBitVec; #[cfg(feature = "env_logger")] mod feature_env_logger { - /// Call this function at the beginning of main to enable rust logging controlled by the - /// `RUST_LOG` environment variable. See [env_logger](https://docs.rs/env_logger/latest/env_logger/). - #[no_mangle] - pub unsafe extern "C" fn init_env_logger() { - env_logger::init(); - } + use crate::macros::wrap_functions; + + wrap_functions!(sp_envlogger; + /// Call this function at the beginning of main to enable rust logging controlled by the + /// `RUST_LOG` environment variable. See [env_logger](https://docs.rs/env_logger/latest/env_logger/). + fn init() { + env_logger::init(); + } + ); } diff --git a/src/packet.rs b/src/packet.rs index af49fc8..4278fe0 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,95 +1,72 @@ use crate::{ containers::ByteSlice, - macros::{wrap_clone, wrap_fields, wrap_free}, + macros::{wrap_clone, wrap_fields, wrap_free, wrap_functions}, mem::{heap_move_nonnull, heap_move_ok}, }; use servicepoint::{CommandCode, Header, Packet}; use std::ptr::NonNull; -/// Tries to load a [Packet] from the passed array with the specified length. -/// -/// returns: NULL in case of an error, pointer to the allocated packet otherwise -#[no_mangle] -pub unsafe extern "C" fn sp_packet_try_load(data: ByteSlice) -> *mut Packet { - let data = unsafe { data.as_slice() }; - heap_move_ok(servicepoint::Packet::try_from(data)) -} +wrap_functions!(sp_packet; -/// Creates a raw [Packet] from parts. -/// -/// returns: new instance. Will never return null. -#[no_mangle] -pub unsafe extern "C" fn sp_packet_from_parts( - header: Header, - payload: ByteSlice, -) -> NonNull { - let payload = if payload == ByteSlice::INVALID { - None - } else { - Some(Vec::from(unsafe { payload.as_slice() })) - }; - - heap_move_nonnull(Packet { header, 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. -#[no_mangle] -pub unsafe extern "C" fn sp_packet_get_header_mut( - packet: NonNull, -) -> NonNull
{ - NonNull::from(unsafe { &mut (*packet.as_ptr()).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 { - match &mut (*packet.as_ptr()).payload { - None => ByteSlice::INVALID, - Some(payload) => ByteSlice::from_slice(payload), - } + /// 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 + fn try_load(data: ByteSlice) -> *mut Packet { + let data = unsafe { data.as_slice() }; + heap_move_ok(servicepoint::Packet::try_from(data)) } -} -/// 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, -) { - unsafe { - (*packet.as_ptr()).payload = if data == ByteSlice::INVALID { + /// Creates a raw [Packet] from parts. + /// + /// returns: new instance. Will never return null. + fn from_parts(header: Header, payload: ByteSlice) -> NonNull { + let payload = if payload == ByteSlice::INVALID { None } else { - Some(data.as_slice().to_vec()) + Some(Vec::from(unsafe { payload.as_slice() })) + }; + + heap_move_nonnull(Packet { header, payload }) + } + + /// 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. + fn get_payload(packet: NonNull) -> ByteSlice { + unsafe { + match &mut (*packet.as_ptr()).payload { + None => ByteSlice::INVALID, + Some(payload) => ByteSlice::from_slice(payload), + } } } -} -/// 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( - packet: NonNull, - mut buffer: ByteSlice, -) { - unsafe { - packet.as_ref().serialize_to(buffer.as_slice_mut()); + /// Sets the payload of the provided packet to the provided data. + /// + /// This makes previous payload pointers invalid. + fn set_payload(packet: NonNull, data: ByteSlice) { + unsafe { + (*packet.as_ptr()).payload = if data == ByteSlice::INVALID { + None + } else { + Some(data.as_slice().to_vec()) + } + } } -} + + /// Serialize the packet into the provided buffer. + /// + /// # Panics + /// + /// - if the buffer is not big enough to hold header+payload. + fn serialize_to(packet: NonNull, buffer: ByteSlice) -> usize { + unsafe { + packet.as_ref().serialize_to(buffer.as_slice_mut()).unwrap_or(0) + } + } +); wrap_clone!(sp_packet::Packet); wrap_free!(sp_packet::Packet); @@ -98,25 +75,29 @@ wrap_fields!( sp_packet::Packet; prop header: Header { get(); + mut get(); set(value); }; ); -/// Converts u16 into [CommandCode]. -/// -/// If the provided value is not valid, false is returned and result is not changed. -#[no_mangle] -pub unsafe extern "C" fn sp_u16_to_command_code( - code: u16, - result: *mut CommandCode, -) -> bool { - match CommandCode::try_from(code) { - Ok(code) => { - unsafe { - *result = code; +wrap_functions!(sp; + + /// Converts u16 into [CommandCode]. + /// + /// If the provided value is not valid, false is returned and result is not changed. + fn u16_to_command_code( + code: u16, + result: *mut CommandCode, + ) -> bool { + match CommandCode::try_from(code) { + Ok(code) => { + unsafe { + *result = code; + } + true } - true + Err(_) => false, } - Err(_) => false, } -} + +); diff --git a/src/udp.rs b/src/udp.rs index e18476a..cb96547 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -1,6 +1,6 @@ use crate::{ commands::{CommandTag, SPCommand}, - macros::wrap_free, + macros::{wrap_free, wrap_functions}, mem::{heap_move_ok, heap_remove}, }; use servicepoint::{Header, Packet, UdpSocketExt}; @@ -10,138 +10,122 @@ use std::{ ptr::NonNull, }; -/// Creates a new instance of [UdpSocket]. -/// -/// returns: NULL if connection fails, or connected instance -/// -/// # Examples -/// -/// ```C -/// UdpSocket connection = sp_udp_open("172.23.42.29:2342"); -/// if (connection != NULL) -/// sp_udp_send_command(connection, sp_command_clear()); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn sp_udp_open(host: NonNull) -> *mut UdpSocket { - let host = unsafe { CStr::from_ptr(host.as_ptr()) } - .to_str() - .expect("Bad encoding"); - - heap_move_ok(UdpSocket::bind_connect(host)) -} - -/// Creates a new instance of [UdpSocket]. -/// -/// returns: NULL if connection fails, or connected instance -/// -/// # Examples -/// -/// ```C -/// UdpSocket connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); -/// if (connection != NULL) -/// sp_udp_send_command(connection, sp_command_clear()); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn sp_udp_open_ipv4( - ip1: u8, - ip2: u8, - ip3: u8, - ip4: u8, - port: u16, -) -> *mut UdpSocket { - let addr = SocketAddrV4::new(Ipv4Addr::from([ip1, ip2, ip3, ip4]), port); - heap_move_ok(UdpSocket::bind_connect(addr)) -} - -/// Sends a [Packet] to the display using the [UdpSocket]. -/// -/// The passed `packet` gets consumed. -/// -/// returns: true in case of success -#[no_mangle] -pub unsafe extern "C" fn sp_udp_send_packet( - connection: NonNull, - packet: NonNull, -) -> bool { - let packet = unsafe { heap_remove(packet) }; - unsafe { connection.as_ref().send(&Vec::from(packet)) }.is_ok() -} - -/// Sends a [SPCommand] to the display using the [UdpSocket]. -/// -/// The passed `command` gets consumed. -/// -/// returns: true in case of success -/// -/// # Examples -/// -/// ```C -/// sp_udp_send_command(connection, sp_command_brightness(5)); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn sp_udp_send_command( - connection: NonNull, - command: SPCommand, -) -> bool { - unsafe { - match command.tag { - CommandTag::Invalid => return false, - CommandTag::Bitmap => connection - .as_ref() - .send_command(heap_remove(command.data.bitmap)), - CommandTag::BitVec => connection - .as_ref() - .send_command(heap_remove(command.data.bitvec)), - CommandTag::BrightnessGrid => connection - .as_ref() - .send_command(heap_remove(command.data.brightness_grid)), - CommandTag::CharGrid => connection - .as_ref() - .send_command(heap_remove(command.data.char_grid)), - CommandTag::Cp437Grid => connection - .as_ref() - .send_command(heap_remove(command.data.cp437_grid)), - CommandTag::GlobalBrightness => connection - .as_ref() - .send_command(heap_remove(command.data.global_brightness)), - CommandTag::Clear => connection - .as_ref() - .send_command(heap_remove(command.data.clear)), - CommandTag::HardReset => connection - .as_ref() - .send_command(heap_remove(command.data.hard_reset)), - CommandTag::FadeOut => connection - .as_ref() - .send_command(heap_remove(command.data.fade_out)), - CommandTag::BitmapLegacy => connection - .as_ref() - .send_command(heap_remove(command.data.bitmap_legacy)), - } - } - .is_some() -} - -/// Sends a [Header] to the display using the [UdpSocket]. -/// -/// returns: true in case of success -/// -/// # Examples -/// -/// ```C -/// sp_udp_send_header(connection, sp_command_brightness(5)); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn sp_udp_send_header( - udp_connection: NonNull, - header: Header, -) -> bool { - let packet = Packet { - header, - payload: None, - }; - unsafe { udp_connection.as_ref() } - .send(&Vec::from(packet)) - .is_ok() -} - wrap_free!(sp_udp::UdpSocket); + +wrap_functions!(sp_udp; + + /// Creates a new instance of [UdpSocket]. + /// + /// returns: NULL if connection fails, or connected instance + /// + /// # Examples + /// + /// ```C + /// UdpSocket connection = sp_udp_open("172.23.42.29:2342"); + /// if (connection != NULL) + /// sp_udp_send_command(connection, sp_command_clear()); + /// ``` + fn open(host: NonNull) -> *mut UdpSocket { + let host = unsafe { CStr::from_ptr(host.as_ptr()) } + .to_str() + .expect("Bad encoding"); + + heap_move_ok(UdpSocket::bind_connect(host)) + } + + /// Creates a new instance of [UdpSocket]. + /// + /// returns: NULL if connection fails, or connected instance + /// + /// # Examples + /// + /// ```C + /// UdpSocket connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); + /// if (connection != NULL) + /// sp_udp_send_command(connection, sp_command_clear()); + /// ``` + fn open_ipv4(ip1: u8, ip2: u8, ip3: u8, ip4: u8, port: u16) -> *mut UdpSocket { + let addr = SocketAddrV4::new(Ipv4Addr::from([ip1, ip2, ip3, ip4]), port); + heap_move_ok(UdpSocket::bind_connect(addr)) + } + + /// Sends a [Packet] to the display using the [UdpSocket]. + /// + /// The passed `packet` gets consumed. + /// + /// returns: true in case of success + fn send_packet(connection: NonNull, packet: NonNull) -> bool { + let packet = unsafe { heap_remove(packet) }; + unsafe { connection.as_ref().send(&Vec::from(packet)) }.is_ok() + } + + /// Sends a [SPCommand] to the display using the [UdpSocket]. + /// + /// The passed `command` gets consumed. + /// + /// returns: true in case of success + /// + /// # Examples + /// + /// ```C + /// sp_udp_send_command(connection, sp_command_brightness(5)); + /// ``` + fn send_command(connection: NonNull, command: SPCommand) -> bool { + unsafe { + match command.tag { + CommandTag::Invalid => return false, + CommandTag::Bitmap => connection + .as_ref() + .send_command(heap_remove(command.data.bitmap)), + CommandTag::BitVec => connection + .as_ref() + .send_command(heap_remove(command.data.bitvec)), + CommandTag::BrightnessGrid => connection + .as_ref() + .send_command(heap_remove(command.data.brightness_grid)), + CommandTag::CharGrid => connection + .as_ref() + .send_command(heap_remove(command.data.char_grid)), + CommandTag::Cp437Grid => connection + .as_ref() + .send_command(heap_remove(command.data.cp437_grid)), + CommandTag::GlobalBrightness => connection + .as_ref() + .send_command(heap_remove(command.data.global_brightness)), + CommandTag::Clear => connection + .as_ref() + .send_command(heap_remove(command.data.clear)), + CommandTag::HardReset => connection + .as_ref() + .send_command(heap_remove(command.data.hard_reset)), + CommandTag::FadeOut => connection + .as_ref() + .send_command(heap_remove(command.data.fade_out)), + CommandTag::BitmapLegacy => connection + .as_ref() + .send_command(heap_remove(command.data.bitmap_legacy)), + } + } + .is_some() + } + + /// Sends a [Header] to the display using the [UdpSocket]. + /// + /// returns: true in case of success + /// + /// # Examples + /// + /// ```C + /// sp_udp_send_header(connection, sp_command_brightness(5)); + /// ``` + fn send_header(udp_connection: NonNull, header: Header) -> bool { + let packet = Packet { + header, + payload: None, + }; + unsafe { udp_connection.as_ref() } + .send(&Vec::from(packet)) + .is_ok() + } + +);