tycho_util/
lib.rs

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