use std::fmt;
pub struct Buffer(String);
impl fmt::Write for Buffer {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0.write_str(s)
}
#[inline]
fn write_char(&mut self, c: char) -> fmt::Result {
self.0.write_char(c)
}
}
impl Buffer {
const fn new() -> Self {
Self(String::new())
}
fn clear_on_err<T, E>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, E>) -> Result<T, E> {
let r = f(self);
if r.is_err() {
self.0.clear();
}
r
}
}
pub fn default(s: &str, buffer: &mut impl fmt::Write) -> fmt::Result {
let Ok(demangled) = rustc_demangle::try_demangle(s) else {
return Err(fmt::Error);
};
write!(buffer, "{demangled:#}")
}
pub fn noop(_: &str, _: &mut impl fmt::Write) -> fmt::Result {
Err(fmt::Error)
}
pub(super) mod internal {
use super::Buffer;
use std::ffi::c_char;
use std::fmt::{self, Write};
use std::ptr::null;
pub unsafe fn implementation<F>(mangled: *const c_char, run: F) -> *const c_char
where
F: FnOnce(&str, &mut Buffer) -> fmt::Result,
{
static mut BUFFER: Buffer = Buffer::new();
if mangled.is_null() {
return null();
}
let cstr = unsafe { std::ffi::CStr::from_ptr(mangled) };
let Ok(str) = cstr.to_str() else {
return null();
};
let buffer = unsafe { &mut *std::ptr::addr_of_mut!(BUFFER) };
buffer.0.clear();
let result = buffer.clear_on_err(|buffer| {
run(str, buffer)?;
match buffer.0.as_bytes().split_last() {
None | Some((&0, [])) => return Err(fmt::Error),
Some((_, v)) if v.contains(&0) => return Err(fmt::Error),
Some((&0, _)) => return Ok(()),
_ => (),
}
buffer.write_char('\0')?;
Ok(())
});
match result {
Ok(()) => {
debug_assert_eq!(buffer.0.as_bytes().last().copied(), Some(0));
buffer.0.as_ptr().cast()
}
Err(fmt::Error) => null(),
}
}
}
#[macro_export]
macro_rules! register_demangler {
() => {
$crate::register_demangler!($crate::internal::demangle::default);
};
($path:path) => {
const _: () = {
#[no_mangle]
unsafe extern "C" fn ___tracy_demangle(
mangled: *const std::ffi::c_char,
) -> *const std::ffi::c_char {
unsafe { $crate::internal::demangle::implementation(mangled, $path) }
}
};
};
}