tycho_util/
lib.rs

1use std::collections::{HashMap, HashSet};
2use std::path::PathBuf;
3use std::process::Command;
4
5pub mod compression;
6pub mod io;
7pub mod progress_bar;
8pub mod serde_helpers;
9pub mod time;
10pub mod tl;
11
12pub mod futures {
13    pub use self::box_future_or_noop::BoxFutureOrNoop;
14    pub use self::join_task::JoinTask;
15    pub use self::shared::{Shared, WeakShared};
16
17    mod box_future_or_noop;
18    mod join_task;
19    mod shared;
20}
21
22pub mod sync {
23    pub use self::once_take::*;
24    pub use self::priority_semaphore::{AcquireError, PrioritySemaphore, TryAcquireError};
25    pub use self::rayon::{rayon_run, rayon_run_fifo};
26    pub use self::task::{yield_on_complex, CancellationFlag, DebounceCancellationFlag};
27
28    mod once_take;
29    mod priority_semaphore;
30    mod rayon;
31    mod task;
32}
33
34#[cfg(any(test, feature = "test"))]
35pub mod test {
36    pub use self::logger::init_logger;
37
38    mod logger;
39}
40
41pub mod metrics {
42    pub use self::gauge_guard::GaugeGuard;
43    pub use self::histogram_guard::{HistogramGuard, HistogramGuardWithLabels};
44    pub use self::metrics_loop::spawn_metrics_loop;
45
46    mod gauge_guard;
47    mod histogram_guard;
48    mod metrics_loop;
49}
50
51mod util {
52    pub(crate) mod linked_list;
53    pub(crate) mod wake_list;
54}
55
56#[cfg(feature = "cli")]
57pub mod cli;
58
59pub use dashmap::mapref::entry::Entry as DashMapEntry;
60
61pub type FastDashMap<K, V> = dashmap::DashMap<K, V, ahash::RandomState>;
62pub type FastDashSet<K> = dashmap::DashSet<K, ahash::RandomState>;
63pub type FastHashMap<K, V> = HashMap<K, V, ahash::RandomState>;
64pub type FastHashSet<K> = HashSet<K, ahash::RandomState>;
65pub type FastHasherState = ahash::RandomState;
66
67/// # Example
68///
69/// ```rust
70/// # use tycho_util::realloc_box_enum;
71/// enum Value {
72///     One(BigValue1),
73///     Two(BigValue2),
74/// }
75///
76/// struct BigValue1([u32; 10]);
77///
78/// struct BigValue2([u32; 7]);
79///
80/// fn convert_to_one(value: Box<Value>) -> Option<Box<BigValue1>> {
81///     realloc_box_enum!(value, {
82///         Value::One(value) => Box::new(value) => Some(value),
83///         _ => None,
84///     })
85/// }
86/// ```
87#[macro_export]
88macro_rules! realloc_box_enum {
89    ($value:expr, {
90        $target_variant:pat => Box::new($extracted:ident) => $target:expr,
91        $other_variant:pat => $other:expr,
92    }) => {{
93        let value: ::std::boxed::Box<_> = $value;
94        match ::core::convert::AsRef::as_ref(&value) {
95            #[allow(unused_variables)]
96            $target_variant => {
97                let $extracted = unsafe {
98                    $crate::__internal::realloc_box(value, |value| match value {
99                        $target_variant => $extracted,
100                        _ => unreachable!(),
101                    })
102                };
103                $target
104            }
105            $other_variant => $other,
106        }
107    }};
108}
109
110#[doc(hidden)]
111pub mod __internal {
112    /// # Safety
113    /// The following must be true:
114    /// - `T` must have the same layout as `R`
115    /// - `f` must not panic
116    pub unsafe fn realloc_box<T, F, R>(value: Box<T>, f: F) -> Box<R>
117    where
118        F: FnOnce(T) -> R,
119    {
120        assert!(std::mem::align_of::<T>() == std::mem::align_of::<R>());
121
122        let ptr = Box::into_raw(value);
123        let value = std::ptr::read(ptr);
124
125        let ptr = std::alloc::realloc(
126            ptr.cast::<u8>(),
127            std::alloc::Layout::new::<T>(),
128            std::mem::size_of::<R>(),
129        )
130        .cast::<R>();
131
132        if ptr.is_null() {
133            std::alloc::handle_alloc_error(std::alloc::Layout::new::<R>());
134        }
135
136        // NOTE: in case of panic, the memory will be leaked
137        std::ptr::write(ptr, f(value));
138
139        Box::from_raw(ptr)
140    }
141}
142
143pub fn project_root() -> Result<PathBuf, anyhow::Error> {
144    use anyhow::Context;
145
146    let project_root = Command::new("git")
147        .arg("rev-parse")
148        .arg("--show-toplevel")
149        .output()?
150        .stdout;
151    // won't work on windows but we don't care
152    let project_root = PathBuf::from(
153        String::from_utf8(project_root)
154            .context("invalid project root")?
155            .trim(),
156    );
157    Ok(project_root)
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    #[allow(dead_code)]
166    fn realloc_enum() {
167        enum Value {
168            One(BigValue1),
169            Two(BigValue2),
170        }
171
172        #[derive(Clone)]
173        struct BigValue1([u32; 10]);
174
175        #[derive(Clone)]
176        struct BigValue2([u32; 7]);
177
178        fn convert_to_one(value: Box<Value>) -> Option<Box<BigValue1>> {
179            realloc_box_enum!(value, {
180                Value::One(value) => Box::new(value) => Some(value),
181                _ => None,
182            })
183        }
184
185        let value = BigValue1([123; 10]);
186        let one = convert_to_one(Box::new(Value::One(value.clone())));
187        assert_eq!(one.unwrap().0, value.0);
188    }
189}