1use std::mem::ManuallyDrop;
11
12#[repr(C)]
16pub struct Buf {
17 pub ptr: *mut u8,
18 pub len: usize,
19 pub cap: usize,
20}
21
22impl Buf {
23 pub const fn empty() -> Self {
25 Buf {
26 ptr: std::ptr::null_mut(),
27 len: 0,
28 cap: 0,
29 }
30 }
31}
32
33pub type HookFn = unsafe extern "C" fn(
36 input: *const u8,
37 input_len: usize,
38 ctx: *const u8,
39 ctx_len: usize,
40 out: *mut Buf,
41) -> i32;
42
43pub type FreeFn = unsafe extern "C" fn(ptr: *mut u8, len: usize, cap: usize);
45
46pub const FLAG_PANIC_ABORT: u16 = 1;
51
52pub const OK: i32 = 0;
54pub const ERR: i32 = 1;
56pub const PANICKED: i32 = 2;
58pub const UNCHANGED: i32 = 3;
60
61pub fn fill(out: &mut Buf, bytes: Vec<u8>) {
64 let mut v = ManuallyDrop::new(bytes);
65 out.ptr = v.as_mut_ptr();
66 out.len = v.len();
67 out.cap = v.capacity();
68}
69
70pub unsafe extern "C" fn free_buf(ptr: *mut u8, len: usize, cap: usize) {
77 if !ptr.is_null() {
78 unsafe { drop(Vec::from_raw_parts(ptr, len, cap)) };
79 }
80}
81
82pub fn panic_message(payload: &(dyn std::any::Any + Send)) -> String {
85 payload
86 .downcast_ref::<&str>()
87 .map(|s| s.to_string())
88 .or_else(|| payload.downcast_ref::<String>().cloned())
89 .unwrap_or_else(|| "non-string panic payload".to_string())
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn a_filled_buffer_round_trips_through_free() {
98 let mut buf = Buf::empty();
99 assert!(buf.ptr.is_null());
100 fill(&mut buf, b"payload".to_vec());
101 assert_eq!(buf.len, 7);
102 let copied = unsafe { std::slice::from_raw_parts(buf.ptr, buf.len) }.to_vec();
103 assert_eq!(copied, b"payload");
104 unsafe { free_buf(buf.ptr, buf.len, buf.cap) };
105 }
106
107 #[test]
108 fn freeing_a_null_buffer_is_a_no_op() {
109 unsafe { free_buf(std::ptr::null_mut(), 0, 0) };
110 }
111
112 #[test]
113 fn panic_payloads_report_their_text_or_say_they_have_none() {
114 let str_payload = std::panic::catch_unwind(|| panic!("literal")).unwrap_err();
115 assert_eq!(panic_message(str_payload.as_ref()), "literal");
116
117 let owned = std::panic::catch_unwind(|| panic!("{}", "formatted")).unwrap_err();
118 assert_eq!(panic_message(owned.as_ref()), "formatted");
119
120 let other = std::panic::catch_unwind(|| std::panic::panic_any(42usize)).unwrap_err();
121 assert_eq!(panic_message(other.as_ref()), "non-string panic payload");
122 }
123}