nice_plug/wrapper/
util.rs1use backtrace::Backtrace;
2use nice_plug_core::plugin::Plugin;
3use std::cmp;
4use std::marker::PhantomData;
5use std::os::raw::c_char;
6use std::sync::atomic::AtomicBool;
7
8use crate::util::permit_alloc;
9
10pub(crate) mod buffer_management;
11#[cfg(debug_assertions)]
12pub(crate) mod context_checks;
13
14#[cfg(all(not(miri), target_feature = "sse", feature = "unsafe_flush_denormals"))]
21const SSE_FTZ_BIT: u32 = 1 << 15;
22
23#[cfg(all(not(miri), target_arch = "aarch64", feature = "unsafe_flush_denormals"))]
28const AARCH64_FTZ_BIT: u64 = 1 << 24;
29
30#[cfg(all(debug_assertions, feature = "assert_process_allocs"))]
31#[global_allocator]
32static A: nice_assert_no_alloc::AllocDisabler = nice_assert_no_alloc::AllocDisabler;
33
34pub fn hash_param_id(id: &str) -> u32 {
36 let mut hash: u32 = 0;
37 for char in id.bytes() {
38 hash = hash.wrapping_mul(31).wrapping_add(char as u32);
39 }
40
41 hash &= !(1 << 31);
44
45 hash
46}
47
48pub fn strlcpy(dest: &mut [c_char], src: &str) {
52 if dest.is_empty() {
53 return;
54 }
55
56 let src_bytes: &[u8] = src.as_bytes();
57 let src_bytes_signed: &[c_char] = unsafe { &*(src_bytes as *const [u8] as *const [c_char]) };
60
61 let copy_len = cmp::min(dest.len() - 1, src.len());
63 dest[..copy_len].copy_from_slice(&src_bytes_signed[..copy_len]);
64 dest[copy_len] = 0;
65}
66
67#[inline]
70pub fn clamp_input_event_timing(timing: u32, total_buffer_len: u32) -> u32 {
71 let last_valid_index = total_buffer_len.saturating_sub(1);
73
74 crate::nice_debug_assert!(
75 timing <= last_valid_index,
76 "Input event is out of bounds, will be clamped to the buffer's size"
77 );
78
79 timing.min(last_valid_index)
80}
81
82#[inline]
85pub fn clamp_output_event_timing(timing: u32, total_buffer_len: u32) -> u32 {
86 let last_valid_index = total_buffer_len.saturating_sub(1);
87
88 crate::nice_debug_assert!(
89 timing <= last_valid_index,
90 "Output event is out of bounds, will be clamped to the buffer's size"
91 );
92
93 timing.min(last_valid_index)
94}
95
96pub fn setup_logger<P: Plugin>() {
109 static DID_SETUP: AtomicBool = AtomicBool::new(false);
110
111 let did_setup = DID_SETUP.swap(true, std::sync::atomic::Ordering::SeqCst);
112 if !did_setup {
113 if let Some(is_ok) = P::setup_logger() {
114 if is_ok {
115 log_panics();
116 }
117
118 return;
119 }
120
121 #[cfg(feature = "tracing-subscriber")]
122 {
123 #[cfg(debug_assertions)]
124 let sub = tracing_subscriber::FmtSubscriber::builder()
125 .with_max_level(tracing::level_filters::LevelFilter::DEBUG)
126 .with_target(true)
127 .with_ansi(false)
128 .with_writer(nice_log::writer_from_env())
129 .finish();
130
131 #[cfg(not(debug_assertions))]
132 let sub = tracing_subscriber::FmtSubscriber::builder()
133 .with_max_level(tracing::level_filters::LevelFilter::INFO)
134 .with_target(false)
135 .without_time()
136 .with_ansi(false)
137 .with_writer(nice_log::writer_from_env())
138 .finish();
139
140 if tracing::subscriber::set_global_default(sub).is_ok() {
141 log_panics();
142 }
143 }
144 }
145}
146
147fn log_panics() {
150 std::panic::set_hook(Box::new(|info| {
151 permit_alloc(|| {
152 let backtrace = Backtrace::new();
155
156 let thread = std::thread::current();
157 let thread = thread.name().unwrap_or("unnamed");
158
159 let msg = match info.payload().downcast_ref::<&'static str>() {
160 Some(s) => *s,
161 None => match info.payload().downcast_ref::<String>() {
162 Some(s) => &**s,
163 None => "Box<Any>",
164 },
165 };
166
167 match info.location() {
168 Some(location) => {
169 crate::nice_error!(
170 target: "panic", "thread '{}' panicked at '{}': {}:{}\n{:?}",
171 thread,
172 msg,
173 location.file(),
174 location.line(),
175 backtrace
176 );
177 }
178 None => {
179 crate::nice_error!(
180 target: "panic",
181 "thread '{}' panicked at '{}'\n{:?}",
182 thread,
183 msg,
184 backtrace
185 )
186 }
187 }
188 })
189 }));
190}
191
192pub fn process_wrapper<T, F: FnOnce() -> T>(f: F) -> T {
196 let _ftz_guard = ScopedFtz::enable();
198
199 #[cfg(all(debug_assertions, feature = "assert_process_allocs"))]
200 return nice_assert_no_alloc::assert_no_alloc(f);
201
202 #[cfg(not(all(debug_assertions, feature = "assert_process_allocs")))]
203 return f();
204}
205
206struct ScopedFtz {
209 #[allow(unused)]
213 should_disable_again: bool,
214 _send_sync_marker: PhantomData<*const ()>,
218}
219
220impl ScopedFtz {
221 fn enable() -> Self {
222 #[cfg(all(not(miri), feature = "unsafe_flush_denormals"))]
223 {
224 #[cfg(target_feature = "sse")]
225 {
226 let mut mxcsr: u32 = 0;
232 unsafe { std::arch::asm!("stmxcsr [{}]", in(reg) &mut mxcsr) };
233 let should_disable_again = mxcsr & SSE_FTZ_BIT == 0;
234 if should_disable_again {
235 unsafe { std::arch::asm!("ldmxcsr [{}]", in(reg) &(mxcsr | SSE_FTZ_BIT)) };
236 }
237
238 return Self {
239 should_disable_again,
240 _send_sync_marker: PhantomData,
241 };
242 }
243
244 #[cfg(target_arch = "aarch64")]
245 {
246 let mut fpcr: u64;
250 unsafe { std::arch::asm!("mrs {}, fpcr", out(reg) fpcr) };
251
252 let should_disable_again = fpcr & AARCH64_FTZ_BIT == 0;
253 if should_disable_again {
254 unsafe { std::arch::asm!("msr fpcr, {}", in(reg) fpcr | AARCH64_FTZ_BIT) };
255 }
256
257 return Self {
258 should_disable_again,
259 _send_sync_marker: PhantomData,
260 };
261 }
262 }
263
264 #[allow(unreachable_code)]
267 Self {
268 should_disable_again: false,
269 _send_sync_marker: PhantomData,
270 }
271 }
272}
273
274impl Drop for ScopedFtz {
275 fn drop(&mut self) {
276 #[cfg(all(not(miri), feature = "unsafe_flush_denormals"))]
277 if self.should_disable_again {
278 #[cfg(target_feature = "sse")]
279 {
280 let mut mxcsr: u32 = 0;
281 unsafe { std::arch::asm!("stmxcsr [{}]", in(reg) &mut mxcsr) };
282 unsafe { std::arch::asm!("ldmxcsr [{}]", in(reg) &(mxcsr & !SSE_FTZ_BIT)) };
283 }
284
285 #[cfg(target_arch = "aarch64")]
286 {
287 let mut fpcr: u64;
288 unsafe { std::arch::asm!("mrs {}, fpcr", out(reg) fpcr) };
289 unsafe { std::arch::asm!("msr fpcr, {}", in(reg) fpcr & !AARCH64_FTZ_BIT) };
290 }
291 }
292 }
293}
294
295#[cfg(test)]
296mod miri {
297 use std::ffi::CStr;
298
299 use super::*;
300
301 #[test]
302 fn strlcpy_normal() {
303 let mut dest = [0; 256];
304 strlcpy(&mut dest, "Hello, world!");
305
306 assert_eq!(
307 unsafe { CStr::from_ptr(dest.as_ptr()) }.to_str(),
308 Ok("Hello, world!")
309 );
310 }
311
312 #[test]
313 fn strlcpy_overflow() {
314 let mut dest = [0; 6];
315 strlcpy(&mut dest, "Hello, world!");
316
317 assert_eq!(
318 unsafe { CStr::from_ptr(dest.as_ptr()) }.to_str(),
319 Ok("Hello")
320 );
321 }
322}