Skip to main content

heed/envs/
mod.rs

1use std::cmp::Ordering;
2use std::collections::HashMap;
3use std::ffi::c_void;
4use std::fs::File;
5#[cfg(unix)]
6use std::os::unix::io::{AsRawFd, RawFd};
7use std::panic::catch_unwind;
8use std::path::{Path, PathBuf};
9use std::process::abort;
10use std::sync::{Arc, LazyLock, RwLock};
11use std::time::Duration;
12#[cfg(windows)]
13use std::{
14    ffi::OsStr,
15    os::windows::io::{AsRawHandle as _, RawHandle},
16};
17use std::{fmt, io};
18
19use heed_traits::{Comparator, LexicographicComparator};
20use synchronoise::event::SignalEvent;
21
22use crate::mdb::ffi;
23#[allow(unused)] // for cargo auto doc links
24use crate::{Database, DatabaseFlags};
25
26#[cfg(master3)]
27mod encrypted_env;
28mod env;
29mod env_open_options;
30
31#[cfg(master3)]
32pub use encrypted_env::EncryptedEnv;
33pub use env::Env;
34pub(crate) use env::EnvInner;
35pub use env_open_options::EnvOpenOptions;
36
37/// Records the current list of opened environments for tracking purposes. The canonical
38/// path of an environment is removed when either an `Env` or `EncryptedEnv` is closed.
39static OPENED_ENV: LazyLock<RwLock<HashMap<PathBuf, Arc<SignalEvent>>>> =
40    LazyLock::new(RwLock::default);
41
42/// Returns a struct that allows to wait for the effective closing of an environment.
43pub fn env_closing_event<P: AsRef<Path>>(path: P) -> Option<EnvClosingEvent> {
44    let lock = OPENED_ENV.read().unwrap();
45    lock.get(path.as_ref()).map(|signal_event| EnvClosingEvent(signal_event.clone()))
46}
47
48/// Contains information about the environment.
49#[derive(Debug, Clone, Copy)]
50pub struct EnvInfo {
51    /// Address of the map, if fixed.
52    pub map_addr: *mut c_void,
53    /// Size of the data memory map.
54    pub map_size: usize,
55    /// ID of the last used page.
56    pub last_page_number: usize,
57    /// ID of the last committed transaction.
58    pub last_txn_id: usize,
59    /// Maximum number of reader slots in the environment.
60    pub maximum_number_of_readers: u32,
61    /// Number of reader slots used in the environment.
62    pub number_of_readers: u32,
63}
64
65/// Statistics for an environment.
66#[derive(Debug, Clone, Copy)]
67pub struct EnvStat {
68    /// Size of a database page.
69    /// This is currently the same for all databases.
70    pub page_size: u32,
71    /// Depth (height) of the B-tree.
72    pub depth: u32,
73    /// Number of internal (non-leaf) pages
74    pub branch_pages: usize,
75    /// Number of leaf pages.
76    pub leaf_pages: usize,
77    /// Number of overflow pages.
78    pub overflow_pages: usize,
79    /// Number of data items.
80    pub entries: usize,
81}
82
83/// A structure that can be used to wait for the closing event.
84/// Multiple threads can wait on this event.
85#[derive(Clone)]
86pub struct EnvClosingEvent(Arc<SignalEvent>);
87
88impl EnvClosingEvent {
89    /// Blocks this thread until the environment is effectively closed.
90    ///
91    /// # Safety
92    ///
93    /// Make sure that you don't have any copy of the environment in the thread
94    /// that is waiting for a close event. If you do, you will have a deadlock.
95    pub fn wait(&self) {
96        self.0.wait()
97    }
98
99    /// Blocks this thread until either the environment has been closed
100    /// or until the timeout elapses. Returns `true` if the environment
101    /// has been effectively closed.
102    pub fn wait_timeout(&self, timeout: Duration) -> bool {
103        self.0.wait_timeout(timeout)
104    }
105}
106
107impl fmt::Debug for EnvClosingEvent {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        f.debug_struct("EnvClosingEvent").finish()
110    }
111}
112
113// Thanks to the mozilla/rkv project
114// Workaround the UNC path on Windows, see https://github.com/rust-lang/rust/issues/42869.
115// Otherwise, `Env::from_env()` will panic with error_no(123).
116#[cfg(not(windows))]
117fn canonicalize_path(path: &Path) -> io::Result<PathBuf> {
118    path.canonicalize()
119}
120
121#[cfg(windows)]
122fn canonicalize_path(path: &Path) -> io::Result<PathBuf> {
123    let canonical = path.canonicalize()?;
124    let url = url::Url::from_file_path(&canonical)
125        .map_err(|_e| io::Error::new(io::ErrorKind::Other, "URL passing error"))?;
126    url.to_file_path()
127        .map_err(|_e| io::Error::new(io::ErrorKind::Other, "path canonicalization error"))
128}
129
130#[cfg(windows)]
131/// Adding a 'missing' trait from windows OsStrExt
132trait OsStrExtLmdb {
133    fn as_bytes(&self) -> &[u8];
134}
135#[cfg(windows)]
136impl OsStrExtLmdb for OsStr {
137    fn as_bytes(&self) -> &[u8] {
138        &self.to_str().unwrap().as_bytes()
139    }
140}
141
142#[cfg(unix)]
143fn get_file_fd(file: &File) -> RawFd {
144    file.as_raw_fd()
145}
146
147#[cfg(windows)]
148fn get_file_fd(file: &File) -> RawHandle {
149    file.as_raw_handle()
150}
151
152/// A helper function that transforms the LMDB types into Rust types (`MDB_val` into slices)
153/// and vice versa, the Rust types into C types (`Ordering` into an integer).
154///
155/// # Safety
156///
157/// `a` and `b` should both properly aligned, valid for reads and should point to a valid
158/// [`MDB_val`][ffi::MDB_val]. An [`MDB_val`][ffi::MDB_val] (consists of a pointer and size) is
159/// valid when its pointer (`mv_data`) is valid for reads of `mv_size` bytes and is not null.
160unsafe extern "C" fn custom_key_cmp_wrapper<C: Comparator>(
161    a: *const ffi::MDB_val,
162    b: *const ffi::MDB_val,
163) -> i32 {
164    let a = unsafe { ffi::from_val(*a) };
165    let b = unsafe { ffi::from_val(*b) };
166    match catch_unwind(|| C::compare(a, b)) {
167        Ok(Ordering::Less) => -1,
168        Ok(Ordering::Equal) => 0,
169        Ok(Ordering::Greater) => 1,
170        Err(_) => abort(),
171    }
172}
173
174/// A representation of LMDB's default comparator behavior.
175///
176/// This enum is used to indicate the absence of a custom comparator for an LMDB
177/// database instance. When a [`Database`] is created or opened with
178/// [`DefaultComparator`], it signifies that the comparator should not be explicitly
179/// set via [`ffi::mdb_set_compare`]. Consequently, the database
180/// instance utilizes LMDB's built-in default comparator, which inherently performs
181/// lexicographic comparison of keys.
182///
183/// This comparator's lexicographic implementation is employed in scenarios involving
184/// prefix iterators. Specifically, methods other than [`Comparator::compare`] are utilized
185/// to determine the lexicographic successors and predecessors of byte sequences, which
186/// is essential for these iterators' operation.
187///
188/// When a custom comparator is provided, the wrapper is responsible for setting
189/// it with the [`ffi::mdb_set_compare`] function, which overrides the default comparison
190/// behavior of LMDB with the user-defined logic.
191#[derive(Debug)]
192pub enum DefaultComparator {}
193
194impl LexicographicComparator for DefaultComparator {
195    #[inline]
196    fn compare_elem(a: u8, b: u8) -> Ordering {
197        a.cmp(&b)
198    }
199
200    #[inline]
201    fn successor(elem: u8) -> Option<u8> {
202        match elem {
203            u8::MAX => None,
204            elem => Some(elem + 1),
205        }
206    }
207
208    #[inline]
209    fn predecessor(elem: u8) -> Option<u8> {
210        match elem {
211            u8::MIN => None,
212            elem => Some(elem - 1),
213        }
214    }
215
216    #[inline]
217    fn max_elem() -> u8 {
218        u8::MAX
219    }
220
221    #[inline]
222    fn min_elem() -> u8 {
223        u8::MIN
224    }
225}
226
227/// A representation of LMDB's `MDB_INTEGERKEY` and `MDB_INTEGERDUP` comparator behavior.
228///
229/// This enum is used to indicate a table should be sorted by the keys numeric
230/// value in native byte order. When a [`Database`] is created or opened with
231/// [`IntegerComparator`], it signifies that the comparator should not be explicitly
232/// set via [`ffi::mdb_set_compare`], instead the flag [`DatabaseFlags::INTEGER_KEY`]
233/// or [`DatabaseFlags::INTEGER_DUP`] is set on the table.
234///
235/// This can only be used on certain types: either `u32` or `usize`.
236/// The keys must all be of the same size.
237#[derive(Debug)]
238pub enum IntegerComparator {}
239
240impl Comparator for IntegerComparator {
241    fn compare(a: &[u8], b: &[u8]) -> Ordering {
242        #[cfg(target_endian = "big")]
243        return a.cmp(b);
244
245        #[cfg(target_endian = "little")]
246        {
247            let len = a.len();
248
249            for i in (0..len).rev() {
250                match a[i].cmp(&b[i]) {
251                    Ordering::Equal => continue,
252                    other => return other,
253                }
254            }
255
256            Ordering::Equal
257        }
258    }
259}
260
261/// Whether to perform compaction while copying an environment.
262#[derive(Debug, Copy, Clone)]
263pub enum CompactionOption {
264    /// Omit free pages and sequentially renumber all pages in output.
265    ///
266    /// This option consumes more CPU and runs more slowly than the default.
267    /// Currently it fails if the environment has suffered a page leak.
268    Enabled,
269
270    /// Copy everything without taking any special action about free pages.
271    Disabled,
272}
273
274/// Whether to enable or disable flags in [`Env::set_flags`].
275#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
276pub enum FlagSetMode {
277    /// Enable the flags.
278    Enable,
279    /// Disable the flags.
280    Disable,
281}
282
283impl FlagSetMode {
284    /// Convert the enum into the `i32` required by LMDB.
285    /// "A non-zero value sets the flags, zero clears them."
286    /// <http://www.lmdb.tech/doc/group__mdb.html#ga83f66cf02bfd42119451e9468dc58445>
287    fn as_mdb_env_set_flags_input(self) -> i32 {
288        match self {
289            Self::Enable => 1,
290            Self::Disable => 0,
291        }
292    }
293}