Skip to content

Commit

Permalink
Change on_event ffi func to accept multiple events
Browse files Browse the repository at this point in the history
  • Loading branch information
hulthe committed May 16, 2024
1 parent b92588b commit ab54d79
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 13 deletions.
8 changes: 5 additions & 3 deletions crates/maybenot-ffi/maybenot.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,20 @@ uint64_t maybenot_num_machines(struct MaybenotFramework *this_);
void maybenot_stop(struct MaybenotFramework *this_);

/**
* Feed an event to the [`MaybenotFramework`] instance.
* Feed events to the [`MaybenotFramework`] instance.
*
* This may generate [super::MaybenotAction]s that will be written to `actions_out`.
* The number of actions will be written to `num_actions_out`.
*
* # Safety
* - `this` MUST have been created by [`maybenot_start`].
* - `events` MUST be a valid pointer to an array of size `num_events`.
* - `actions_out` MUST have capacity for [`maybenot_num_machines`] items of size
* `sizeof(MaybenotAction)` bytes.
* - `num_actions_out` MUST be a valid pointer where a 64bit int can be written.
*/
MaybenotResult maybenot_on_event(struct MaybenotFramework *this_,
struct MaybenotEvent event,
const struct MaybenotEvent *events,
uintptr_t num_events,
struct MaybenotAction *actions_out,
uint64_t *num_actions_out);
uintptr_t *num_actions_out);
17 changes: 12 additions & 5 deletions crates/maybenot-ffi/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use core::{
mem::MaybeUninit,
slice::from_raw_parts_mut,
};
use std::slice::from_raw_parts;

// NOTE: must be null-terminated.
static VERSION: &str = concat!("maybenot-ffi/", env!("CARGO_PKG_VERSION"), "\0");
Expand Down Expand Up @@ -80,39 +81,45 @@ pub unsafe extern "C" fn maybenot_stop(this: *mut MaybenotFramework) {
let _this = unsafe { Box::from_raw(this) };
}

/// Feed an event to the [`MaybenotFramework`] instance.
/// Feed events to the [`MaybenotFramework`] instance.
///
/// This may generate [super::MaybenotAction]s that will be written to `actions_out`.
/// The number of actions will be written to `num_actions_out`.
///
/// # Safety
/// - `this` MUST have been created by [`maybenot_start`].
/// - `events` MUST be a valid pointer to an array of size `num_events`.
/// - `actions_out` MUST have capacity for [`maybenot_num_machines`] items of size
/// `sizeof(MaybenotAction)` bytes.
/// - `num_actions_out` MUST be a valid pointer where a 64bit int can be written.
#[no_mangle]
pub unsafe extern "C" fn maybenot_on_event(
this: *mut MaybenotFramework,
event: MaybenotEvent,
events: *const MaybenotEvent,
num_events: usize,
actions_out: *mut MaybeUninit<MaybenotAction>,
num_actions_out: *mut u64,
num_actions_out: *mut usize,
) -> MaybenotResult {
let Some(this) = (unsafe { this.as_mut() }) else {
return MaybenotResult::NullPointer;
};

if num_actions_out.is_null() {
if events.is_null() || actions_out.is_null() || num_actions_out.is_null() {
return MaybenotResult::NullPointer;
}

// SAFETY: called promises that `events` points to valid array containing `num_events` events.
// Rust arrays have the same layout as C arrays.
let events: &[MaybenotEvent] = unsafe { from_raw_parts(events, num_events) };

// SAFETY: called promises that `actions_out` points to valid memory with the capacity to
// hold at least a `num_machines` amount of `MaybenotAction`. Rust arrays have the same
// layout as C arrays. Since we use `MaybeUninit`, rust won't assume that the slice
// elements have been initialized.
let actions: &mut [MaybeUninit<MaybenotAction>] =
unsafe { from_raw_parts_mut(actions_out, this.framework.num_machines()) };

let num_actions = this.on_event(event, actions) as u64;
let num_actions = this.on_events(events, actions);
unsafe { num_actions_out.write(num_actions) };
MaybenotResult::Ok
}
27 changes: 22 additions & 5 deletions crates/maybenot-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ pub use ffi::*;
/// - Stop it: [maybenot_stop].
pub struct MaybenotFramework {
framework: Framework<Vec<Machine>>,

/// A buffer used internally for converting from [MaybenotEvent]s.
events_buf: Vec<TriggerEvent>,
}

#[repr(C)]
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct MaybenotEvent {
pub event_type: MaybenotEventType,

Expand Down Expand Up @@ -111,6 +114,8 @@ impl MaybenotFramework {
.collect::<Result<_, _>>()
.map_err(|_e| MaybenotResult::InvalidMachineString)?;

let machines_count = machines.len();

let framework = Framework::new(
machines,
max_padding_bytes,
Expand All @@ -120,20 +125,32 @@ impl MaybenotFramework {
)
.map_err(|_e| MaybenotResult::StartFramework)?;

Ok(MaybenotFramework { framework })
Ok(MaybenotFramework {
framework,
events_buf: Vec::with_capacity(machines_count),
})
}

fn on_event(
fn on_events(
&mut self,
event: MaybenotEvent,
events: &[MaybenotEvent],
actions: &mut [MaybeUninit<MaybenotAction>],
) -> usize {
let now = Instant::now();

// convert from the repr(C) events and store them temporarily in our buffer
self.events_buf.clear();
for &event in events {
self.events_buf.push(convert_event(event));
}

let num_actions = self
.framework
.trigger_events(&[convert_event(event)], Instant::now())
.trigger_events(&self.events_buf, now)
// convert maybenot actions to repr(C) equivalents
.map(convert_action)
// write the actions to the out buffer
// NOTE: trigger_events will not emit more than one action per machine.
.zip(actions.iter_mut())
.map(|(action, out)| out.write(action))
.count();
Expand Down

0 comments on commit ab54d79

Please sign in to comment.