| Crates.io | dolphin |
| lib.rs | dolphin |
| version | 0.3.0 |
| created_at | 2025-08-09 02:55:19.576158+00 |
| updated_at | 2025-11-26 03:03:58.509511+00 |
| description | A lightweight and safe Rust FFI library for dynamically loading and invoking functions from shared libraries with support for return values |
| homepage | https://github.com/Logan-Garrett/dolphin |
| repository | https://github.com/Logan-Garrett/dolphin |
| max_upload_size | |
| id | 1787532 |
| size | 146,725 |
A lightweight and safe Rust FFI library for dynamically loading and invoking functions from shared libraries (.dll, .so, .dylib). Dolphin provides a simple interface to call native C functions from any compiled library at runtime.
.dll), Linux (.so), and macOS (.dylib)Add Dolphin to your Cargo.toml:
[dependencies]
dolphin = "0.3.0"
use dolphin::load_and_invoke;
fn main() {
// Call a C function with a string
let message = "Hello from Rust!";
load_and_invoke("./mylib.dylib", "print_message", message.as_bytes())
.expect("Failed to call function");
// Call with integer array
let numbers = vec![10, 20, 30, 40, 50];
load_and_invoke("./mylib.dylib", "calculate_sum", &numbers)
.expect("Failed to call function");
}
use dolphin::load_and_invoke_with_return;
fn main() {
// Call a function that returns an integer
let numbers = vec![10, 20, 30];
let sum: i32 = load_and_invoke_with_return("./mylib.dylib", "sum_integers", &numbers)
.expect("Failed to call function");
println!("Sum: {}", sum); // Prints: Sum: 60
// Call a function that returns a double
let values = vec![100.0, 200.0, 300.0];
let avg: f64 = load_and_invoke_with_return("./mylib.dylib", "calculate_average", &values)
.expect("Failed to call function");
println!("Average: {}", avg); // Prints: Average: 200.0
}
For repeated calls, pre-load the function once and reuse the address:
use dolphin::{load, invoke};
fn main() {
// Load once
let print_addr = load("./mylib.dylib", "print_message")
.expect("Failed to load function");
// Invoke many times with zero loading overhead
for i in 0..1000 {
let msg = format!("Message {}", i);
invoke(print_addr, msg.as_bytes()).ok();
}
}
Pre-loading also works with return values:
use dolphin::{load_with_return, invoke_with_return};
fn main() {
// Load once
let sum_addr = load_with_return::<i32>("./mylib.dylib", "sum_integers")
.expect("Failed to load function");
// Invoke many times (1000x faster than repeated loading)
for i in 0..1000 {
let numbers = vec![i, i * 2, i * 3];
let result: i32 = invoke_with_return(sum_addr, &numbers)
.expect("Failed to invoke");
println!("Sum: {}", result);
}
}
use dolphin::load_and_invoke;
#[repr(C)]
struct User {
id: i32,
name: [u8; 64],
age: i32,
balance: f64,
}
fn main() {
let user = User {
id: 1,
name: [0; 64], // Initialize with your data
age: 30,
balance: 1000.0,
};
let user_bytes = unsafe {
std::slice::from_raw_parts(
&user as *const User as *const u8,
std::mem::size_of::<User>()
)
};
load_and_invoke("./mylib.dylib", "process_user", user_bytes)
.expect("Failed to process user");
}
load(library_path, function_name) - Load a function and return its address
Option<usize> - The function address if foundinvoke(address, arguments) - Call a pre-loaded function
Result<(), String> - Success or error messageload_and_invoke(library_path, function_name, arguments) - Load and call in one step
Result<(), String> - Success or error messageload_with_return::<R>(library_path, function_name) - Load a function that returns type R
Result<usize, String> - The function address or error messageinvoke_with_return::<T, R>(address, arguments) - Call a pre-loaded function that returns type R
Result<R, String> - The return value or error messageload_and_invoke_with_return::<T, R>(library_path, function_name, arguments) - Load and call in one step
Result<R, String> - The return value or error messageC functions that don't return values must follow this signature:
void your_function(const uint8_t* data, size_t len) {
// Your implementation
}
C functions that return values must follow this signature:
ReturnType your_function(const uint8_t* data, size_t len) {
// Your implementation
return value;
}
Where ReturnType can be any C type (int, double, struct, etc.).
The data pointer contains the serialized arguments, and len is the byte count.
#include <stdio.h>
#include <stdint.h>
// Void function (no return)
void print_message(const uint8_t* data, size_t len) {
printf("Message: %.*s\n", (int)len, (char*)data);
}
// Function returning an integer
int32_t sum_integers(const uint8_t* data, size_t len) {
int count = len / sizeof(int32_t);
const int32_t* numbers = (const int32_t*)data;
int32_t sum = 0;
for (int i = 0; i < count; i++) {
sum += numbers[i];
}
return sum;
}
// Function returning a double
double calculate_average(const uint8_t* data, size_t len) {
int count = len / sizeof(double);
const double* values = (const double*)data;
double sum = 0.0;
for (int i = 0; i < count; i++) {
sum += values[i];
}
return count > 0 ? sum / count : 0.0;
}
Compile it:
# macOS
gcc -shared -fPIC mylib.c -o libmylib.dylib
# Linux
gcc -shared -fPIC mylib.c -o libmylib.so
# Windows
gcc -shared mylib.c -o mylib.dll
The repository includes comprehensive examples:
# Clone the repository
git clone https://github.com/Logan-Garrett/dolphin.git
cd dolphin/dolphin
# Build C examples
cd examples && make && cd ..
# Run examples
cargo run --example usage_example # Basic usage patterns
cargo run --example preload_example # Pre-loading pattern
cargo run --example return_values_example # Return value examples
C# Interop: See examples/CSHARP.md for documentation on C# interop via NativeAOT (advanced).
Dolphin is designed for performance:
libloadingBenchmark comparison:
load_and_invoke: ~1-2ยตs per call (includes library loading)load + invoke: ~50-100ns per call (pre-loaded)While FFI is inherently unsafe, Dolphin provides guardrails:
Result types for error handling| Platform | Library Format | Tested |
|---|---|---|
| macOS | .dylib |
โ |
| Linux | .so |
โ |
| Windows | .dll |
โ |
Contributions are welcome! Please feel free to submit issues or pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.
Built with:
libloading - Cross-platform dynamic library loadingMade with ๐ฌ by Logan Garrett
cargo test # Run basic tests
cargo test -- --ignored # Run integration tests (requires gcc)
cargo test -- --include-ignored # Run all tests