tycho_util/
lib.rs

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