use std::collections::HashMap;
use std::thread;
use std::thread::ThreadId;
extern crate backtrace;
extern crate const_format;
use const_format::concatcp;
extern crate lazy_static;
use lazy_static::lazy_static;
extern crate mut_static;
type StackDepth = usize;
type MapThreadidSd<'a> = HashMap<ThreadId, StackDepth>;
lazy_static! {
static ref STACK_OFFSET_TABLE: mut_static::MutStatic<MapThreadidSd<'static>> = {
mut_static::MutStatic::new()
};
}
#[inline(always)]
fn stack_depth() -> StackDepth {
let mut sd: StackDepth = 0;
backtrace::trace(|_| {
sd += 1;
true
});
sd
}
#[inline(never)]
fn stack_offset_table_create() -> bool {
match STACK_OFFSET_TABLE.is_set() {
Ok(false) => {
#[allow(clippy::single_match)]
match STACK_OFFSET_TABLE.set(MapThreadidSd::new()) {
Err(err) => {
if matches!(err.kind(), mut_static::ErrorKind::StaticIsAlreadySet) {
return true;
}
let tid = thread::current().id();
eprintln!(
"ERROR: stack_offset: STACK_OFFSET_TABLE.set failed in thread {:?}; {:?}",
tid, err
);
return false;
}
_ => {}
}
}
Ok(true) => {}
Err(err) => {
panic!("STACK_OFFSET_TABLE.is_set() failed {}", err);
}
}
true
}
#[inline(never)]
fn stack_offset() -> StackDepth {
stack_offset_set(Some(1));
let mut sd: StackDepth = stack_depth();
#[allow(clippy::implicit_saturating_sub)]
if sd > 0 {
sd -= 1;
}
let tid: ThreadId = thread::current().id();
let so_table = match STACK_OFFSET_TABLE.read() {
Ok(table) => table,
Err(_err) => {
return 0;
}
};
let sd_: StackDepth = sd; let so: &usize = so_table.get(&tid).unwrap_or(&sd_);
if &sd < so {
return 0;
}
sd -= so;
sd
}
#[inline(never)]
pub fn stack_offset_set(correction: Option<isize>) {
if !stack_offset_table_create() {
return;
}
let tid: ThreadId = thread::current().id();
{
if STACK_OFFSET_TABLE.read().unwrap().contains_key(&tid) {
return;
}
}
let mut sd: StackDepth = stack_depth();
#[allow(clippy::implicit_saturating_sub)]
if sd > 0 {
sd -= 1;
}
let sdi: isize = (sd as isize) - correction.unwrap_or(0);
let so: StackDepth = std::cmp::max(sdi, 0) as StackDepth;
match STACK_OFFSET_TABLE.write() {
Ok(mut table) => {
table.insert(tid, so);
}
Err(_err) => {}
}
}
const S_0: &str = "";
const S_1: &str = " ";
const S_2: &str = " ";
const S_3: &str = " ";
const S_4: &str = " ";
const S_5: &str = " ";
const S_6: &str = " ";
const S_7: &str = " ";
const S_8: &str = " ";
const S_9: &str = " ";
const S_10: &str = " ";
const S_11: &str = " ";
const S_12: &str = " ";
const S_13: &str = " ";
const S_14: &str = " ";
const S_15: &str = " ";
const S_16: &str = " ";
const S_17: &str = " ";
const S_18: &str = " ";
const S_19: &str = " ";
#[rustfmt::skip]
const S_20: &str = " ";
#[rustfmt::skip]
const S_21: &str = " ";
#[rustfmt::skip]
const S_22: &str = " ";
#[rustfmt::skip]
const S_23: &str = " ";
#[rustfmt::skip]
const S_24: &str = " ";
#[rustfmt::skip]
const S_25: &str = " ";
#[rustfmt::skip]
const S_26: &str = " ";
#[rustfmt::skip]
const S_27: &str = " ";
#[rustfmt::skip]
const S_28: &str = " ";
#[rustfmt::skip]
const S_29: &str = " ";
#[rustfmt::skip]
const S__: &str = " ";
pub(crate) const SO_LEAD: &str = " ";
pub fn so() -> &'static str {
let so_ = stack_offset();
match so_ {
0 => concatcp!(S_0, SO_LEAD),
1 => concatcp!(S_1, SO_LEAD),
2 => concatcp!(S_2, SO_LEAD),
3 => concatcp!(S_3, SO_LEAD),
4 => concatcp!(S_4, SO_LEAD),
5 => concatcp!(S_5, SO_LEAD),
6 => concatcp!(S_6, SO_LEAD),
7 => concatcp!(S_7, SO_LEAD),
8 => concatcp!(S_8, SO_LEAD),
9 => concatcp!(S_9, SO_LEAD),
10 => concatcp!(S_10, SO_LEAD),
11 => concatcp!(S_11, SO_LEAD),
12 => concatcp!(S_12, SO_LEAD),
13 => concatcp!(S_13, SO_LEAD),
14 => concatcp!(S_14, SO_LEAD),
15 => concatcp!(S_15, SO_LEAD),
16 => concatcp!(S_16, SO_LEAD),
17 => concatcp!(S_17, SO_LEAD),
18 => concatcp!(S_18, SO_LEAD),
19 => concatcp!(S_19, SO_LEAD),
20 => concatcp!(S_20, SO_LEAD),
21 => concatcp!(S_21, SO_LEAD),
22 => concatcp!(S_22, SO_LEAD),
23 => concatcp!(S_23, SO_LEAD),
24 => concatcp!(S_24, SO_LEAD),
25 => concatcp!(S_25, SO_LEAD),
26 => concatcp!(S_26, SO_LEAD),
27 => concatcp!(S_27, SO_LEAD),
28 => concatcp!(S_28, SO_LEAD),
29 => concatcp!(S_29, SO_LEAD),
_ => concatcp!(S__, SO_LEAD),
}
}
pub(crate) const SN_LEAD: &str = "→";
pub fn sn() -> &'static str {
let so = stack_offset();
match so {
0 => concatcp!(S_0, SN_LEAD),
1 => concatcp!(S_1, SN_LEAD),
2 => concatcp!(S_2, SN_LEAD),
3 => concatcp!(S_3, SN_LEAD),
4 => concatcp!(S_4, SN_LEAD),
5 => concatcp!(S_5, SN_LEAD),
6 => concatcp!(S_6, SN_LEAD),
7 => concatcp!(S_7, SN_LEAD),
8 => concatcp!(S_8, SN_LEAD),
9 => concatcp!(S_9, SN_LEAD),
10 => concatcp!(S_10, SN_LEAD),
11 => concatcp!(S_11, SN_LEAD),
12 => concatcp!(S_12, SN_LEAD),
13 => concatcp!(S_13, SN_LEAD),
14 => concatcp!(S_14, SN_LEAD),
15 => concatcp!(S_15, SN_LEAD),
16 => concatcp!(S_16, SN_LEAD),
17 => concatcp!(S_17, SN_LEAD),
18 => concatcp!(S_18, SN_LEAD),
19 => concatcp!(S_19, SN_LEAD),
20 => concatcp!(S_20, SN_LEAD),
21 => concatcp!(S_21, SN_LEAD),
22 => concatcp!(S_22, SN_LEAD),
23 => concatcp!(S_23, SN_LEAD),
24 => concatcp!(S_24, SN_LEAD),
25 => concatcp!(S_25, SN_LEAD),
26 => concatcp!(S_26, SN_LEAD),
27 => concatcp!(S_27, SN_LEAD),
28 => concatcp!(S_28, SN_LEAD),
29 => concatcp!(S_29, SN_LEAD),
_ => concatcp!(S__, SN_LEAD),
}
}
pub(crate) const SX_LEAD: &str = "←";
pub fn sx() -> &'static str {
let so = stack_offset();
match so {
0 => concatcp!(S_0, SX_LEAD),
1 => concatcp!(S_1, SX_LEAD),
2 => concatcp!(S_2, SX_LEAD),
3 => concatcp!(S_3, SX_LEAD),
4 => concatcp!(S_4, SX_LEAD),
5 => concatcp!(S_5, SX_LEAD),
6 => concatcp!(S_6, SX_LEAD),
7 => concatcp!(S_7, SX_LEAD),
8 => concatcp!(S_8, SX_LEAD),
9 => concatcp!(S_9, SX_LEAD),
10 => concatcp!(S_10, SX_LEAD),
11 => concatcp!(S_11, SX_LEAD),
12 => concatcp!(S_12, SX_LEAD),
13 => concatcp!(S_13, SX_LEAD),
14 => concatcp!(S_14, SX_LEAD),
15 => concatcp!(S_15, SX_LEAD),
16 => concatcp!(S_16, SX_LEAD),
17 => concatcp!(S_17, SX_LEAD),
18 => concatcp!(S_18, SX_LEAD),
19 => concatcp!(S_19, SX_LEAD),
20 => concatcp!(S_20, SX_LEAD),
21 => concatcp!(S_21, SX_LEAD),
22 => concatcp!(S_22, SX_LEAD),
23 => concatcp!(S_23, SX_LEAD),
24 => concatcp!(S_24, SX_LEAD),
25 => concatcp!(S_25, SX_LEAD),
26 => concatcp!(S_26, SX_LEAD),
27 => concatcp!(S_27, SX_LEAD),
28 => concatcp!(S_28, SX_LEAD),
29 => concatcp!(S_29, SX_LEAD),
_ => concatcp!(S__, SX_LEAD),
}
}
pub(crate) const SÑ_LEAD: &str = "↔";
pub fn sñ() -> &'static str {
let so = stack_offset();
match so {
0 => concatcp!(S_0, SÑ_LEAD),
1 => concatcp!(S_1, SÑ_LEAD),
2 => concatcp!(S_2, SÑ_LEAD),
3 => concatcp!(S_3, SÑ_LEAD),
4 => concatcp!(S_4, SÑ_LEAD),
5 => concatcp!(S_5, SÑ_LEAD),
6 => concatcp!(S_6, SÑ_LEAD),
7 => concatcp!(S_7, SÑ_LEAD),
8 => concatcp!(S_8, SÑ_LEAD),
9 => concatcp!(S_9, SÑ_LEAD),
10 => concatcp!(S_10, SÑ_LEAD),
11 => concatcp!(S_11, SÑ_LEAD),
12 => concatcp!(S_12, SÑ_LEAD),
13 => concatcp!(S_13, SÑ_LEAD),
14 => concatcp!(S_14, SÑ_LEAD),
15 => concatcp!(S_15, SÑ_LEAD),
16 => concatcp!(S_16, SÑ_LEAD),
17 => concatcp!(S_17, SÑ_LEAD),
18 => concatcp!(S_18, SÑ_LEAD),
19 => concatcp!(S_19, SÑ_LEAD),
20 => concatcp!(S_10, SÑ_LEAD),
21 => concatcp!(S_21, SÑ_LEAD),
22 => concatcp!(S_22, SÑ_LEAD),
23 => concatcp!(S_23, SÑ_LEAD),
24 => concatcp!(S_24, SÑ_LEAD),
25 => concatcp!(S_25, SÑ_LEAD),
26 => concatcp!(S_26, SÑ_LEAD),
27 => concatcp!(S_27, SÑ_LEAD),
28 => concatcp!(S_28, SÑ_LEAD),
29 => concatcp!(S_29, SÑ_LEAD),
_ => concatcp!(S__, SÑ_LEAD),
}
}
#[cfg(test)]
mod tests {
use super::{sn, so, stack_depth, stack_offset, stack_offset_set, sx, sñ, StackDepth};
#[test]
fn test_stack_depth() {
let a = stack_depth();
fn func1() -> StackDepth {
stack_depth()
}
fn func2() -> StackDepth {
stack_depth()
}
let b = func1();
let c = func2();
assert!(
b - 1 == a,
"expected stack depth difference of 1, got stack depth {}, plus a function stack depth {}",
a,
b,
);
assert!(b == c, "expected same, got stack depths {} {}", b, c,);
}
#[test]
fn test_stack_offset_a_b() {
let a = stack_offset();
fn func1() -> StackDepth {
stack_offset()
}
let b = func1();
assert!(
b == a + 1,
"expected stack offset difference of 1, got stack offset {}, plus a function stack offset {}",
a,
b,
);
}
#[test]
fn test_stack_offset_a_b_c() {
let a = stack_offset();
fn func1() -> StackDepth {
stack_offset()
}
fn func2() -> StackDepth {
stack_offset()
}
let b = func1();
let c = func2();
assert!(
b == a + 1,
"expected stack offset difference of 1, got stack offset {}, plus a function stack offset {}",
a,
b,
);
assert!(b == c, "expected same, got stack depths {} {}", b, c,);
}
#[test]
fn test_stack_offset_set_none() {
stack_offset_set(None);
let a = stack_offset();
fn func1() -> StackDepth {
stack_offset()
}
let b = func1();
assert!(
b == a + 1,
"expected stack offset difference of 1, got stack offset {}, plus a function stack offset {}",
a,
b,
);
}
#[test]
fn test_stack_offset_set_none_999() {
stack_offset_set(None);
let a = stack_offset();
fn func1() -> StackDepth {
stack_offset_set(Some(999));
stack_offset()
}
let b = func1();
assert!(
b == a + 1,
"expected stack offset difference of 1, got stack offset {}, plus a function stack offset {}",
a,
b,
);
}
#[test]
fn test_stack_offset_set_multiple_threads() {
let mut handles = Vec::<std::thread::JoinHandle<()>>::new();
for _i in 1..99 {
let handle = std::thread::spawn(|| {
stack_offset_set(None);
let a = stack_offset();
fn func1() -> StackDepth {
stack_offset()
}
let b = func1();
assert!(
b == a + 1,
"expected stack offset difference of 1, got stack offset {}, plus a function stack offset {}",
a,
b,
);
});
handles.push(handle);
}
for handle in handles.into_iter() {
match handle.join() {
Ok(_) => {}
Err(err) => {
panic!("handle.join failed {:?}", err);
}
}
}
}
#[test]
fn test_stack_offset_set_10() {
stack_offset_set(Some(10));
let a = stack_offset();
fn func1() -> StackDepth {
stack_offset()
}
let b = func1();
assert!(
b == a + 1,
"expected stack offset difference of 1, got stack offset {}, plus a function stack offset {}",
a,
b,
);
}
#[test]
fn test_so() {
so();
}
#[test]
fn test_sn() {
sn();
}
#[test]
fn test_sx() {
sx();
}
#[test]
fn test_sñ() {
sñ();
}
}