1use dolphin::{load, invoke};
2use std::collections::HashMap;
3
4#[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
29struct FunctionRegistry {
31 functions: HashMap<String, usize>,
32}
33
34impl FunctionRegistry {
35 fn new() -> Self {
36 FunctionRegistry {
37 functions: HashMap::new(),
38 }
39 }
40
41 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 fn get(&self, function_name: &str) -> Option<usize> {
57 self.functions.get(function_name).copied()
58 }
59
60 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 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 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 println!("\n--- Phase 2: Using Pre-loaded Functions ---\n");
122
123 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 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 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 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 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 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 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}