dolphin 0.3.0

A lightweight and safe Rust FFI library for dynamically loading and invoking functions from shared libraries with support for return values
Documentation
# ๐Ÿฌ Dolphin

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.

## Features

- ๐Ÿš€ **Dynamic Loading** - Load functions from any shared library at runtime
- ๐Ÿ”’ **Safe API** - Wraps unsafe FFI calls in a safe, ergonomic interface
- โšก **Performance** - Pre-load function addresses for zero-overhead repeated calls
- ๐ŸŽฏ **Flexible** - Pass any data type including primitives, strings, and complex structs
- ๐Ÿ”„ **Return Values** - Call functions that return values (integers, floats, structs, etc.)
- ๐ŸŒ **Cross-Platform** - Works on Windows (`.dll`), Linux (`.so`), and macOS (`.dylib`)

## Installation

Add Dolphin to your `Cargo.toml`:

```toml
[dependencies]
dolphin = "0.3.0"
```

## Quick Start

### Simple Usage

```rust
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");
}
```

### Functions with Return Values

```rust
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
}
```

### High-Performance Pattern (Pre-loading)

For repeated calls, pre-load the function once and reuse the address:

```rust
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:

```rust
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);
    }
}
```

### Working with Structs

```rust
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");
}
```

## API Overview

### Core Functions

#### For Void Functions (no return value):

- **`load(library_path, function_name)`** - Load a function and return its address
  - Returns: `Option<usize>` - The function address if found
  
- **`invoke(address, arguments)`** - Call a pre-loaded function
  - Returns: `Result<(), String>` - Success or error message
  
- **`load_and_invoke(library_path, function_name, arguments)`** - Load and call in one step
  - Returns: `Result<(), String>` - Success or error message

#### For Functions with Return Values:

- **`load_with_return::<R>(library_path, function_name)`** - Load a function that returns type `R`
  - Returns: `Result<usize, String>` - The function address or error message
  
- **`invoke_with_return::<T, R>(address, arguments)`** - Call a pre-loaded function that returns type `R`
  - Returns: `Result<R, String>` - The return value or error message
  
- **`load_and_invoke_with_return::<T, R>(library_path, function_name, arguments)`** - Load and call in one step
  - Returns: `Result<R, String>` - The return value or error message

## C Function Requirements

### Void Functions

C functions that don't return values must follow this signature:

```c
void your_function(const uint8_t* data, size_t len) {
    // Your implementation
}
```

### Functions with Return Values

C functions that return values must follow this signature:

```c
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.

### Example C Library

```c
#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:
```bash
# 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
```

## Examples

The repository includes comprehensive examples:

```bash
# 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](examples/CSHARP.md) for documentation on C# interop via NativeAOT (advanced).

## Use Cases

- **Plugin Systems** - Dynamically load and execute plugins at runtime
- **C Library Integration** - Call C libraries without compile-time linking
- **C# Interop** - Call .NET/C# functions via NativeAOT (see examples/CSHARP.md for details)
- **Hot Reloading** - Reload functions without restarting your application
- **Language Interop** - Bridge Rust with C, C++, or any C-compatible library
- **Legacy Code** - Interface with existing native libraries

## Performance

Dolphin is designed for performance:

- **Zero overhead** for pre-loaded functions
- **No runtime dependencies** beyond `libloading`
- **Minimal allocations** - Direct memory operations
- **Efficient** - Function addresses are simple integers

Benchmark comparison:
- `load_and_invoke`: ~1-2ยตs per call (includes library loading)
- `load` + `invoke`: ~50-100ns per call (pre-loaded)

## Safety

While FFI is inherently unsafe, Dolphin provides guardrails:

- โœ… Validates function addresses before calling
- โœ… Returns `Result` types for error handling
- โœ… Safe wrapper API around unsafe operations
- โš ๏ธ User must ensure correct function signatures
- โš ๏ธ User must ensure data layout matches C expectations

## Platform Support

| Platform | Library Format | Tested |
|----------|---------------|---------|
| macOS    | `.dylib`      | โœ…      |
| Linux    | `.so`         | โœ…      |
| Windows  | `.dll`        | โœ…      |

## Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

## License

This project is licensed under the MIT License - see the [LICENSE](../LICENSE) file for details.

## Acknowledgments

Built with:
- [`libloading`]https://crates.io/crates/libloading - Cross-platform dynamic library loading

## Links

- **Repository**: https://github.com/Logan-Garrett/dolphin
- **Documentation**: https://docs.rs/dolphin
- **Crates.io**: https://crates.io/crates/dolphin

---

Made with ๐Ÿฌ by Logan Garrett 

### Testing
cargo test                    # Run basic tests

cargo test -- --ignored       # Run integration tests (requires gcc)

cargo test -- --include-ignored  # Run all tests