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