From d0ad1f954591349690eb517273dd95fcdd04431b Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Mon, 13 May 2024 18:45:46 +0200 Subject: [PATCH 01/20] Add maybenot-ffi --- Cargo.toml | 2 + crates/maybenot-ffi/Cargo.toml | 17 +++ crates/maybenot-ffi/cbindgen.toml | 19 +++ crates/maybenot-ffi/maybenot-ffi.h | 181 +++++++++++++++++++++++++ crates/maybenot-ffi/src/error.rs | 47 +++++++ crates/maybenot-ffi/src/ffi.rs | 95 +++++++++++++ crates/maybenot-ffi/src/lib.rs | 209 +++++++++++++++++++++++++++++ 7 files changed, 570 insertions(+) create mode 100644 crates/maybenot-ffi/Cargo.toml create mode 100644 crates/maybenot-ffi/cbindgen.toml create mode 100644 crates/maybenot-ffi/maybenot-ffi.h create mode 100644 crates/maybenot-ffi/src/error.rs create mode 100644 crates/maybenot-ffi/src/ffi.rs create mode 100644 crates/maybenot-ffi/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 454c3a6..e936dcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ resolver = "2" members = [ # lib for the Maybenot framework "crates/maybenot", + # ffi lib for the Maybenot framework + "crates/maybenot-ffi", # simulator for the Maybenot framework "crates/maybenot-simulator", ] diff --git a/crates/maybenot-ffi/Cargo.toml b/crates/maybenot-ffi/Cargo.toml new file mode 100644 index 0000000..92e90c0 --- /dev/null +++ b/crates/maybenot-ffi/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "maybenot-ffi" +description = "An FFI wrapper around Maybenot" +version = "1.1.1" +edition.workspace = true +license.workspace = true +homepage.workspace = true +keywords.workspace = true +categories.workspace = true +repository.workspace = true + +[lib] +crate-type = ["lib", "staticlib", "cdylib"] + +[dependencies] +maybenot = { version = "1.1.1", path = "../maybenot" } +thiserror = "1.0" diff --git a/crates/maybenot-ffi/cbindgen.toml b/crates/maybenot-ffi/cbindgen.toml new file mode 100644 index 0000000..fb5b98c --- /dev/null +++ b/crates/maybenot-ffi/cbindgen.toml @@ -0,0 +1,19 @@ +# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml + +language = "C" + +# header = "/* Text to put at the beginning of the generated file. Probably a license. */" +# trailer = "/* Text to put at the end of the generated file */" +autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" +include_version = true + +braces = "SameLine" +line_length = 100 +tab_width = 2 +documentation = true +documentation_style = "auto" +documentation_length = "full" +line_endings = "LF" # also "CR", "CRLF", "Native" + +[enum] +prefix_with_name = true diff --git a/crates/maybenot-ffi/maybenot-ffi.h b/crates/maybenot-ffi/maybenot-ffi.h new file mode 100644 index 0000000..c62e1db --- /dev/null +++ b/crates/maybenot-ffi/maybenot-ffi.h @@ -0,0 +1,181 @@ +/* Generated with cbindgen:0.26.0 */ + +/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ + +#include +#include +#include +#include + +enum MaybenotEventType { + /** + * We sent a normal packet. + */ + MaybenotEventType_NonpaddingSent = 0, + /** + * We received a normal packet. + */ + MaybenotEventType_NonpaddingReceived = 1, + /** + * We send a padding packet. + */ + MaybenotEventType_PaddingSent = 2, + /** + * We received a padding packet. + */ + MaybenotEventType_PaddingReceived = 3, +}; +typedef uint32_t MaybenotEventType; + +/** + * An FFI friendly result type. + */ +enum MaybenotResult { + /** + * Operation completed successfully + */ + MaybenotResult_Ok = 0, + MaybenotResult_MachineStringNotUtf8 = 1, + MaybenotResult_InvalidMachineString = 2, + MaybenotResult_StartFramework = 3, + MaybenotResult_NullPointer = 4, +}; +typedef uint32_t MaybenotResult; + +/** + * A running Maybenot instance. + * + * - Create it: [ffi::maybenot_start]. + * - Feed it actions: [ffi::maybenot_on_event]. + * - Stop it: [ffi::maybenot_stop]. + */ +typedef struct MaybenotFramework MaybenotFramework; + +typedef struct MaybenotEvent { + MaybenotEventType event_type; + /** + * The number of bytes that was sent or received. + */ + uint16_t xmit_bytes; + /** + * The ID of the machine that triggered the event, if any. + */ + uintptr_t machine; +} MaybenotEvent; + +typedef struct MaybenotDuration { + /** + * Number of whole seconds + */ + uint64_t secs; + /** + * A nanosecond fraction of a second. + */ + uint32_t nanos; +} MaybenotDuration; + +enum MaybenotAction_Tag { + MaybenotAction_Cancel = 0, + /** + * Send a padding packet. + */ + MaybenotAction_InjectPadding = 1, + MaybenotAction_BlockOutgoing = 2, +}; +typedef uint32_t MaybenotAction_Tag; + +typedef struct MaybenotAction_Cancel_Body { + /** + * The machine that generated the action. + */ + uintptr_t machine; +} MaybenotAction_Cancel_Body; + +typedef struct MaybenotAction_InjectPadding_Body { + /** + * The machine that generated the action. + */ + uintptr_t machine; + /** + * The time to wait before injecting a padding packet. + */ + struct MaybenotDuration timeout; + bool replace; + bool bypass; + /** + * The size of the padding packet. + */ + uint16_t size; +} MaybenotAction_InjectPadding_Body; + +typedef struct MaybenotAction_BlockOutgoing_Body { + /** + * The machine that generated the action. + */ + uintptr_t machine; + /** + * The time to wait before blocking. + */ + struct MaybenotDuration timeout; + bool replace; + bool bypass; + /** + * How long to block. + */ + struct MaybenotDuration duration; +} MaybenotAction_BlockOutgoing_Body; + +typedef struct MaybenotAction { + MaybenotAction_Tag tag; + union { + MaybenotAction_Cancel_Body cancel; + MaybenotAction_InjectPadding_Body inject_padding; + MaybenotAction_BlockOutgoing_Body block_outgoing; + }; +} MaybenotAction; + +/** + * Start a new [`MaybenotFramework`] instance. + * + * # Safety + * - `machines_str` must be a null-terminated UTF-8 string, containing LF-separated machines. + * - `out` must be a valid pointer to some valid pointer-sized memory. + */ +MaybenotResult maybenot_start(const char *machines_str, + double max_padding_bytes, + double max_blocking_bytes, + uint16_t mtu, + struct MaybenotFramework **out); + +/** + * Get the number of machines running in the [`MaybenotFramework`] instance. + * + * # Safety + * + * `this` must be a valid pointer to a [`MaybenotFramework`] instance + */ +uint64_t maybenot_num_machines(struct MaybenotFramework *this_); + +/** + * Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. + * + * # Safety + * The pointer MUST have been created by [maybenot_start]. + */ +void maybenot_stop(struct MaybenotFramework *this_); + +/** + * Feed an event to the [`MaybenotFramework`] instance. + * + * This may generate [super::MaybenotAction]s that will be written to `actions_out`. + * + * # Safety + * `actions_out` must have capacity for [maybenot_num_machines] items of size + * `sizeof(MaybenotAction)` bytes. + * + * The number of actions will be written to `num_actions_out`. + */ +MaybenotResult maybenot_on_event(struct MaybenotFramework *this_, + struct MaybenotEvent event, + struct MaybenotAction *actions_out, + uint64_t *num_actions_out); diff --git a/crates/maybenot-ffi/src/error.rs b/crates/maybenot-ffi/src/error.rs new file mode 100644 index 0000000..da54d05 --- /dev/null +++ b/crates/maybenot-ffi/src/error.rs @@ -0,0 +1,47 @@ +use std::str::Utf8Error; + +/// An FFI friendly result error code type. +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +pub enum MaybenotResult { + /// Operation completed successfully + Ok = 0, + MachineStringNotUtf8 = 1, + InvalidMachineString = 2, + StartFramework = 3, + NullPointer = 4, +} + +#[derive(Clone, Debug, thiserror::Error)] +pub enum Error { + #[error("The machine string wasn't valid UTF-8")] + MachineStringNotUtf8(#[source] Utf8Error), + + #[error("Failed to parse machine string")] + InvalidMachineString, + + #[error("Failed to start framework")] + StartFramework, + + #[error("A null pointer was encountered")] + NullPointer, +} + +impl From for MaybenotResult { + fn from(error: Error) -> Self { + match error { + Error::MachineStringNotUtf8(_) => MaybenotResult::MachineStringNotUtf8, + Error::InvalidMachineString => MaybenotResult::InvalidMachineString, + Error::StartFramework => MaybenotResult::StartFramework, + Error::NullPointer => MaybenotResult::NullPointer, + } + } +} + +impl From> for MaybenotResult { + fn from(result: Result) -> Self { + result + .map(|_| MaybenotResult::Ok) + .unwrap_or_else(MaybenotResult::from) + } +} diff --git a/crates/maybenot-ffi/src/ffi.rs b/crates/maybenot-ffi/src/ffi.rs new file mode 100644 index 0000000..0816df1 --- /dev/null +++ b/crates/maybenot-ffi/src/ffi.rs @@ -0,0 +1,95 @@ +use crate::{error::MaybenotResult, MaybenotAction, MaybenotEvent, MaybenotFramework}; +use core::{ + ffi::{c_char, CStr}, + mem::MaybeUninit, + slice::from_raw_parts_mut, +}; + +/// Start a new [`MaybenotFramework`] instance. +/// +/// # Safety +/// - `machines_str` must be a null-terminated UTF-8 string, containing LF-separated machines. +/// - `out` must be a valid pointer to some valid pointer-sized memory. +#[no_mangle] +pub unsafe extern "C" fn maybenot_start( + machines_str: *const c_char, + max_padding_bytes: f64, + max_blocking_bytes: f64, + mtu: u16, + out: *mut MaybeUninit<*mut MaybenotFramework>, +) -> MaybenotResult { + // SAFETY: see function docs + let Some(out) = (unsafe { out.as_mut() }) else { + return MaybenotResult::NullPointer; + }; + + // SAFETY: see function docs + let machines_str = unsafe { CStr::from_ptr(machines_str) }; + let Ok(machines_str) = machines_str.to_str() else { + return MaybenotResult::MachineStringNotUtf8; + }; + + MaybenotFramework::start(machines_str, max_padding_bytes, max_blocking_bytes, mtu) + .map(|maybenot| { + let box_pointer = Box::into_raw(Box::new(maybenot)); + out.write(box_pointer); + }) + .into() +} + +/// Get the number of machines running in the [`MaybenotFramework`] instance. +/// +/// # Safety +/// +/// `this` must be a valid pointer to a [`MaybenotFramework`] instance +#[no_mangle] +pub unsafe extern "C" fn maybenot_num_machines(this: *mut MaybenotFramework) -> u64 { + let Some(this) = (unsafe { this.as_mut() }) else { + return 0; + }; + + this.framework.num_machines() as u64 +} + +/// Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. +/// +/// # Safety +/// The pointer MUST have been created by [maybenot_start]. +#[no_mangle] +pub unsafe extern "C" fn maybenot_stop(this: *mut MaybenotFramework) { + // Reconstruct the Box and drop it. + // SAFETY: caller pinky promises that this pointer was created by `maybenot_start` + let _this = unsafe { Box::from_raw(this) }; +} + +/// Feed an event to the [`MaybenotFramework`] instance. +/// +/// This may generate [super::MaybenotAction]s that will be written to `actions_out`. +/// +/// # Safety +/// `actions_out` must have capacity for [maybenot_num_machines] items of size +/// `sizeof(MaybenotAction)` bytes. +/// +/// The number of actions will be written to `num_actions_out`. +#[no_mangle] +pub unsafe extern "C" fn maybenot_on_event( + this: *mut MaybenotFramework, + event: MaybenotEvent, + actions_out: *mut MaybeUninit, + num_actions_out: *mut u64, +) -> MaybenotResult { + let Some(this) = (unsafe { this.as_mut() }) else { + return MaybenotResult::NullPointer; + }; + + // 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] = + unsafe { from_raw_parts_mut(actions_out, this.framework.num_machines()) }; + + let num_actions = this.on_event(event, actions) as u64; + unsafe { num_actions_out.write(num_actions) }; + MaybenotResult::Ok +} diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs new file mode 100644 index 0000000..b9447df --- /dev/null +++ b/crates/maybenot-ffi/src/lib.rs @@ -0,0 +1,209 @@ +mod error; +pub use error::*; + +mod ffi; +pub use ffi::*; + +use core::{mem::MaybeUninit, str::FromStr, time::Duration}; +use std::time::Instant; + +use maybenot::{ + framework::{Framework, MachineId, TriggerEvent}, + machine::Machine, +}; + +/// A running Maybenot instance. +/// +/// - Create it: [maybenot_start]. +/// - Feed it actions: [maybenot_on_event]. +/// - Stop it: [maybenot_stop]. +pub struct MaybenotFramework { + framework: Framework>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct MaybenotEvent { + pub event_type: MaybenotEventType, + + /// The number of bytes that was sent or received. + pub xmit_bytes: u16, + + /// The ID of the machine that triggered the event, if any. + pub machine: usize, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct MaybenotDuration { + /// Number of whole seconds + pub secs: u64, + + /// A nanosecond fraction of a second. + pub nanos: u32, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub enum MaybenotEventType { + /// We sent a normal packet. + NonpaddingSent = 0, + + /// We received a normal packet. + NonpaddingReceived = 1, + + /// We send a padding packet. + PaddingSent = 2, + + /// We received a padding packet. + PaddingReceived = 3, +} + +#[repr(C, u32)] +#[derive(Debug, Clone, Copy)] +pub enum MaybenotAction { + Cancel { + /// The machine that generated the action. + machine: usize, + } = 0, + + /// Send a padding packet. + InjectPadding { + /// The machine that generated the action. + machine: usize, + + /// The time to wait before injecting a padding packet. + timeout: MaybenotDuration, + + replace: bool, + bypass: bool, + + /// The size of the padding packet. + size: u16, + } = 1, + + BlockOutgoing { + /// The machine that generated the action. + machine: usize, + + /// The time to wait before blocking. + timeout: MaybenotDuration, + + replace: bool, + bypass: bool, + + /// How long to block. + duration: MaybenotDuration, + } = 2, +} + +impl MaybenotFramework { + pub fn start( + machines_str: &str, + max_padding_bytes: f64, + max_blocking_bytes: f64, + mtu: u16, + ) -> Result { + let machines: Vec<_> = machines_str + .lines() + .map(|line| line.trim()) + .filter(|line| !line.is_empty()) + .filter(|line| !line.starts_with('#')) + .map(Machine::from_str) + .collect::>() + .map_err(|_e| Error::InvalidMachineString)?; + + let framework = Framework::new( + machines, + max_padding_bytes, + max_blocking_bytes, + mtu, + Instant::now(), + ) + .map_err(|_e| Error::StartFramework)?; + + Ok(MaybenotFramework { framework }) + } + + pub fn on_event( + &mut self, + event: MaybenotEvent, + actions: &mut [MaybeUninit], + ) -> usize { + let num_actions = self + .framework + .trigger_events(&[convert_event(event)], Instant::now()) + // convert maybenot actions to repr(C) equivalents + .map(convert_action) + // write the actions to the out buffer + .zip(actions.iter_mut()) + .map(|(action, out)| out.write(action)) + .count(); + + num_actions + } +} + +/// Convert an action from [maybenot] to our own `repr(C)` action type. +fn convert_action(action: &maybenot::framework::Action) -> MaybenotAction { + match *action { + maybenot::framework::Action::Cancel { machine } => MaybenotAction::Cancel { + machine: machine.into_raw(), + }, + maybenot::framework::Action::InjectPadding { + timeout, + size, + bypass, + replace, + machine, + } => MaybenotAction::InjectPadding { + timeout: timeout.into(), + size, + replace, + bypass, + machine: machine.into_raw(), + }, + maybenot::framework::Action::BlockOutgoing { + timeout, + duration, + bypass, + replace, + machine, + } => MaybenotAction::BlockOutgoing { + timeout: timeout.into(), + duration: duration.into(), + replace, + bypass, + machine: machine.into_raw(), + }, + } +} + +fn convert_event(event: MaybenotEvent) -> TriggerEvent { + match event.event_type { + MaybenotEventType::NonpaddingSent => TriggerEvent::NonPaddingSent { + bytes_sent: event.xmit_bytes, + }, + MaybenotEventType::NonpaddingReceived => TriggerEvent::NonPaddingRecv { + bytes_recv: event.xmit_bytes, + }, + MaybenotEventType::PaddingSent => TriggerEvent::PaddingSent { + bytes_sent: event.xmit_bytes, + machine: MachineId::from_raw(event.machine), + }, + MaybenotEventType::PaddingReceived => TriggerEvent::PaddingRecv { + bytes_recv: event.xmit_bytes, + }, + } +} + +impl From for MaybenotDuration { + #[inline] + fn from(duration: Duration) -> Self { + MaybenotDuration { + secs: duration.as_secs(), + nanos: duration.subsec_nanos(), + } + } +} From 595d372b6782691bb052c0c95b5dc76f27baa802 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Mon, 13 May 2024 19:23:16 +0200 Subject: [PATCH 02/20] Add makefile and readme --- .gitignore | 1 + crates/maybenot-ffi/Makefile | 30 ++++++++++++++++++++++++++++++ crates/maybenot-ffi/README.md | 26 ++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 crates/maybenot-ffi/Makefile create mode 100644 crates/maybenot-ffi/README.md diff --git a/.gitignore b/.gitignore index 4fffb2f..d78768a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +*.a diff --git a/crates/maybenot-ffi/Makefile b/crates/maybenot-ffi/Makefile new file mode 100644 index 0000000..e86dd47 --- /dev/null +++ b/crates/maybenot-ffi/Makefile @@ -0,0 +1,30 @@ +CARGO ?= cargo +TARGET ?= +PROFILE ?= release +DESTINATION ?= . + +CARGOFLAGS += --locked +TARGET_DIR := ../../target/$(TARGET)/$(PROFILE) + +ifeq ($(PROFILE), release) + CARGOFLAGS += --release +endif + +ifneq ($(TARGET),) + CARGOFLAGS += --target + CARGOFLAGS += $(TARGET) +endif + +.PHONY: clean + +# copy the library to the final destination, and strip the _ffi part +$(DESTINATION)/libmaybenot.a: $(TARGET_DIR)/libmaybenot_ffi.a + cp $^ $@ + +# build the static library +$(TARGET_DIR)/libmaybenot_ffi.a: src/*.rs Cargo.toml + RUSTFLAGS="-C metadata=maybenot-ffi" ${CARGO} build $(CARGOFLAGS) + +clean: + rm -f $(DESTINATION)/libmaybenot.a + cargo clean diff --git a/crates/maybenot-ffi/README.md b/crates/maybenot-ffi/README.md new file mode 100644 index 0000000..ae9553e --- /dev/null +++ b/crates/maybenot-ffi/README.md @@ -0,0 +1,26 @@ +# Maybenot FFI + +This crate contains C FFI bindings for Maybenot, which let's you use Maybenot as a static library +for languages other than Rust. + +## Building +You need to have [rust](https://rustup.rs/) installed. +Then just run `make` to build a static library at `maybenot-ffi/libmaybenot.a`. + +Arguments to `make`: +- `CARGO` override the `cargo` command +- `TARGET` override target architecture; cross-compile. +- `PROFILE` override the cargo profile, valid options are `release` and `debug`. +- `DESTINATION` change the directory where the output artifacts will be places. + +Example: +``` +make TARGET=x86_64-unknown-linux-gnu PROFILE=debug +``` + +In order to link the resulting library to your program, you'll need to explicitly link some +additional dependencies in addition to `-lmaybenot`. +Run the following command to get an up-to-date list of the required flags for your platform: +``` +RUSTFLAGS="--print native-static-libs" cargo build +``` From 90e1f2c84b372e951a73551a4e1ee32116cfd781 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 14 May 2024 09:50:32 +0200 Subject: [PATCH 03/20] Run cbindgen in build script --- crates/maybenot-ffi/Cargo.toml | 3 +++ crates/maybenot-ffi/README.md | 3 ++- crates/maybenot-ffi/build.rs | 9 +++++++++ crates/maybenot-ffi/{maybenot-ffi.h => maybenot.h} | 8 ++++---- 4 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 crates/maybenot-ffi/build.rs rename crates/maybenot-ffi/{maybenot-ffi.h => maybenot.h} (96%) diff --git a/crates/maybenot-ffi/Cargo.toml b/crates/maybenot-ffi/Cargo.toml index 92e90c0..546c026 100644 --- a/crates/maybenot-ffi/Cargo.toml +++ b/crates/maybenot-ffi/Cargo.toml @@ -15,3 +15,6 @@ crate-type = ["lib", "staticlib", "cdylib"] [dependencies] maybenot = { version = "1.1.1", path = "../maybenot" } thiserror = "1.0" + +[build-dependencies] +cbindgen = { version = "0.26.0", default-features = false } diff --git a/crates/maybenot-ffi/README.md b/crates/maybenot-ffi/README.md index ae9553e..3436cff 100644 --- a/crates/maybenot-ffi/README.md +++ b/crates/maybenot-ffi/README.md @@ -1,7 +1,8 @@ # Maybenot FFI This crate contains C FFI bindings for Maybenot, which let's you use Maybenot as a static library -for languages other than Rust. +for languages other than Rust. Headers are found at `maybenot-ffi/maybenot.h` and are +auto-generated when compiling. ## Building You need to have [rust](https://rustup.rs/) installed. diff --git a/crates/maybenot-ffi/build.rs b/crates/maybenot-ffi/build.rs new file mode 100644 index 0000000..bf46550 --- /dev/null +++ b/crates/maybenot-ffi/build.rs @@ -0,0 +1,9 @@ +use std::env; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + cbindgen::generate(&crate_dir) + .expect("Unable to generate bindings") + .write_to_file(format!("{crate_dir}/maybenot.h")); +} diff --git a/crates/maybenot-ffi/maybenot-ffi.h b/crates/maybenot-ffi/maybenot.h similarity index 96% rename from crates/maybenot-ffi/maybenot-ffi.h rename to crates/maybenot-ffi/maybenot.h index c62e1db..396e926 100644 --- a/crates/maybenot-ffi/maybenot-ffi.h +++ b/crates/maybenot-ffi/maybenot.h @@ -28,7 +28,7 @@ enum MaybenotEventType { typedef uint32_t MaybenotEventType; /** - * An FFI friendly result type. + * An FFI friendly result error code type. */ enum MaybenotResult { /** @@ -45,9 +45,9 @@ typedef uint32_t MaybenotResult; /** * A running Maybenot instance. * - * - Create it: [ffi::maybenot_start]. - * - Feed it actions: [ffi::maybenot_on_event]. - * - Stop it: [ffi::maybenot_stop]. + * - Create it: [maybenot_start]. + * - Feed it actions: [maybenot_on_event]. + * - Stop it: [maybenot_stop]. */ typedef struct MaybenotFramework MaybenotFramework; From c9ec827c896de8fc2a79d4cfbbac3dc40588caa0 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 14 May 2024 10:17:34 +0200 Subject: [PATCH 04/20] Embed crate version in static library --- crates/maybenot-ffi/Makefile | 2 +- crates/maybenot-ffi/build.rs | 29 ++++++++++++++++++++++++++++- crates/maybenot-ffi/src/lib.rs | 3 +++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/maybenot-ffi/Makefile b/crates/maybenot-ffi/Makefile index e86dd47..9918603 100644 --- a/crates/maybenot-ffi/Makefile +++ b/crates/maybenot-ffi/Makefile @@ -22,7 +22,7 @@ $(DESTINATION)/libmaybenot.a: $(TARGET_DIR)/libmaybenot_ffi.a cp $^ $@ # build the static library -$(TARGET_DIR)/libmaybenot_ffi.a: src/*.rs Cargo.toml +$(TARGET_DIR)/libmaybenot_ffi.a: src/*.rs Cargo.toml build.rs cbindgen.toml RUSTFLAGS="-C metadata=maybenot-ffi" ${CARGO} build $(CARGOFLAGS) clean: diff --git a/crates/maybenot-ffi/build.rs b/crates/maybenot-ffi/build.rs index bf46550..a7c6d7d 100644 --- a/crates/maybenot-ffi/build.rs +++ b/crates/maybenot-ffi/build.rs @@ -1,9 +1,36 @@ -use std::env; +use std::{ + env, + fs::File, + io::{self, Write}, + path::{Path, PathBuf}, +}; fn main() { + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + generate_c_header(&crate_dir); + generate_version_rs(&out_dir).expect("failed to generate version.rs"); +} + +fn generate_c_header(crate_dir: &str) { cbindgen::generate(&crate_dir) .expect("Unable to generate bindings") .write_to_file(format!("{crate_dir}/maybenot.h")); } + +/// Generate version.rs and embed a symbol containing the crate version +fn generate_version_rs(out_dir: &Path) -> io::Result<()> { + let version_rs_path = out_dir.join("version.rs"); + let mut f = File::create(version_rs_path)?; + let pkg_version = env!("CARGO_PKG_VERSION"); + writeln!(&mut f, "#[no_mangle]")?; + writeln!( + &mut f, + r#"static MAYBENOT_FFI_VERSION: &core::ffi::CStr = c"maybenot-ffi/{pkg_version}";"#, + )?; + + println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); + + Ok(()) +} diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs index b9447df..87ccaa3 100644 --- a/crates/maybenot-ffi/src/lib.rs +++ b/crates/maybenot-ffi/src/lib.rs @@ -4,6 +4,9 @@ pub use error::*; mod ffi; pub use ffi::*; +// version.rs gets written at compile time by build.rs +include!(concat!(env!("OUT_DIR"), "/version.rs")); + use core::{mem::MaybeUninit, str::FromStr, time::Duration}; use std::time::Instant; From 433e897d0196dbe5613329bd34292c5230e22070 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 14 May 2024 16:48:41 +0200 Subject: [PATCH 05/20] Correctly expose crate version using a function instead of a static &CStr, which is not FFI safe --- crates/maybenot-ffi/build.rs | 25 +------------------------ crates/maybenot-ffi/maybenot.h | 7 +++++++ crates/maybenot-ffi/src/ffi.rs | 17 +++++++++++++++++ crates/maybenot-ffi/src/lib.rs | 3 --- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/crates/maybenot-ffi/build.rs b/crates/maybenot-ffi/build.rs index a7c6d7d..72b6b3d 100644 --- a/crates/maybenot-ffi/build.rs +++ b/crates/maybenot-ffi/build.rs @@ -1,16 +1,9 @@ -use std::{ - env, - fs::File, - io::{self, Write}, - path::{Path, PathBuf}, -}; +use std::env; fn main() { - let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); generate_c_header(&crate_dir); - generate_version_rs(&out_dir).expect("failed to generate version.rs"); } fn generate_c_header(crate_dir: &str) { @@ -18,19 +11,3 @@ fn generate_c_header(crate_dir: &str) { .expect("Unable to generate bindings") .write_to_file(format!("{crate_dir}/maybenot.h")); } - -/// Generate version.rs and embed a symbol containing the crate version -fn generate_version_rs(out_dir: &Path) -> io::Result<()> { - let version_rs_path = out_dir.join("version.rs"); - let mut f = File::create(version_rs_path)?; - let pkg_version = env!("CARGO_PKG_VERSION"); - writeln!(&mut f, "#[no_mangle]")?; - writeln!( - &mut f, - r#"static MAYBENOT_FFI_VERSION: &core::ffi::CStr = c"maybenot-ffi/{pkg_version}";"#, - )?; - - println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); - - Ok(()) -} diff --git a/crates/maybenot-ffi/maybenot.h b/crates/maybenot-ffi/maybenot.h index 396e926..5e92f73 100644 --- a/crates/maybenot-ffi/maybenot.h +++ b/crates/maybenot-ffi/maybenot.h @@ -134,6 +134,13 @@ typedef struct MaybenotAction { }; } MaybenotAction; +/** + * Get the version of maybenot-ffi as a null terminated UTF-8-string. + * + * Example: `maybenot-ffi/1.0.1` + */ +const char *maybenot_version(void); + /** * Start a new [`MaybenotFramework`] instance. * diff --git a/crates/maybenot-ffi/src/ffi.rs b/crates/maybenot-ffi/src/ffi.rs index 0816df1..3196a6d 100644 --- a/crates/maybenot-ffi/src/ffi.rs +++ b/crates/maybenot-ffi/src/ffi.rs @@ -5,6 +5,23 @@ use core::{ slice::from_raw_parts_mut, }; +// NOTE: must be null-terminated. +static VERSION: &str = concat!("maybenot-ffi/", env!("CARGO_PKG_VERSION"), "\0"); + +/// Get the version of maybenot-ffi as a null terminated UTF-8-string. +/// +/// Example: `maybenot-ffi/1.0.1` +#[no_mangle] +pub extern "C" fn maybenot_version() -> *const c_char { + debug_assert_eq!( + VERSION.chars().last(), + Some('\0'), + "VERSION must be null terminated" + ); + + VERSION.as_ptr().cast() +} + /// Start a new [`MaybenotFramework`] instance. /// /// # Safety diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs index 87ccaa3..b9447df 100644 --- a/crates/maybenot-ffi/src/lib.rs +++ b/crates/maybenot-ffi/src/lib.rs @@ -4,9 +4,6 @@ pub use error::*; mod ffi; pub use ffi::*; -// version.rs gets written at compile time by build.rs -include!(concat!(env!("OUT_DIR"), "/version.rs")); - use core::{mem::MaybeUninit, str::FromStr, time::Duration}; use std::time::Instant; From 776dac957f26fdc3159e22fb7615247c540d2199 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 14 May 2024 16:49:40 +0200 Subject: [PATCH 06/20] Move mod statements below use --- crates/maybenot-ffi/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs index b9447df..3b06e67 100644 --- a/crates/maybenot-ffi/src/lib.rs +++ b/crates/maybenot-ffi/src/lib.rs @@ -1,9 +1,3 @@ -mod error; -pub use error::*; - -mod ffi; -pub use ffi::*; - use core::{mem::MaybeUninit, str::FromStr, time::Duration}; use std::time::Instant; @@ -12,6 +6,12 @@ use maybenot::{ machine::Machine, }; +mod error; +pub use error::*; + +mod ffi; +pub use ffi::*; + /// A running Maybenot instance. /// /// - Create it: [maybenot_start]. From 975ee0ae402f6036985ab7c9f8fc91e712606f1c Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 14 May 2024 17:08:20 +0200 Subject: [PATCH 07/20] Make public Rust API be same as public FFI API --- crates/maybenot-ffi/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs index 3b06e67..d931e8b 100644 --- a/crates/maybenot-ffi/src/lib.rs +++ b/crates/maybenot-ffi/src/lib.rs @@ -7,7 +7,8 @@ use maybenot::{ }; mod error; -pub use error::*; +use error::Error; +pub use error::MaybenotResult; mod ffi; pub use ffi::*; @@ -99,7 +100,7 @@ pub enum MaybenotAction { } impl MaybenotFramework { - pub fn start( + fn start( machines_str: &str, max_padding_bytes: f64, max_blocking_bytes: f64, @@ -126,7 +127,7 @@ impl MaybenotFramework { Ok(MaybenotFramework { framework }) } - pub fn on_event( + fn on_event( &mut self, event: MaybenotEvent, actions: &mut [MaybeUninit], From b56ccef9073aaff4035e34ae676d62fcba9b5273 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 15 May 2024 11:17:49 +0200 Subject: [PATCH 08/20] Pin cbindgen to avoid any format change surprises --- crates/maybenot-ffi/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/maybenot-ffi/Cargo.toml b/crates/maybenot-ffi/Cargo.toml index 546c026..7bdc7b1 100644 --- a/crates/maybenot-ffi/Cargo.toml +++ b/crates/maybenot-ffi/Cargo.toml @@ -17,4 +17,4 @@ maybenot = { version = "1.1.1", path = "../maybenot" } thiserror = "1.0" [build-dependencies] -cbindgen = { version = "0.26.0", default-features = false } +cbindgen = { version = "=0.26.0", default-features = false } From aa9aeab8d69315c7e4237d1e4a1b9bf7197f6f98 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 15 May 2024 13:04:15 +0200 Subject: [PATCH 09/20] Remove obsolete error type --- crates/maybenot-ffi/maybenot.h | 12 +++++++++ crates/maybenot-ffi/src/error.rs | 44 ++++++++------------------------ crates/maybenot-ffi/src/lib.rs | 7 +++-- 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/crates/maybenot-ffi/maybenot.h b/crates/maybenot-ffi/maybenot.h index 5e92f73..b4cf2c4 100644 --- a/crates/maybenot-ffi/maybenot.h +++ b/crates/maybenot-ffi/maybenot.h @@ -35,9 +35,21 @@ enum MaybenotResult { * Operation completed successfully */ MaybenotResult_Ok = 0, + /** + * The machine string wasn't valid UTF-8 + */ MaybenotResult_MachineStringNotUtf8 = 1, + /** + * Failed to parse machine string + */ MaybenotResult_InvalidMachineString = 2, + /** + * Failed to start framework + */ MaybenotResult_StartFramework = 3, + /** + * A null pointer was encountered + */ MaybenotResult_NullPointer = 4, }; typedef uint32_t MaybenotResult; diff --git a/crates/maybenot-ffi/src/error.rs b/crates/maybenot-ffi/src/error.rs index da54d05..1f0deea 100644 --- a/crates/maybenot-ffi/src/error.rs +++ b/crates/maybenot-ffi/src/error.rs @@ -1,47 +1,25 @@ -use std::str::Utf8Error; - /// An FFI friendly result error code type. #[repr(u32)] #[derive(Debug, Clone, Copy)] pub enum MaybenotResult { /// Operation completed successfully Ok = 0, - MachineStringNotUtf8 = 1, - InvalidMachineString = 2, - StartFramework = 3, - NullPointer = 4, -} - -#[derive(Clone, Debug, thiserror::Error)] -pub enum Error { - #[error("The machine string wasn't valid UTF-8")] - MachineStringNotUtf8(#[source] Utf8Error), - #[error("Failed to parse machine string")] - InvalidMachineString, + /// The machine string wasn't valid UTF-8 + MachineStringNotUtf8 = 1, - #[error("Failed to start framework")] - StartFramework, + /// Failed to parse machine string + InvalidMachineString = 2, - #[error("A null pointer was encountered")] - NullPointer, -} + /// Failed to start framework + StartFramework = 3, -impl From for MaybenotResult { - fn from(error: Error) -> Self { - match error { - Error::MachineStringNotUtf8(_) => MaybenotResult::MachineStringNotUtf8, - Error::InvalidMachineString => MaybenotResult::InvalidMachineString, - Error::StartFramework => MaybenotResult::StartFramework, - Error::NullPointer => MaybenotResult::NullPointer, - } - } + /// A null pointer was encountered + NullPointer = 4, } -impl From> for MaybenotResult { - fn from(result: Result) -> Self { - result - .map(|_| MaybenotResult::Ok) - .unwrap_or_else(MaybenotResult::from) +impl From> for MaybenotResult { + fn from(result: Result) -> Self { + result.map(|_| MaybenotResult::Ok).unwrap_or_else(|err| err) } } diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs index d931e8b..d6c7498 100644 --- a/crates/maybenot-ffi/src/lib.rs +++ b/crates/maybenot-ffi/src/lib.rs @@ -7,7 +7,6 @@ use maybenot::{ }; mod error; -use error::Error; pub use error::MaybenotResult; mod ffi; @@ -105,7 +104,7 @@ impl MaybenotFramework { max_padding_bytes: f64, max_blocking_bytes: f64, mtu: u16, - ) -> Result { + ) -> Result { let machines: Vec<_> = machines_str .lines() .map(|line| line.trim()) @@ -113,7 +112,7 @@ impl MaybenotFramework { .filter(|line| !line.starts_with('#')) .map(Machine::from_str) .collect::>() - .map_err(|_e| Error::InvalidMachineString)?; + .map_err(|_e| MaybenotResult::InvalidMachineString)?; let framework = Framework::new( machines, @@ -122,7 +121,7 @@ impl MaybenotFramework { mtu, Instant::now(), ) - .map_err(|_e| Error::StartFramework)?; + .map_err(|_e| MaybenotResult::StartFramework)?; Ok(MaybenotFramework { framework }) } From 1365ba0fc5a0df6b50e4da3cbc48a912ffc7c8dc Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 15 May 2024 13:08:11 +0200 Subject: [PATCH 10/20] Remove thiserror --- crates/maybenot-ffi/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/maybenot-ffi/Cargo.toml b/crates/maybenot-ffi/Cargo.toml index 7bdc7b1..7c7297e 100644 --- a/crates/maybenot-ffi/Cargo.toml +++ b/crates/maybenot-ffi/Cargo.toml @@ -14,7 +14,6 @@ crate-type = ["lib", "staticlib", "cdylib"] [dependencies] maybenot = { version = "1.1.1", path = "../maybenot" } -thiserror = "1.0" [build-dependencies] cbindgen = { version = "=0.26.0", default-features = false } From 096d2c05f234fef34eeac0c7ec6e93f011ec57a8 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 15 May 2024 13:28:59 +0200 Subject: [PATCH 11/20] Remove default cbindgen.toml options --- crates/maybenot-ffi/cbindgen.toml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/crates/maybenot-ffi/cbindgen.toml b/crates/maybenot-ffi/cbindgen.toml index fb5b98c..7ff27bf 100644 --- a/crates/maybenot-ffi/cbindgen.toml +++ b/crates/maybenot-ffi/cbindgen.toml @@ -2,18 +2,8 @@ language = "C" -# header = "/* Text to put at the beginning of the generated file. Probably a license. */" -# trailer = "/* Text to put at the end of the generated file */" autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" include_version = true -braces = "SameLine" -line_length = 100 -tab_width = 2 -documentation = true -documentation_style = "auto" -documentation_length = "full" -line_endings = "LF" # also "CR", "CRLF", "Native" - [enum] prefix_with_name = true From ae992664647d09df902c29896c5b6560f2311d7e Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 15 May 2024 13:32:59 +0200 Subject: [PATCH 12/20] Remove --locked --- crates/maybenot-ffi/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/maybenot-ffi/Makefile b/crates/maybenot-ffi/Makefile index 9918603..1ce4d4b 100644 --- a/crates/maybenot-ffi/Makefile +++ b/crates/maybenot-ffi/Makefile @@ -3,7 +3,6 @@ TARGET ?= PROFILE ?= release DESTINATION ?= . -CARGOFLAGS += --locked TARGET_DIR := ../../target/$(TARGET)/$(PROFILE) ifeq ($(PROFILE), release) From 566982db2e424a781091fa95fb1aff004d936f9e Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 15 May 2024 16:11:40 +0200 Subject: [PATCH 13/20] Respect CARGO_TARGET_DIR in makefile --- crates/maybenot-ffi/Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/maybenot-ffi/Makefile b/crates/maybenot-ffi/Makefile index 1ce4d4b..7381c3b 100644 --- a/crates/maybenot-ffi/Makefile +++ b/crates/maybenot-ffi/Makefile @@ -2,8 +2,10 @@ CARGO ?= cargo TARGET ?= PROFILE ?= release DESTINATION ?= . +CARGO_TARGET_DIR ?= ../../target -TARGET_DIR := ../../target/$(TARGET)/$(PROFILE) +CARGO_OUTPUT_DIR := $(CARGO_TARGET_DIR)/$(TARGET)/$(PROFILE) +CARGOFLAGS += --target-dir $(CARGO_TARGET_DIR) ifeq ($(PROFILE), release) CARGOFLAGS += --release @@ -17,13 +19,13 @@ endif .PHONY: clean # copy the library to the final destination, and strip the _ffi part -$(DESTINATION)/libmaybenot.a: $(TARGET_DIR)/libmaybenot_ffi.a +$(DESTINATION)/libmaybenot.a: $(CARGO_OUTPUT_DIR)/libmaybenot_ffi.a cp $^ $@ # build the static library -$(TARGET_DIR)/libmaybenot_ffi.a: src/*.rs Cargo.toml build.rs cbindgen.toml +$(CARGO_OUTPUT_DIR)/libmaybenot_ffi.a: src/*.rs Cargo.toml build.rs cbindgen.toml RUSTFLAGS="-C metadata=maybenot-ffi" ${CARGO} build $(CARGOFLAGS) clean: rm -f $(DESTINATION)/libmaybenot.a - cargo clean + ${CARGO} clean $(CARGOFLAGS) From 8a0f60a88435bae1a53874feb53c975c2166785a Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 16 May 2024 10:00:37 +0200 Subject: [PATCH 14/20] Commit Cargo.lock Also remove cbindgen version pin, since version pinning in Cargo.toml has problems when dealing with multiple versions of the some package in the dependency tree. --- .gitignore | 1 - Cargo.lock | 1022 ++++++++++++++++++++++++++++++++ crates/maybenot-ffi/Cargo.toml | 2 +- 3 files changed, 1023 insertions(+), 2 deletions(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index d78768a..0095668 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target -/Cargo.lock *.a diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7c877b4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1022 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cbindgen" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49" +dependencies = [ + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "libflate" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" +dependencies = [ + "core2", + "hashbrown 0.14.5", + "rle-decode-fast", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "maybenot" +version = "1.1.1" +dependencies = [ + "byteorder", + "hex", + "libflate", + "rand", + "rand_distr", + "ring", + "serde", + "simple-error", +] + +[[package]] +name = "maybenot-ffi" +version = "1.1.1" +dependencies = [ + "cbindgen", + "maybenot", +] + +[[package]] +name = "maybenot-simulator" +version = "1.1.1" +dependencies = [ + "env_logger 0.10.2", + "fastrand", + "log", + "maybenot", + "priority-queue", + "rand", + "serde", + "serde_json", + "test-log", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "priority-queue" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bda9164fe05bc9225752d54aae413343c36f684380005398a6a8fde95fe785" +dependencies = [ + "autocfg", + "indexmap", +] + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "simple-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8542b68b8800c3cda649d2c72d688b6907b30f1580043135d61669d4aad1c175" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger 0.11.3", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.63", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] diff --git a/crates/maybenot-ffi/Cargo.toml b/crates/maybenot-ffi/Cargo.toml index 7c7297e..3a07b96 100644 --- a/crates/maybenot-ffi/Cargo.toml +++ b/crates/maybenot-ffi/Cargo.toml @@ -16,4 +16,4 @@ crate-type = ["lib", "staticlib", "cdylib"] maybenot = { version = "1.1.1", path = "../maybenot" } [build-dependencies] -cbindgen = { version = "=0.26.0", default-features = false } +cbindgen = { version = "0.26.0", default-features = false } From 6ffacaa8a8939921fafcf5a90c694d160adb75cb Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 16 May 2024 10:35:51 +0200 Subject: [PATCH 15/20] Set ffi lib version to 1.0.0 --- Cargo.lock | 2 +- crates/maybenot-ffi/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c877b4..04731e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,7 +390,7 @@ dependencies = [ [[package]] name = "maybenot-ffi" -version = "1.1.1" +version = "1.0.0" dependencies = [ "cbindgen", "maybenot", diff --git a/crates/maybenot-ffi/Cargo.toml b/crates/maybenot-ffi/Cargo.toml index 3a07b96..f8452cf 100644 --- a/crates/maybenot-ffi/Cargo.toml +++ b/crates/maybenot-ffi/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "maybenot-ffi" description = "An FFI wrapper around Maybenot" -version = "1.1.1" +version = "1.0.0" edition.workspace = true license.workspace = true homepage.workspace = true From 995ec41db197c3f7588a2fa87a3b6c00a3edd19d Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 16 May 2024 10:42:56 +0200 Subject: [PATCH 16/20] Add maybenot-ffi to /README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6552c81..19b55c1 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ framework for creating defenses that hide such patterns. ## Workspace structure The Maybenot workspace consists of the following crates: - [maybenot](crates/maybenot): The core framework for creating defenses. +- [maybenot-ffi](crates/maybenot-ffi): A wrapper library around maybenot with a C FFI. - [maybenot-simulator](crates/maybenot-simulator): A simulator for testing defenses. From 48523fd114f077b013b9130c1b3265d5fb607841 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 16 May 2024 12:47:23 +0200 Subject: [PATCH 17/20] Tighten restriction on null bytes in version str --- crates/maybenot-ffi/src/ffi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/maybenot-ffi/src/ffi.rs b/crates/maybenot-ffi/src/ffi.rs index 3196a6d..2d01b1d 100644 --- a/crates/maybenot-ffi/src/ffi.rs +++ b/crates/maybenot-ffi/src/ffi.rs @@ -14,8 +14,8 @@ static VERSION: &str = concat!("maybenot-ffi/", env!("CARGO_PKG_VERSION"), "\0") #[no_mangle] pub extern "C" fn maybenot_version() -> *const c_char { debug_assert_eq!( - VERSION.chars().last(), - Some('\0'), + VERSION.find('\0'), + Some(VERSION.len() - 1), "VERSION must be null terminated" ); From 60a1930c921d0cb7e8b4049ce6eb1324cc5cd66b Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 16 May 2024 14:39:44 +0200 Subject: [PATCH 18/20] Remove machine string filtering & trimming in ffi --- crates/maybenot-ffi/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs index d6c7498..47510f7 100644 --- a/crates/maybenot-ffi/src/lib.rs +++ b/crates/maybenot-ffi/src/lib.rs @@ -107,9 +107,6 @@ impl MaybenotFramework { ) -> Result { let machines: Vec<_> = machines_str .lines() - .map(|line| line.trim()) - .filter(|line| !line.is_empty()) - .filter(|line| !line.starts_with('#')) .map(Machine::from_str) .collect::>() .map_err(|_e| MaybenotResult::InvalidMachineString)?; From b92588b653f2c2a3b9cc7098de53fe78a6410ce7 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 16 May 2024 15:29:51 +0200 Subject: [PATCH 19/20] More safety docs --- crates/maybenot-ffi/maybenot.h | 18 ++++++++++-------- crates/maybenot-ffi/src/ffi.rs | 22 ++++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/crates/maybenot-ffi/maybenot.h b/crates/maybenot-ffi/maybenot.h index b4cf2c4..0b8ddc4 100644 --- a/crates/maybenot-ffi/maybenot.h +++ b/crates/maybenot-ffi/maybenot.h @@ -158,7 +158,8 @@ const char *maybenot_version(void); * * # Safety * - `machines_str` must be a null-terminated UTF-8 string, containing LF-separated machines. - * - `out` must be a valid pointer to some valid pointer-sized memory. + * - `out` must be a valid pointer to some valid and aligned pointer-sized memory. + * - The pointer written to `out` is NOT safe to be used concurrently. */ MaybenotResult maybenot_start(const char *machines_str, double max_padding_bytes, @@ -170,8 +171,7 @@ MaybenotResult maybenot_start(const char *machines_str, * Get the number of machines running in the [`MaybenotFramework`] instance. * * # Safety - * - * `this` must be a valid pointer to a [`MaybenotFramework`] instance + * - `this` must have been created by [`maybenot_start`]. */ uint64_t maybenot_num_machines(struct MaybenotFramework *this_); @@ -179,7 +179,8 @@ uint64_t maybenot_num_machines(struct MaybenotFramework *this_); * Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. * * # Safety - * The pointer MUST have been created by [maybenot_start]. + * - `this` MUST have been created by [`maybenot_start`]. + * - `this` MUST NOT be used after it has been passed to [`maybenot_stop`]. */ void maybenot_stop(struct MaybenotFramework *this_); @@ -187,12 +188,13 @@ void maybenot_stop(struct MaybenotFramework *this_); * Feed an event 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 - * `actions_out` must have capacity for [maybenot_num_machines] items of size - * `sizeof(MaybenotAction)` bytes. - * - * The number of actions will be written to `num_actions_out`. + * - `this` MUST have been created by [`maybenot_start`]. + * - `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, diff --git a/crates/maybenot-ffi/src/ffi.rs b/crates/maybenot-ffi/src/ffi.rs index 2d01b1d..d7b34b8 100644 --- a/crates/maybenot-ffi/src/ffi.rs +++ b/crates/maybenot-ffi/src/ffi.rs @@ -26,7 +26,8 @@ pub extern "C" fn maybenot_version() -> *const c_char { /// /// # Safety /// - `machines_str` must be a null-terminated UTF-8 string, containing LF-separated machines. -/// - `out` must be a valid pointer to some valid pointer-sized memory. +/// - `out` must be a valid pointer to some valid and aligned pointer-sized memory. +/// - The pointer written to `out` is NOT safe to be used concurrently. #[no_mangle] pub unsafe extern "C" fn maybenot_start( machines_str: *const c_char, @@ -57,8 +58,7 @@ pub unsafe extern "C" fn maybenot_start( /// Get the number of machines running in the [`MaybenotFramework`] instance. /// /// # Safety -/// -/// `this` must be a valid pointer to a [`MaybenotFramework`] instance +/// - `this` must have been created by [`maybenot_start`]. #[no_mangle] pub unsafe extern "C" fn maybenot_num_machines(this: *mut MaybenotFramework) -> u64 { let Some(this) = (unsafe { this.as_mut() }) else { @@ -71,7 +71,8 @@ pub unsafe extern "C" fn maybenot_num_machines(this: *mut MaybenotFramework) -> /// Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. /// /// # Safety -/// The pointer MUST have been created by [maybenot_start]. +/// - `this` MUST have been created by [`maybenot_start`]. +/// - `this` MUST NOT be used after it has been passed to [`maybenot_stop`]. #[no_mangle] pub unsafe extern "C" fn maybenot_stop(this: *mut MaybenotFramework) { // Reconstruct the Box and drop it. @@ -82,12 +83,13 @@ pub unsafe extern "C" fn maybenot_stop(this: *mut MaybenotFramework) { /// Feed an event 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 -/// `actions_out` must have capacity for [maybenot_num_machines] items of size -/// `sizeof(MaybenotAction)` bytes. -/// -/// The number of actions will be written to `num_actions_out`. +/// - `this` MUST have been created by [`maybenot_start`]. +/// - `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, @@ -99,6 +101,10 @@ pub unsafe extern "C" fn maybenot_on_event( return MaybenotResult::NullPointer; }; + if num_actions_out.is_null() { + return MaybenotResult::NullPointer; + } + // 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 From 9dc03fb7b6962b73c800fe308a7182a590f357d1 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 16 May 2024 15:54:57 +0200 Subject: [PATCH 20/20] Change on_event ffi func to accept multiple events --- crates/maybenot-ffi/maybenot.h | 14 ++++++++------ crates/maybenot-ffi/src/ffi.rs | 23 +++++++++++++++-------- crates/maybenot-ffi/src/lib.rs | 27 ++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/crates/maybenot-ffi/maybenot.h b/crates/maybenot-ffi/maybenot.h index 0b8ddc4..b802ac2 100644 --- a/crates/maybenot-ffi/maybenot.h +++ b/crates/maybenot-ffi/maybenot.h @@ -173,7 +173,7 @@ MaybenotResult maybenot_start(const char *machines_str, * # Safety * - `this` must have been created by [`maybenot_start`]. */ -uint64_t maybenot_num_machines(struct MaybenotFramework *this_); +uintptr_t maybenot_num_machines(struct MaybenotFramework *this_); /** * Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. @@ -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, - struct MaybenotAction *actions_out, - uint64_t *num_actions_out); +MaybenotResult maybenot_on_events(struct MaybenotFramework *this_, + const struct MaybenotEvent *events, + uintptr_t num_events, + struct MaybenotAction *actions_out, + uintptr_t *num_actions_out); diff --git a/crates/maybenot-ffi/src/ffi.rs b/crates/maybenot-ffi/src/ffi.rs index d7b34b8..5c0fcc0 100644 --- a/crates/maybenot-ffi/src/ffi.rs +++ b/crates/maybenot-ffi/src/ffi.rs @@ -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"); @@ -60,12 +61,12 @@ pub unsafe extern "C" fn maybenot_start( /// # Safety /// - `this` must have been created by [`maybenot_start`]. #[no_mangle] -pub unsafe extern "C" fn maybenot_num_machines(this: *mut MaybenotFramework) -> u64 { +pub unsafe extern "C" fn maybenot_num_machines(this: *mut MaybenotFramework) -> usize { let Some(this) = (unsafe { this.as_mut() }) else { return 0; }; - this.framework.num_machines() as u64 + this.framework.num_machines() } /// Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. @@ -80,31 +81,37 @@ 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( +pub unsafe extern "C" fn maybenot_on_events( this: *mut MaybenotFramework, - event: MaybenotEvent, + events: *const MaybenotEvent, + num_events: usize, actions_out: *mut MaybeUninit, - 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 @@ -112,7 +119,7 @@ pub unsafe extern "C" fn maybenot_on_event( let actions: &mut [MaybeUninit] = 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 } diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs index 47510f7..648e102 100644 --- a/crates/maybenot-ffi/src/lib.rs +++ b/crates/maybenot-ffi/src/lib.rs @@ -19,10 +19,13 @@ pub use ffi::*; /// - Stop it: [maybenot_stop]. pub struct MaybenotFramework { framework: Framework>, + + /// A buffer used internally for converting from [MaybenotEvent]s. + events_buf: Vec, } #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct MaybenotEvent { pub event_type: MaybenotEventType, @@ -111,6 +114,8 @@ impl MaybenotFramework { .collect::>() .map_err(|_e| MaybenotResult::InvalidMachineString)?; + let machines_count = machines.len(); + let framework = Framework::new( machines, max_padding_bytes, @@ -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], ) -> 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();