invoke

Function invoke 

Source
pub fn invoke(address: usize, arguments: &[u8]) -> Result<(), String>
Expand description

Invokes a pre-loaded function using its memory address.

This function calls a C function that has been previously loaded with load. Use this for better performance when calling the same function multiple times.

§Arguments

  • address - Memory address of the function (obtained from load)
  • arguments - Byte slice containing the serialized arguments

§Returns

  • Ok(()) - Function was successfully invoked
  • Err(String) - Error message (e.g., invalid address)

§Examples

use dolphin::{load, invoke};

// Load once
let addr = load("./mylib.dylib", "print_message").unwrap();

// Invoke multiple times
for i in 0..100 {
    let msg = format!("Call {}", i);
    invoke(addr, msg.as_bytes())?;
}

§Safety

This is a safe wrapper that validates the address before calling. However, you must ensure:

  • The address is valid and was obtained from load
  • The function is still loaded in memory
  • The arguments match the C function’s expectations
Examples found in repository?
examples/preload_example.rs (line 72)
61    fn invoke<T>(&self, function_name: &str, arguments: &[T]) -> Result<(), String> {
62        let address = self.get(function_name)
63            .ok_or_else(|| format!("Function '{}' not found in registry", function_name))?;
64        
65        let arg_bytes = unsafe {
66            std::slice::from_raw_parts(
67                arguments.as_ptr() as *const u8,
68                arguments.len() * std::mem::size_of::<T>()
69            )
70        };
71        
72        invoke(address, arg_bytes)
73    }
74    
75    /// List all registered functions
76    fn list_functions(&self) {
77        println!("\nRegistered Functions:");
78        println!("{:-<60}", "");
79        for (name, address) in &self.functions {
80            println!("  {} -> 0x{:x}", name, address);
81        }
82        println!("{:-<60}", "");
83    }
84}
85
86fn main() {
87    println!("=== Dolphin Pre-Load Example ===\n");
88    println!("This example demonstrates loading all function addresses");
89    println!("at startup for better performance on repeated calls.\n");
90    
91    let library_path = "./examples/libexample.dylib";
92    let mut registry = FunctionRegistry::new();
93    
94    // Phase 1: Pre-load all functions at startup
95    println!("--- Phase 1: Pre-loading Functions ---");
96    
97    let functions = vec![
98        "calculate_sum",
99        "print_message",
100        "reverse_string",
101        "count_characters",
102        "print_user",
103        "update_user_balance",
104        "print_product",
105        "process_order",
106        "print_user_list",
107    ];
108    
109    let mut loaded_count = 0;
110    for func_name in &functions {
111        match registry.register(library_path, func_name) {
112            Ok(_) => loaded_count += 1,
113            Err(e) => eprintln!("✗ {}", e),
114        }
115    }
116    
117    println!("\n✓ Successfully loaded {}/{} functions\n", loaded_count, functions.len());
118    registry.list_functions();
119    
120    // Phase 2: Use pre-loaded functions (faster - no library loading overhead)
121    println!("\n--- Phase 2: Using Pre-loaded Functions ---\n");
122    
123    // Example 1: Calculate sum
124    println!("Example 1: Calculate Sum");
125    let numbers = vec![5, 10, 15, 20, 25];
126    match registry.invoke("calculate_sum", &numbers) {
127        Ok(_) => println!("✓ Invoked calculate_sum\n"),
128        Err(e) => eprintln!("✗ Error: {}\n", e),
129    }
130    
131    // Example 2: Print message
132    println!("Example 2: Print Message");
133    let message = "Pre-loaded function call!";
134    match registry.invoke("print_message", message.as_bytes()) {
135        Ok(_) => println!("✓ Invoked print_message\n"),
136        Err(e) => eprintln!("✗ Error: {}\n", e),
137    }
138    
139    // Example 3: Reverse string
140    println!("Example 3: Reverse String");
141    let text = "Fast Pre-loaded Call";
142    match registry.invoke("reverse_string", text.as_bytes()) {
143        Ok(_) => println!("✓ Invoked reverse_string\n"),
144        Err(e) => eprintln!("✗ Error: {}\n", e),
145    }
146    
147    // Example 4: User model
148    println!("Example 4: User Model");
149    let user = User::new(42, "John Doe", 30, 5000.00);
150    let user_bytes = unsafe {
151        std::slice::from_raw_parts(
152            &user as *const User as *const u8,
153            std::mem::size_of::<User>()
154        )
155    };
156    match registry.invoke("print_user", user_bytes) {
157        Ok(_) => println!("✓ Invoked print_user\n"),
158        Err(e) => eprintln!("✗ Error: {}\n", e),
159    }
160    
161    // Example 5: Multiple calls to same function (demonstrating performance benefit)
162    println!("Example 5: Multiple Calls (demonstrating pre-load benefit)");
163    let messages = vec![
164        "First call",
165        "Second call",
166        "Third call",
167        "Fourth call",
168    ];
169    
170    for (i, msg) in messages.iter().enumerate() {
171        match registry.invoke("print_message", msg.as_bytes()) {
172            Ok(_) => println!("  ✓ Call {} completed", i + 1),
173            Err(e) => eprintln!("  ✗ Call {} failed: {}", i + 1, e),
174        }
175    }
176    
177    // Example 6: Direct address usage (lowest level)
178    println!("\n--- Example 6: Direct Address Usage ---");
179    if let Some(address) = registry.get("print_message") {
180        println!("Using address 0x{:x} directly:", address);
181        let direct_msg = "Direct address call!";
182        match invoke(address, direct_msg.as_bytes()) {
183            Ok(_) => println!("✓ Direct invoke successful\n"),
184            Err(e) => eprintln!("✗ Direct invoke failed: {}\n", e),
185        }
186    }
187    
188    // Performance comparison note
189    println!("--- Performance Notes ---");
190    println!("Pre-loading benefits:");
191    println!("  • All functions loaded once at startup");
192    println!("  • No library loading overhead on each call");
193    println!("  • Ideal for applications making many FFI calls");
194    println!("  • Can validate all functions exist at startup");
195    println!("  • Functions remain loaded for entire program lifetime");
196    
197    println!("\n=== Example completed ===");
198}
More examples
Hide additional examples
examples/usage_example.rs (line 106)
52fn main() {
53    println!("=== Dolphin FFI Examples ===\n");
54    
55    // ========================================
56    // PART 1: Using load_and_invoke (convenience method)
57    // ========================================
58    println!("=== PART 1: Using load_and_invoke ===\n");
59    
60    // Example 1: Simple integer array
61    println!("--- Example 1: Calculate Sum ---");
62    let numbers = vec![10, 20, 30, 40, 50];
63    match load_and_invoke("./examples/libexample.dylib", "calculate_sum", &numbers) {
64        Ok(_) => println!("✓ Successfully invoked calculate_sum\n"),
65        Err(e) => eprintln!("✗ Error: {}\n", e),
66    }
67    
68    // Example 2: String operations
69    println!("--- Example 2: Print Message ---");
70    let message = "Hello from Rust!";
71    match load_and_invoke("./examples/libexample.dylib", "print_message", message.as_bytes()) {
72        Ok(_) => println!("✓ Successfully invoked print_message\n"),
73        Err(e) => eprintln!("✗ Error: {}\n", e),
74    }
75    
76    // Example 3: User model with load_and_invoke
77    println!("--- Example 3: Print User Model ---");
78    let user = User::new(1, "Alice Johnson", 28, 1500.50);
79    let user_bytes = unsafe {
80        std::slice::from_raw_parts(
81            &user as *const User as *const u8,
82            std::mem::size_of::<User>()
83        )
84    };
85    match load_and_invoke("./examples/libexample.dylib", "print_user", user_bytes) {
86        Ok(_) => println!("✓ Successfully invoked print_user\n"),
87        Err(e) => eprintln!("✗ Error: {}\n", e),
88    }
89    
90    // ========================================
91    // PART 2: Using load() then invoke() separately
92    // ========================================
93    println!("\n=== PART 2: Using load() + invoke() separately ===\n");
94    println!("This approach is better when you need to call the same function multiple times.\n");
95    
96    // Example 4: Load once, invoke multiple times
97    println!("--- Example 4: Load 'reverse_string' once, use multiple times ---");
98    match load("./examples/libexample.dylib", "reverse_string") {
99        Some(reverse_addr) => {
100            println!("✓ Loaded 'reverse_string' at address: 0x{:x}\n", reverse_addr);
101            
102            // Now invoke it multiple times with different strings
103            let strings = vec!["Dolphin", "FFI", "Library", "Rust"];
104            for (i, text) in strings.iter().enumerate() {
105                println!("  Call {}: Reversing '{}'", i + 1, text);
106                match invoke(reverse_addr, text.as_bytes()) {
107                    Ok(_) => println!("  ✓ Success\n"),
108                    Err(e) => eprintln!("  ✗ Error: {}\n", e),
109                }
110            }
111        }
112        None => eprintln!("✗ Failed to load 'reverse_string'\n"),
113    }
114    
115    // Example 5: Load multiple functions, then use them
116    println!("--- Example 5: Pre-load multiple functions ---");
117    let calc_sum_addr = load("./examples/libexample.dylib", "calculate_sum");
118    let print_msg_addr = load("./examples/libexample.dylib", "print_message");
119    let count_chars_addr = load("./examples/libexample.dylib", "count_characters");
120    
121    if calc_sum_addr.is_some() && print_msg_addr.is_some() && count_chars_addr.is_some() {
122        println!("✓ All functions loaded successfully:");
123        println!("  - calculate_sum: 0x{:x}", calc_sum_addr.unwrap());
124        println!("  - print_message: 0x{:x}", print_msg_addr.unwrap());
125        println!("  - count_characters: 0x{:x}\n", count_chars_addr.unwrap());
126        
127        // Use calculate_sum
128        println!("Using calculate_sum:");
129        let nums1 = vec![5, 10, 15];
130        let bytes1 = unsafe {
131            std::slice::from_raw_parts(
132                nums1.as_ptr() as *const u8,
133                nums1.len() * std::mem::size_of::<i32>()
134            )
135        };
136        invoke(calc_sum_addr.unwrap(), bytes1).ok();
137        
138        // Use print_message
139        println!("\nUsing print_message:");
140        let msg = "Pre-loaded function!";
141        invoke(print_msg_addr.unwrap(), msg.as_bytes()).ok();
142        
143        // Use count_characters
144        println!("\nUsing count_characters:");
145        let sample = "Rust FFI 2024";
146        invoke(count_chars_addr.unwrap(), sample.as_bytes()).ok();
147        
148        println!("\n✓ All pre-loaded functions executed\n");
149    } else {
150        eprintln!("✗ Failed to load one or more functions\n");
151    }
152    
153    // Example 6: Load and invoke with User model
154    println!("--- Example 6: User operations with load + invoke ---");
155    if let Some(print_user_addr) = load("./examples/libexample.dylib", "print_user") {
156        println!("✓ Loaded 'print_user' at 0x{:x}\n", print_user_addr);
157        
158        // Create multiple users and print them using same loaded function
159        let users_to_print = vec![
160            User::new(10, "John", 30, 1000.00),
161            User::new(20, "Jane", 25, 2500.50),
162            User::new(30, "Jake", 40, 5000.75),
163        ];
164        
165        for (i, user) in users_to_print.iter().enumerate() {
166            println!("Printing user {}:", i + 1);
167            let bytes = unsafe {
168                std::slice::from_raw_parts(
169                    user as *const User as *const u8,
170                    std::mem::size_of::<User>()
171                )
172            };
173            match invoke(print_user_addr, bytes) {
174                Ok(_) => println!("✓ User printed\n"),
175                Err(e) => eprintln!("✗ Error: {}\n", e),
176            }
177        }
178    } else {
179        eprintln!("✗ Failed to load 'print_user'\n");
180    }
181    
182    // Example 7: Load and invoke with Product model
183    println!("--- Example 7: Product processing with load + invoke ---");
184    if let Some(process_order_addr) = load("./examples/libexample.dylib", "process_order") {
185        println!("✓ Loaded 'process_order' at 0x{:x}\n", process_order_addr);
186        
187        // Process multiple orders
188        let orders = vec![
189            (Product::new(1, "Mouse", 29.99, 100), 5),
190            (Product::new(2, "Keyboard", 79.99, 50), 3),
191            (Product::new(3, "Monitor", 299.99, 20), 2),
192        ];
193        
194        for (i, (product, qty)) in orders.iter().enumerate() {
195            println!("Processing order {}:", i + 1);
196            let mut order_data = Vec::new();
197            order_data.extend_from_slice(unsafe {
198                std::slice::from_raw_parts(
199                    product as *const Product as *const u8,
200                    std::mem::size_of::<Product>()
201                )
202            });
203            order_data.extend_from_slice(unsafe {
204                std::slice::from_raw_parts(
205                    qty as *const i32 as *const u8,
206                    std::mem::size_of::<i32>()
207                )
208            });
209            
210            match invoke(process_order_addr, &order_data) {
211                Ok(_) => println!("✓ Order processed\n"),
212                Err(e) => eprintln!("✗ Error: {}\n", e),
213            }
214        }
215    } else {
216        eprintln!("✗ Failed to load 'process_order'\n");
217    }
218    
219    // ========================================
220    // PART 3: Performance comparison
221    // ========================================
222    println!("\n=== PART 3: Performance Demonstration ===\n");
223    println!("--- Calling same function 5 times ---\n");
224    
225    // Method 1: load_and_invoke (loads library each time)
226    println!("Method 1: Using load_and_invoke (loads each time)");
227    for i in 1..=5 {
228        let msg = format!("Message {}", i);
229        load_and_invoke("./examples/libexample.dylib", "print_message", msg.as_bytes()).ok();
230    }
231    
232    println!("\nMethod 2: Using load once + invoke many times (faster)");
233    if let Some(addr) = load("./examples/libexample.dylib", "print_message") {
234        for i in 1..=5 {
235            let msg = format!("Fast call {}", i);
236            invoke(addr, msg.as_bytes()).ok();
237        }
238    }
239    
240    println!("\n✓ Notice: Method 2 is more efficient for repeated calls!\n");
241    
242    println!("=== All examples completed ===");
243}