Rust · 2998 bytes Raw Blame History
1 //! Memory management — allocate, deallocate, string operations.
2 //!
3 //! All heap allocation goes through these functions so we can
4 //! track allocations, detect leaks, and implement Fortran's
5 //! automatic deallocation semantics.
6
7 use std::ptr;
8
9 // Use libc malloc/free directly so allocate/deallocate are paired correctly
10 // without needing to track Rust Layout. The system allocator on macOS returns
11 // 16-byte aligned pointers from malloc, satisfying our alignment requirement.
12 extern "C" {
13 fn malloc(size: usize) -> *mut u8;
14 fn free(ptr: *mut u8);
15 }
16
17 /// Allocate `size` bytes on the heap. Returns a pointer.
18 /// Aborts on allocation failure (Fortran ALLOCATE with no STAT=).
19 #[no_mangle]
20 pub extern "C" fn afs_allocate(size: i64) -> *mut u8 {
21 if size <= 0 {
22 return ptr::null_mut();
23 }
24 let ptr = unsafe { malloc(size as usize) };
25 if ptr.is_null() {
26 eprintln!("ALLOCATE: out of memory ({} bytes)", size);
27 std::process::exit(1);
28 }
29 ptr
30 }
31
32 /// Deallocate memory previously allocated by afs_allocate.
33 #[no_mangle]
34 pub extern "C" fn afs_deallocate(ptr: *mut u8) {
35 if ptr.is_null() {
36 return;
37 }
38 unsafe { free(ptr) };
39 }
40
41 /// Concatenate two strings. Returns a newly allocated string.
42 /// Caller is responsible for freeing the result.
43 #[no_mangle]
44 pub extern "C" fn afs_string_concat(a: *const u8, alen: i64, b: *const u8, blen: i64) -> *mut u8 {
45 let total = (alen + blen) as usize;
46 let result = afs_allocate(total as i64);
47 if !a.is_null() && alen > 0 {
48 unsafe { ptr::copy_nonoverlapping(a, result, alen as usize) };
49 }
50 if !b.is_null() && blen > 0 {
51 unsafe { ptr::copy_nonoverlapping(b, result.add(alen as usize), blen as usize) };
52 }
53 result
54 }
55
56 /// Copy a string into a fixed-length buffer, padding with spaces.
57 /// Used for character assignment to fixed-length variables.
58 #[no_mangle]
59 pub extern "C" fn afs_string_copy(dest: *mut u8, dest_len: i64, src: *const u8, src_len: i64) {
60 if dest.is_null() || dest_len <= 0 {
61 return;
62 }
63 let copy_len = std::cmp::min(src_len, dest_len) as usize;
64 if !src.is_null() && copy_len > 0 {
65 unsafe { ptr::copy_nonoverlapping(src, dest, copy_len) };
66 }
67 // Pad remainder with spaces (Fortran character assignment rule).
68 if (copy_len as i64) < dest_len {
69 unsafe {
70 ptr::write_bytes(dest.add(copy_len), b' ', (dest_len as usize) - copy_len);
71 }
72 }
73 }
74
75 /// Compare two strings lexicographically.
76 /// Returns negative, zero, or positive (like strcmp but for counted strings).
77 #[no_mangle]
78 pub extern "C" fn afs_string_compare(a: *const u8, alen: i64, b: *const u8, blen: i64) -> i32 {
79 let sa = if !a.is_null() && alen > 0 {
80 unsafe { std::slice::from_raw_parts(a, alen as usize) }
81 } else {
82 &[]
83 };
84 let sb = if !b.is_null() && blen > 0 {
85 unsafe { std::slice::from_raw_parts(b, blen as usize) }
86 } else {
87 &[]
88 };
89 sa.cmp(sb) as i32
90 }
91