usage_example/
usage_example.rs

1use dolphin::{load_and_invoke, load, invoke};
2
3// Define models matching C structs
4#[repr(C)]
5struct User {
6    id: i32,
7    name: [u8; 64],
8    age: i32,
9    balance: f64,
10}
11
12impl User {
13    fn new(id: i32, name: &str, age: i32, balance: f64) -> Self {
14        let mut name_buf = [0u8; 64];
15        let name_bytes = name.as_bytes();
16        let len = name_bytes.len().min(63); // Leave room for null terminator
17        name_buf[..len].copy_from_slice(&name_bytes[..len]);
18        
19        User {
20            id,
21            name: name_buf,
22            age,
23            balance,
24        }
25    }
26}
27
28#[repr(C)]
29struct Product {
30    product_id: i32,
31    name: [u8; 100],
32    price: f64,
33    quantity: i32,
34}
35
36impl Product {
37    fn new(product_id: i32, name: &str, price: f64, quantity: i32) -> Self {
38        let mut name_buf = [0u8; 100];
39        let name_bytes = name.as_bytes();
40        let len = name_bytes.len().min(99);
41        name_buf[..len].copy_from_slice(&name_bytes[..len]);
42        
43        Product {
44            product_id,
45            name: name_buf,
46            price,
47            quantity,
48        }
49    }
50}
51
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}