rprofiler 0.2.0

A library for simple profiling your code with HTML reports as result
Documentation
use crate::BlockStat;
use std::{
    time::Instant,
    thread::ThreadId,
};

const HTML_REPORT_HEADER: &str = r#"<html><body>
<title>Profile report</title>

<style>
    body {
        color: #111;
        font-family: Noto Mono;
    }
    tr:nth-child(even) {
        background: #efeeef;
    }
    tr:nth-child(odd) {
        background: #fff;
    }
    td:nth-child(1) {
        font-weight: bold;
        text-align: left;
    }
    td:nth-child(n+2) {
        text-align: right;
    }
</style>

<h1>Functions statistics</h1>
"#;

const HTML_REPORT_FOOTER: &str = "</body></html>";

#[inline]
fn thread_id_to_usize(thread_id: ThreadId) -> usize {
    unsafe { *(&thread_id as *const ThreadId as *const usize) }
}

pub struct ProfilerData {
    pub(crate) main_block_start_time: Instant,
    pub(crate) main_block: BlockStat,
    pub(crate) blocks_stack: Vec<Vec<*mut BlockStat>>,
}

impl ProfilerData {
    pub(crate) fn new() -> ProfilerData {
        ProfilerData {
            main_block_start_time: Instant::now(),
            main_block: BlockStat::new("ProfilerMainBlock"),
            blocks_stack: Vec::new(),
        }
    }

    pub(crate) fn build_report_string(&self) -> String {
        let mut report = String::with_capacity(8192);
        report += HTML_REPORT_HEADER;

        report += "<table>\n";
        report += "<thead><th>Block name</th><th>Global percents</th><th>Relative to parent percents</th><th>Average time</th></thead>\n";

        self.main_block.build_report().build_string(&mut report);

        report += "</table>\n";

        report += HTML_REPORT_FOOTER;
        report
    }

    #[inline]
    pub(crate) fn current_block_on_thread(&mut self, thread_id: ThreadId) -> Option<*mut BlockStat> {
        let thread_id_value = thread_id_to_usize(thread_id);

        if self.blocks_stack.len() < thread_id_value + 1 {
            self.blocks_stack.resize(thread_id_value + 1, Vec::new());
        }

        unsafe {
            self.blocks_stack.get_unchecked(thread_id_value).last().cloned()
        }
    }

    #[inline]
    pub(crate) fn push_block_to_thread_stack(&mut self, thread_id: ThreadId, block: *mut BlockStat) {
        let thread_id_value = thread_id_to_usize(thread_id);
        unsafe {
            self.blocks_stack.get_unchecked_mut(thread_id_value).push(block);
        }
    }

    #[inline]
    pub(crate) fn pop_block_from_thread_stack(&mut self, thread_id: ThreadId) -> Option<*mut BlockStat> {
        let thread_id_value = thread_id_to_usize(thread_id);
        unsafe {
            self.blocks_stack.get_unchecked_mut(thread_id_value).pop()
        }
    }
}