preload_example/
preload_example.rs

1use dolphin::{load, invoke};
2use std::collections::HashMap;
3
4// Define models matching C structs
5#[repr(C)]
6struct User {
7    id: i32,
8    name: [u8; 64],
9    age: i32,
10    balance: f64,
11}
12
13impl User {
14    fn new(id: i32, name: &str, age: i32, balance: f64) -> Self {
15        let mut name_buf = [0u8; 64];
16        let name_bytes = name.as_bytes();
17        let len = name_bytes.len().min(63);
18        name_buf[..len].copy_from_slice(&name_bytes[..len]);
19        
20        User {
21            id,
22            name: name_buf,
23            age,
24            balance,
25        }
26    }
27}
28
29/// Function registry for pre-loaded function addresses
30struct FunctionRegistry {
31    functions: HashMap<String, usize>,
32}
33
34impl FunctionRegistry {
35    fn new() -> Self {
36        FunctionRegistry {
37            functions: HashMap::new(),
38        }
39    }
40    
41    /// Pre-load a function and store its address
42    fn register(&mut self, library_path: &str, function_name: &str) -> Result<(), String> {
43        match load(library_path, function_name) {
44            Some(address) => {
45                println!("✓ Loaded '{}' at address: 0x{:x}", function_name, address);
46                self.functions.insert(function_name.to_string(), address);
47                Ok(())
48            }
49            None => {
50                Err(format!("Failed to load function '{}'", function_name))
51            }
52        }
53    }
54    
55    /// Get a pre-loaded function address
56    fn get(&self, function_name: &str) -> Option<usize> {
57        self.functions.get(function_name).copied()
58    }
59    
60    /// Invoke a pre-loaded function by name
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}