1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::{
borrow::Cow,
cell::RefCell,
ffi::{CStr, CString},
os::raw::{c_char, c_int},
slice,
};
static EMPTY_STRING: c_char = 0;
thread_local! {
static RETURN_STRING: RefCell<CString> = RefCell::new(CString::default());
}
pub unsafe fn parse_args<'a>(argc: c_int, argv: *const *const c_char) -> Vec<Cow<'a, str>> {
slice::from_raw_parts(argv, argc as usize)
.iter()
.map(|ptr| CStr::from_ptr(*ptr))
.map(|cstr| cstr.to_string_lossy())
.collect()
}
pub fn byond_return(value: Option<Vec<u8>>) -> *const c_char {
match value {
None => &EMPTY_STRING,
Some(vec) if vec.is_empty() => &EMPTY_STRING,
Some(vec) => RETURN_STRING.with(|cell| {
let cstring = match CString::new(vec) {
Ok(s) => s,
Err(e) => {
let (pos, mut vec) = (e.nul_position(), e.into_vec());
vec.truncate(pos);
CString::new(vec).unwrap_or_default()
}
};
cell.replace(cstring);
cell.borrow().as_ptr()
}),
}
}
#[macro_export]
macro_rules! byond_ffi_fn {
($name:ident() $body:block) => {
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
extern "C" fn $name(
_argc: ::std::os::raw::c_int, _argv: *const *const ::std::os::raw::c_char
) -> *const ::std::os::raw::c_char {
let closure = || ($body);
$crate::byond_ffi::byond_return(closure().map(From::from))
}
};
($name:ident($($arg:ident),* $(, ...$rest:ident)?) $body:block) => {
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
extern "C" fn $name(
_argc: ::std::os::raw::c_int, _argv: *const *const ::std::os::raw::c_char
) -> *const ::std::os::raw::c_char {
let __args;
unsafe {
__args = $crate::byond_ffi::parse_args(_argc, _argv);
}
let mut __argn = 0;
$(
let $arg: &str = __args.get(__argn).map_or("", |cow| &*cow);
__argn += 1;
)*
$(
let $rest = __args.get(__argn..).unwrap_or(&[]);
)?
let closure = || ($body);
$crate::byond_ffi::byond_return(closure().map(From::from))
}
};
}