Packages
html_to_markdown
2.30.0
3.9.0
3.8.3
3.8.2
3.8.1
3.8.0
3.8.0-rc.2
3.8.0-rc.1
3.7.2
3.7.1
3.7.0
3.6.21
3.6.20
3.6.19
3.6.18
3.6.17
3.6.16
3.6.15
3.6.14
3.6.13
3.6.11
3.6.10
3.6.9
3.6.8
3.6.3
3.6.1
3.6.0
3.6.0-rc.25
3.6.0-rc.23
3.6.0-rc.22
3.6.0-rc.21
3.6.0-rc.20
3.6.0-rc.17
3.6.0-rc.16
3.6.0-rc.14
3.6.0-rc.13
3.6.0-rc.12
3.6.0-rc.11
3.6.0-rc.10
3.6.0-rc.9
3.6.0-rc.7
3.6.0-rc.3
3.6.0-rc.2
3.6.0-rc.1
3.5.7
3.5.5
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.4.0
3.4.0-rc.45
3.4.0-rc.44
3.4.0-rc.42
3.4.0-rc.41
3.4.0-rc.40
3.4.0-rc.25
3.4.0-rc.24
3.4.0-rc.23
3.4.0-rc.21
3.4.0-rc.20
3.4.0-rc.18
3.4.0-rc.16
3.4.0-rc.15
3.4.0-rc.14
3.4.0-rc.13
3.4.0-rc.12
3.4.0-rc.11
3.4.0-rc.9
3.4.0-rc.8
3.4.0-rc.2
3.4.0-rc.1
3.3.2
3.2.4
3.2.2
3.2.1
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
2.30.0
2.29.0
2.28.6
2.28.5
2.28.4
2.28.3
2.28.2
2.28.1
2.28.0
2.27.3
2.27.2
2.27.1
2.27.0
2.26.3
2.26.2
2.26.1
2.25.1
2.25.0
2.24.6
2.24.5
2.24.4
2.24.3
2.24.1
2.23.4
2.23.3
2.23.2
2.23.1
2.23.0
2.22.5
2.22.2
2.22.1
2.22.0
2.21.1
2.20.0
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
2.19.2
2.19.1
2.19.0
2.18.0
2.16.1
2.16.0
2.15.0
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.3
2.14.2
2.14.1
2.14.0
2.13.0
2.12.1
2.12.0
2.11.4
2.11.3
2.11.1
2.10.1
2.9.2
2.9.1
2.9.0
2.8.3
High-performance HTML to Markdown converter
Current section
Files
Jump to
Current section
Files
native/html_to_markdown_elixir/src/profiling.rs
use html_to_markdown_rs::{ConversionError, Result};
use std::path::PathBuf;
#[cfg(all(not(target_os = "windows"), feature = "profiling"))]
mod enabled {
use super::{ConversionError, PathBuf, Result};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, OnceLock};
const ENV_OUTPUT: &str = "HTML_TO_MARKDOWN_PROFILE_OUTPUT";
const ENV_FREQUENCY: &str = "HTML_TO_MARKDOWN_PROFILE_FREQUENCY";
const ENV_ONCE: &str = "HTML_TO_MARKDOWN_PROFILE_ONCE";
const ENV_REPEAT: &str = "HTML_TO_MARKDOWN_PROFILE_REPEAT";
static PROFILED_ONCE: AtomicBool = AtomicBool::new(false);
static PROFILE_ACTIVE: AtomicBool = AtomicBool::new(false);
struct EnvProfileConfig {
output: Option<PathBuf>,
profile_once: bool,
repeat: usize,
frequency: i32,
}
fn env_profile_config() -> &'static EnvProfileConfig {
static ENV_CONFIG: OnceLock<EnvProfileConfig> = OnceLock::new();
ENV_CONFIG.get_or_init(|| {
let output = match std::env::var(ENV_OUTPUT) {
Ok(value) if !value.trim().is_empty() => Some(PathBuf::from(value)),
_ => None,
};
let profile_once = match std::env::var(ENV_ONCE) {
Ok(value) => !matches!(value.as_str(), "0" | "false" | "no"),
Err(_) => true,
};
let repeat = std::env::var(ENV_REPEAT)
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(1)
.max(1);
let frequency = std::env::var(ENV_FREQUENCY)
.ok()
.and_then(|value| value.parse::<i32>().ok())
.unwrap_or(1000);
EnvProfileConfig {
output,
profile_once,
repeat,
frequency,
}
})
}
struct ProfileState {
guard: Option<pprof::ProfilerGuard<'static>>,
output: Option<PathBuf>,
}
fn state() -> &'static Mutex<ProfileState> {
static STATE: OnceLock<Mutex<ProfileState>> = OnceLock::new();
STATE.get_or_init(|| {
Mutex::new(ProfileState {
guard: None,
output: None,
})
})
}
pub fn start(output_path: PathBuf, frequency: i32) -> Result<()> {
let mut state = state()
.lock()
.map_err(|_| ConversionError::Other("profiling state lock poisoned".to_string()))?;
if state.guard.is_some() {
return Err(ConversionError::Other("profiling already active".to_string()));
}
let guard = pprof::ProfilerGuardBuilder::default()
.frequency(frequency)
.blocklist(&["libc", "libpthread", "libgcc", "libm"])
.build()
.map_err(|err| ConversionError::Other(format!("Profiling init failed: {err}")))?;
state.guard = Some(guard);
state.output = Some(output_path);
PROFILE_ACTIVE.store(true, Ordering::Release);
Ok(())
}
pub fn stop() -> Result<()> {
let (guard, output) = {
let mut state = state()
.lock()
.map_err(|_| ConversionError::Other("profiling state lock poisoned".to_string()))?;
let guard = state.guard.take();
let output = state.output.take();
(guard, output)
};
PROFILE_ACTIVE.store(false, Ordering::Release);
let Some(guard) = guard else {
return Err(ConversionError::Other("profiling not active".to_string()));
};
let Some(output_path) = output else {
return Err(ConversionError::Other("profiling output path missing".to_string()));
};
if let Some(parent) = output_path.parent() {
std::fs::create_dir_all(parent).map_err(ConversionError::IoError)?;
}
let report = guard
.report()
.build()
.map_err(|err| ConversionError::Other(format!("Profiling report failed: {err}")))?;
let file = std::fs::File::create(&output_path).map_err(ConversionError::IoError)?;
report
.flamegraph(file)
.map_err(|err| ConversionError::Other(format!("Flamegraph write failed: {err}")))?;
PROFILE_ACTIVE.store(false, Ordering::Release);
Ok(())
}
pub fn maybe_profile<T, F>(mut f: F) -> Result<T>
where
F: FnMut() -> Result<T>,
{
if PROFILE_ACTIVE.load(Ordering::Relaxed) {
return f();
}
let config = env_profile_config();
let Some(output_path) = config.output.as_ref() else {
return f();
};
if config.profile_once && PROFILED_ONCE.swap(true, Ordering::SeqCst) {
return f();
}
struct ActiveGuard;
impl Drop for ActiveGuard {
fn drop(&mut self) {
PROFILE_ACTIVE.store(false, Ordering::Release);
}
}
PROFILE_ACTIVE.store(true, Ordering::Release);
let _active = ActiveGuard;
let guard = pprof::ProfilerGuardBuilder::default()
.frequency(config.frequency)
.blocklist(&["libc", "libpthread", "libgcc", "libm"])
.build()
.map_err(|err| ConversionError::Other(format!("Profiling init failed: {err}")))?;
let mut last_result = None;
for _ in 0..config.repeat {
let value = f()?;
last_result = Some(value);
}
let result =
last_result.ok_or_else(|| ConversionError::Other("Profiling repeat produced no result".to_string()))?;
if let Some(parent) = output_path.parent() {
std::fs::create_dir_all(parent).map_err(ConversionError::IoError)?;
}
let report = guard
.report()
.build()
.map_err(|err| ConversionError::Other(format!("Profiling report failed: {err}")))?;
let file = std::fs::File::create(output_path).map_err(ConversionError::IoError)?;
report
.flamegraph(file)
.map_err(|err| ConversionError::Other(format!("Flamegraph write failed: {err}")))?;
Ok(result)
}
}
#[cfg(all(not(target_os = "windows"), feature = "profiling"))]
pub use enabled::{maybe_profile, start, stop};
#[cfg(target_os = "windows")]
pub fn start(_output_path: PathBuf, _frequency: i32) -> Result<()> {
Err(ConversionError::Other(
"Profiling is not supported on Windows".to_string(),
))
}
#[cfg(all(not(target_os = "windows"), not(feature = "profiling")))]
pub fn start(_output_path: PathBuf, _frequency: i32) -> Result<()> {
Err(ConversionError::Other(
"Profiling is disabled; rebuild with the profiling feature".to_string(),
))
}
#[cfg(target_os = "windows")]
pub fn stop() -> Result<()> {
Err(ConversionError::Other(
"Profiling is not supported on Windows".to_string(),
))
}
#[cfg(all(not(target_os = "windows"), not(feature = "profiling")))]
pub fn stop() -> Result<()> {
Err(ConversionError::Other(
"Profiling is disabled; rebuild with the profiling feature".to_string(),
))
}
#[cfg(any(target_os = "windows", not(feature = "profiling")))]
pub fn maybe_profile<T, F>(mut f: F) -> Result<T>
where
F: FnMut() -> Result<T>,
{
f()
}