pijul nest
guest [sign in]

Proofreading and commenting of this crate (massive bug fixes included)

[?]
Feb 20, 2021, 9:55 PM
E4MD6T3LNOYWVFTFFWCUKRNS4M2XVSKRLDWPYHMZHGDNO2T5JREQC

Dependencies

  • [2] TSMS6W4D Fully commented implementation of Sized nodes + massive cleanup
  • [3] W2MIZD5B Single file databases + CRC for the root pages (checking the other pages makes everything very slow)
  • [4] 6DCQHIFP Minor changes after benchmarking
  • [5] LSQ6V7M6 Cleanup + docs
  • [6] WS4ZQM4R Debugging, tests, etc.
  • [7] AFKBHYVE Comparing the two implementations of leaves (sized/unsized). Sized are faster for writes, slower for reads.
  • [8] T7QB6QEP Adding debug.rs
  • [9] 52X5P7ND Cleaning up the unsized part
  • [10] UUUVNC4D Debugging/cleanup around cursors
  • [11] KMT3MF5N Drop a database
  • [12] YXKP4AIW New file locks, with multiple sets of free pages
  • [13] ONES3V46 reference counting works for put
  • [14] 6UVFCERM Formatting, debugging, etc.
  • [15] EYNN7RLS Tests++ (including UUID)
  • [16] OFINGD26 implementing prev() on cursors (+ some cleanup)
  • [17] OP6SVMOD Resetting history
  • [18] NXMFNPZ7 Comments + debugging drop
  • [19] EAAYH6BQ Debugging put
  • [20] KX3WVNZW Testing/debugging "rebalance causes split of the root"
  • [21] XEU2QVLC Debugging after plugging this into Pijul
  • [22] G4JEQLLX Debugging synchronisation
  • [23] OHUZ73MK Versions
  • [24] SO25TWFL A few features for integrating it into Pijul
  • [25] KM3JAFGP Adding a test for next/prev
  • [26] S4V4QZ5C Debugging reference-counting for put
  • [27] H3FVSQIQ Unsized pages
  • [28] AOX2XQIS Actually, with the correct functions, Unsized pages are always slower than Sized pages (especially for writing)
  • [29] OTWDDJE7 Trait/type cleanup
  • [30] Q7DRIBBR Debugging replace (which cannot be del+put)
  • [31] LROAI3NB Two iterators (convenience functions), along with tests to move cursors (put and del still destroy cursors though)
  • [32] MSRWB47Y Deletions at immutable leaves weren't really deleting anything
  • [33] HN6Z5DU4 Cleanup
  • [34] QYDGYIZR Split trait Representable into its mandatory part and an optional part
  • [35] X3QVVQIS More debugging (del seems to work now)
  • [36] 6DMPXOAT More debugging
  • [37] UAQX27N4 Tests
  • [38] YWFYZNLZ Cleanup + inter-process concurrency

Change contents

  • replacement in sanakirja/src/tests.rs at line 98
    [4.25811][4.577:608]()
    let n = 10_000_000 as u64;
    [4.25811]
    [4.4616]
    let n = 10_000_000u64;
  • replacement in sanakirja/src/tests.rs at line 115
    [4.947][4.947:977]()
    let n = 1_000_000 as u64;
    [4.947]
    [4.977]
    let n = 1_000_000u64;
  • replacement in sanakirja/src/tests.rs at line 128
    [4.1433][4.5228:5256](),[4.25872][4.5228:5256](),[4.5228][4.5228:5256]()
    let n = 100_000 as u64;
    [4.25872]
    [4.5256]
    let n = 100_000u64;
  • replacement in sanakirja/src/tests.rs at line 157
    [4.1550][4.9160:9186](),[4.25933][4.9160:9186](),[4.9160][4.9160:9186]()
    let n = 2_000 as u64;
    [4.25933]
    [4.9230]
    let n = 2_000u64;
  • replacement in sanakirja/src/tests.rs at line 175
    [4.443][4.443:490]()
    let n = 20 as u64;
    let i0 = 10 as u64;
    [4.443]
    [4.10810]
    let n = 20u64;
    let i0 = 10u64;
  • replacement in sanakirja/src/tests.rs at line 187
    [4.11108][4.11108:11145]()
    btree::iter(&txn, &db, None)
    [4.11108]
    [4.11145]
    iter(&txn, &db, None)
  • replacement in sanakirja/src/tests.rs at line 201
    [4.11504][4.13228:13252](),[4.13252][4.11528:11553](),[4.11528][4.11528:11553]()
    let n = 256 as u64;
    let i0 = 127 as u64;
    [4.11504]
    [4.490]
    let n = 256u64;
    let i0 = 127u64;
  • replacement in sanakirja/src/tests.rs at line 223
    [4.788][4.788:825]()
    btree::iter(&txn, &db, None)
    [4.788]
    [4.825]
    iter(&txn, &db, None)
  • replacement in sanakirja/src/tests.rs at line 239
    [4.25993][4.21018:21041]()
    let n = 19 as u64;
    [4.25993]
    [4.6021]
    let n = 19u64;
  • replacement in sanakirja/src/tests.rs at line 335
    [4.7597][4.15246:15365]()
    let mut curs: btree::cursor::Cursor<_, _, B> =
    btree::cursor::Cursor::new(txn, &db_free).unwrap();
    [4.7597]
    [4.7693]
    let mut curs: Cursor<_, _, B> = btree::cursor::Cursor::new(txn, &db_free).unwrap();
  • replacement in sanakirja/src/tests.rs at line 387
    [4.835][4.835:880]()
    let mut child = unsafe { libc::fork() };
    [4.835]
    [4.880]
    let child = unsafe { libc::fork() };
  • replacement in sanakirja/src/tests.rs at line 391
    [4.918][4.918:988]()
    let env = Env::new("/tmp/sanakirja0", 4096 * 20, 2).unwrap();
    [4.918]
    [4.988]
    let env = Env::new("/tmp/sanakirja0/db", 4096 * 20, 2).unwrap();
  • replacement in sanakirja/src/tests.rs at line 395
    [4.1069][4.1069:1139]()
    info!("started child mutable txn {:?}", txn.env.root.lock());
    [4.1069]
    [4.1139]
    info!("started child mutable txn {:?}", txn.root);
    assert_eq!(txn.root, 0);
  • replacement in sanakirja/src/tests.rs at line 402
    [4.15688][4.1297:1360](),[4.1297][4.1297:1360]()
    std::thread::sleep(std::time::Duration::from_secs(2));
    [4.15688]
    [4.1360]
    std::thread::sleep(std::time::Duration::from_millis(200));
  • edit in sanakirja/src/tests.rs at line 407
    [4.1522]
    [4.1522]
    assert_eq!(txn.root, 1);
  • replacement in sanakirja/src/tests.rs at line 413
    [4.1689][4.1689:1839]()
    assert!(t.elapsed().unwrap() >= std::time::Duration::from_millis(900));
    info!("started child mutable txn {:?}", txn.env.root.lock());
    [4.1689]
    [4.1839]
    assert!(t.elapsed().unwrap() >= std::time::Duration::from_millis(90));
    info!("started child mutable txn {:?}", txn.root);
  • replacement in sanakirja/src/tests.rs at line 418
    [3.2593][4.1997:2060](),[4.15757][4.1997:2060](),[4.1997][4.1997:2060]()
    std::thread::sleep(std::time::Duration::from_secs(1));
    [3.2593]
    [4.2060]
    std::thread::sleep(std::time::Duration::from_millis(100));
  • replacement in sanakirja/src/tests.rs at line 424
    [4.2156][4.2156:2219]()
    std::thread::sleep(std::time::Duration::from_secs(1));
    [4.2156]
    [4.2219]
    std::thread::sleep(std::time::Duration::from_millis(100));
  • edit in sanakirja/src/tests.rs at line 429
    [4.2312][4.2312:2365]()
    info!("starting parent txn {:?}", env.root);
  • replacement in sanakirja/src/tests.rs at line 430
    [4.2414][4.2414:2514]()
    info!("started parent txn");
    std::thread::sleep(std::time::Duration::from_secs(3));
    [4.2414]
    [4.2514]
    info!("started parent txn {:?}", txn.root);
    // The child didn't commit yet.
    assert_eq!(txn.root, 1);
    std::thread::sleep(std::time::Duration::from_millis(300));
  • replacement in sanakirja/src/tests.rs at line 438
    [4.2544][4.2544:2587]()
    unsafe { libc::wait(&mut child) };
    [4.2544]
    [4.2587]
    std::thread::sleep(std::time::Duration::from_millis(100));
    let txn = Env::txn_begin(&env).unwrap();
    info!("started parent txn {:?}", txn.root);
    // The parent committed, this is a new transaction.
    assert_eq!(txn.root, 0);
    let mut status = 1;
    unsafe { libc::wait(&mut status) };
    assert_eq!(status, 0);
  • edit in sanakirja/src/tests.rs at line 472
    [4.52816]
    [4.52816]
    }
    #[test]
    fn more_than_two_versions() {
    env_logger::try_init().unwrap_or(());
    let n = 5;
    let env = Env::new_anon(40960, n).unwrap();
    let mut txn = Env::mut_txn_begin(&env).unwrap();
    // Allocate two pages.
    for i in 0..n {
    let page = txn.alloc_page().unwrap();
    debug!("page = {:?}", page);
    txn.set_root(i, page.0.offset);
    }
    txn.commit().unwrap();
    for i in 0..n {
    let mut txn = Env::mut_txn_begin(&env).unwrap();
    // Free one of the pages.
    debug!("root(0) = {:?}", txn.root(i));
    txn.decr_rc(txn.root(i).unwrap()).unwrap();
    txn.remove_root(i);
    txn.commit().unwrap();
    }
    let mut txn = Env::mut_txn_begin(&env).unwrap();
    unsafe {
    let p = &*(env.mmaps.lock()[0].ptr.add(txn.root * PAGE_SIZE) as *const GlobalHeader);
    debug!("free page: 0x{:x}", u64::from_le(p.free_db));
    let db: Db<u64, ()> = Db {
    db: u64::from_le(p.free_db),
    k: std::marker::PhantomData,
    v: std::marker::PhantomData,
    p: std::marker::PhantomData,
    };
    for x in iter(&txn, &db, None).unwrap() {
    debug!("0x{:x}", x.unwrap().0);
    }
    }
    let page = txn.alloc_page().unwrap();
    debug!("page = {:?}", page);
  • replacement in sanakirja/src/tests.rs at line 535
    [4.303][4.303:353]()
    btree::get(&txn, &db, &i, None).unwrap();
    [4.303]
    [4.353]
    get(&txn, &db, &i, None).unwrap();
  • replacement in sanakirja/src/tests.rs at line 564
    [4.651][4.651:701]()
    btree::get(&txn, &db, &i, None).unwrap();
    [4.651]
    [4.701]
    get(&txn, &db, &i, None).unwrap();
  • replacement in sanakirja/src/tests.rs at line 609
    [4.2081][4.2081:2160]()
    assert_eq!(btree::get(&txn, &db, &k, None).unwrap(), Some((k, v)))
    [4.2081]
    [4.2160]
    assert_eq!(get(&txn, &db, &k, None).unwrap(), Some((k, v)))
  • replacement in sanakirja/src/tests.rs at line 624
    [4.2733][4.2733:2812]()
    assert_eq!(btree::get(&txn, &db, &k, None).unwrap(), Some((k, v)))
    [4.2733]
    [4.2812]
    assert_eq!(get(&txn, &db, &k, None).unwrap(), Some((k, v)))
  • replacement in sanakirja/src/tests.rs at line 765
    [4.13516][4.486:517](),[4.486][4.486:517]()
    P: btree::BTreePage<K, V>,
    [4.13516]
    [4.517]
    P: BTreePage<K, V>,
  • replacement in sanakirja/src/tests.rs at line 837
    [4.1903][4.1903:1971]()
    btree::get(&txn, &db, k.as_bytes(), None).unwrap(),
    [4.1903]
    [4.1971]
    get(&txn, &db, k.as_bytes(), None).unwrap(),
  • replacement in sanakirja/src/tests.rs at line 856
    [4.2643][4.2643:2686]()
    let k = lmdb_rs::MDB_val {
    [4.2643]
    [4.2686]
    let k = MDB_val {
  • replacement in sanakirja/src/tests.rs at line 873
    [4.3239][4.3239:3278]()
    let k = lmdb_rs::MDB_val {
    [4.3239]
    [4.3278]
    let k = MDB_val {
  • replacement in sanakirja/src/tests.rs at line 928
    [4.5264][4.5264:5329]()
    for (kv, n) in btree::rev_iter(&txn, &db, Some((&i0, None)))
    [4.5264]
    [4.5329]
    for (kv, n) in rev_iter(&txn, &db, Some((&i0, None)))
  • replacement in sanakirja/src/tests.rs at line 937
    [4.5497][4.5497:5579]()
    for (kv, n) in btree::iter(&txn, &db, Some((&i0, None))).unwrap().zip(i0..) {
    [4.5497]
    [4.10291]
    for (kv, n) in iter(&txn, &db, Some((&i0, None))).unwrap().zip(i0..) {
  • replacement in sanakirja/src/tests.rs at line 943
    [4.10334][4.10334:10408]()
    let mut it = btree::rev_iter(&txn, &db, Some((&100, None))).unwrap();
    [4.10334]
    [4.10408]
    let mut it = rev_iter(&txn, &db, Some((&100, None))).unwrap();
  • replacement in sanakirja/src/tests.rs at line 977
    [4.11523][4.11523:11545]()
    let n = 6 as u64;
    [4.11523]
    [4.11545]
    let n = 6u64;
  • edit in sanakirja/src/tests.rs at line 991
    [4.11952][4.11952:11959]()
    /*
  • edit in sanakirja/src/tests.rs at line 994
    [4.12086][4.12086:12135]()
    // add_refs(&txn, &db3, &mut refs).unwrap();
  • edit in sanakirja/src/tests.rs at line 1014
    [4.12742][4.12742:12749]()
    */
  • replacement in sanakirja/src/tests.rs at line 1022
    [4.4010][4.4010:4059]()
    let n = 1000 as u64;
    let i0 = 10 as u64;
    [4.4010]
    [4.4059]
    let n = 1000u64;
    let i0 = 10u64;
  • edit in sanakirja/src/lib.rs at line 1
    [4.79574]
    [3.2744]
    #![deny(
    missing_docs,
    trivial_casts,
    trivial_numeric_casts,
    unused_import_braces,
    unused_qualifications
    )]
  • edit in sanakirja/src/lib.rs at line 13
    [3.2979]
    [4.79575]
    //!
    //! The binary format of a Sanakirja database is the following:
    //!
    //! - There is a fixed number of "current versions", set at file
    //! initialisation. If a file has n versions, then for all k between 0
    //! and n-1 (included), the k^th page (i.e. the byte positions between
    //! `k * 4096` and `(k+1) * 4096`, also written as `k << 12` and
    //! `(k+1) << 12`) stores the data relative to that version, and is
    //! called the "root page" of that version.
    //!
    //! This is a way to handle concurrent access: indeed, mutable
    //! transactions do not exclude readers, but readers that started
    //! before the commit of a mutable transaction will keep reading the
    //! database as it was before the commit. However, this means that
    //! older versions of the database have to be kept "alive", and the
    //! "number of current versions" here is the limit on the number of
    //! versions that can be kept "alive" at the same time.
    //!
    //! When a reader starts, it takes a shared file lock on the file
    //! representing the youngest committed version. When a writer starts,
    //! it takes an exclusive file lock on the file representing the
    //! oldest committed version. This implies that if readers are still
    //! reading that version, the writer will wait for the exclusive lock.
    //!
    //! After taking a lock, the writer (also called "mutable
    //! transaction" or [`MutTxn`]) copies the entire root page of the
    //! youngest committed version onto the root page of the oldest
    //! committed version, hence erasing the root page of the oldest
    //! version.
    //!
    //! - Root pages have the following format: a 32-bytes header
    //! (described below), followed by 4064 bytes, usable in a more or
    //! less free format. The current implementation defines two methods
    //! on [`MutTxn`], [`MutTxn::set_root`] and [`MutTxn::remove_root`],
    //! treating that space as an array of type `[u64; 510]`. A reasonable
    //! use for these is to point to different datastructures allocated in
    //! the file, such as the offsets in the file to the root pages of B
    //! trees.
    //!
    //! Now, about the header, there's a version identifier on the first
    //! 16 bytes, followed by two bytes: `root` is the version used by the
    //! current mutable transaction (if there is current mutable
    //! transaction), or by the next mutable transaction (else). The
    //! `n_roots` field is the total number of versions.
    //!
    //! ```
    //! #[repr(C)]
    //! pub struct GlobalHeader {
    //! /// Version of Sanakirja
    //! pub version: u16,
    //! /// Which page is currently the root page? (only valid for page 0).
    //! pub root: u8,
    //! /// Total number of versions (or "root pages")
    //! pub n_roots: u8,
    //! /// CRC of this page.
    //! pub crc: u32,
    //! /// First free page at the end of the file (only valid for page 0).
    //! pub length: u64,
    //! /// Offset of the free list.
    //! pub free_db: u64,
    //! /// Offset of the RC database.
    //! pub rc_db: u64,
    //! }
    //! ```
  • replacement in sanakirja/src/lib.rs at line 81
    [3.2997][4.176:217](),[4.79615][4.176:217](),[4.217][4.13517:13607]()
    pub use environment::{Env, MutTxn, Txn};
    pub use sanakirja_core::{btree, direct_repr, CowPage, LoadPage, MutPage, Page, Storable};
    [3.2997]
    [4.312]
    pub use environment::{Commit, Env, MutTxn, RootDb, Txn};
    pub use sanakirja_core::{btree, direct_repr, LoadPage, Storable, UnsizedStorable};
  • edit in sanakirja/src/environment/muttxn.rs at line 15
    [4.2774]
    [4.80352]
    /// The root page of this transaction, which is 1 + the root
    /// written on page 0. The root written on page 0 changes at
    /// commit time.
    pub(crate) root: usize,
  • edit in sanakirja/src/environment/muttxn.rs at line 21
    [4.2579]
    [4.15956]
    /// Offset to the root of the B tree of free pages.
  • edit in sanakirja/src/environment/muttxn.rs at line 24
    [4.15982]
    [4.27437]
    /// Reference counts use a strange encoding, meant to avoid code
    /// bloat: indeed, the list of free pages uses `Db<u64, ()>`, so
    /// we're just reusing the same code here, encoding the reference
    /// counts in the 12 least significant bits of the keys, and the
    /// actual pages in the 52 most significant bits.
  • edit in sanakirja/src/environment/muttxn.rs at line 31
    [4.27473]
    [4.80470]
  • edit in sanakirja/src/environment/muttxn.rs at line 42
    [4.80851]
    [4.549]
    ///
    /// Since we can't reuse them in the same transaction, another
    /// option would be to put them directly into the table of free
    /// pages. However, since calls to `put` may allocate and free
    /// pages, this could recurse infinitely, which is why we store
    /// them outside of the file.
  • edit in sanakirja/src/environment/muttxn.rs at line 52
    [4.80903]
    [4.99]
    /// When dropping a transaction, we need to unlock the read-write
    /// locks internal to this process, and possibly the file locks.
  • replacement in sanakirja/src/environment/muttxn.rs at line 58
    [4.81041][4.81041:81081](),[4.81081][4.3039:3091](),[4.3091][4.148:317](),[4.81081][4.148:317]()
    let root = env.root.lock();
    debug!("unlock exclusive {:?}", *root);
    env.roots[*root].rw.unlock_exclusive();
    if let Some(ref f) = env.roots[*root].lock_file {
    f.unlock().unwrap_or(())
    }
    [4.81041]
    [4.81129]
    env.mut_txn_unlock().unwrap_or(());
    env.roots[self.root].rw.unlock_exclusive();
    env.unlock(self.root).unwrap_or(())
  • replacement in sanakirja/src/environment/muttxn.rs at line 65
    [4.81148][4.81148:81188]()
    /// Transactions that can be committed.
    [4.81148]
    [4.81188]
    /// Transactions that can be committed. This trait is an abstraction
    /// over mutable transactions and their subtransactions.
  • edit in sanakirja/src/environment/muttxn.rs at line 72
    [4.81284]
    [4.318]
    /// The following is very easy, we're just extending all values of the
    /// current transaction with values of the subtransaction.
  • edit in sanakirja/src/environment/muttxn.rs at line 103
    [4.403]
    [4.81964]
    #[cfg(feature = "mmap")]
    fn mut_txn_lock(&self) -> Result<(), Error> {
    self.mut_txn_lock.lock();
    if let Some(ref f) = self.file {
    f.lock_exclusive()?;
    }
    Ok(())
    }
    #[cfg(not(feature = "mmap"))]
    fn mut_txn_lock(&self) -> Result<(), Error> {
    self.mut_txn_lock.lock();
    Ok(())
    }
    #[cfg(feature = "mmap")]
    fn mut_txn_unlock(&self) -> Result<(), Error> {
    unsafe {
    self.mut_txn_lock.unlock();
    }
    if let Some(ref f) = self.file {
    f.unlock()?
    }
    Ok(())
    }
    #[cfg(not(feature = "mmap"))]
    fn mut_txn_unlock(&self) -> Result<(), Error> {
    unsafe {
    self.mut_txn_lock.unlock();
    }
    Ok(())
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 141
    [4.82179][4.82179:82396](),[4.82396][4.404:578](),[4.82513][4.579:623](),[4.623][4.3116:3227](),[4.660][4.82513:82586](),[4.3227][4.82513:82586](),[4.82513][4.82513:82586](),[4.82586][4.661:808](),[4.808][4.3228:3329]()
    let (header, free, rc) = {
    let env_ = env.borrow();
    let maps = env_.mmaps.lock()[0].ptr;
    let v = env_.root.lock();
    let n = env_.roots.len();
    env_.roots[*v].rw.lock_exclusive();
    if let Some(ref f) = env_.roots[*v].lock_file {
    f.lock_exclusive()?
    }
    // Root of the last MutTxn.
    let v0 = (*v + n - 1) % env_.roots.len();
    debug!("v = {:?} v0 = {:?}", v, v0);
    // Copy the roots of the last transaction onto this one.
    let page_ptr = maps.offset((v0 * PAGE_SIZE) as isize);
    let next_page_ptr = maps.offset((*v * PAGE_SIZE) as isize);
    std::ptr::copy_nonoverlapping(page_ptr.add(8), next_page_ptr.add(8), PAGE_SIZE - 8);
    [4.82179]
    [4.82828]
    let env_ = env.borrow();
  • replacement in sanakirja/src/environment/muttxn.rs at line 143
    [4.82829][4.82829:82918](),[4.82918][3.3014:3051]()
    let header = GlobalHeader::from_le(&*(page_ptr as *const GlobalHeader));
    env_.check_crc(v0)?;
    [4.82829]
    [3.3051]
    // First, take an exclusive file lock on the whole file to
    // make sure that no other process is starting a mutable
    // transaction at the same time. The worst that can happen
    // here is if the other process commits while we're still
    // waiting for a lock on the current page, because if that
    // happens, this new transaction will erase the
    // transaction in the other process.
    env_.mut_txn_lock()?;
    // Then, we can lock the root page of this transaction.
    let maps = env_.mmaps.lock()[0].ptr;
    let root = (&*(maps as *const GlobalHeader)).root as usize;
    debug!("BEGIN_TXN root = {:?}", root);
    env_.roots[root].rw.lock_exclusive();
    env_.lock_exclusive(root)?;
    // Root of the last MutTxn.
    let v0 = (root + env_.roots.len() - 1) % env_.roots.len();
    env_.check_crc(v0)?;
    // Copy the root page of the last transaction onto this
    // one.
    let page_ptr = maps.offset((v0 * PAGE_SIZE) as isize);
    let next_page_ptr = maps.offset((root * PAGE_SIZE) as isize);
    std::ptr::copy_nonoverlapping(page_ptr.add(8), next_page_ptr.add(8), PAGE_SIZE - 8);
  • replacement in sanakirja/src/environment/muttxn.rs at line 167
    [3.3052][4.15983:16026](),[4.82918][4.15983:16026](),[4.16026][4.83075:83210](),[4.83075][4.83075:83210](),[4.83210][4.16027:16069](),[4.16069][4.52819:52978](),[4.83388][4.52819:52978](),[4.52978][4.83446:83523](),[4.83446][4.83446:83523](),[4.83523][4.12750:12926](),[4.12926][4.83523:83538](),[4.83523][4.83523:83538]()
    let free = header.free_db;
    let rc = if header.rc_db == 0 {
    None
    } else {
    Some(btree::Db {
    db: header.rc_db,
    k: std::marker::PhantomData,
    v: std::marker::PhantomData,
    p: std::marker::PhantomData,
    })
    };
    (header, free, rc)
    };
    let length = if header.length == 0 {
    (PAGE_SIZE as u64) * (header.n_roots as u64)
    } else {
    header.length
    };
    [3.3052]
    [4.83538]
    // Finally, read the header and start the transaction.
    let header = GlobalHeader::from_le(&*(next_page_ptr as *const GlobalHeader));
    debug!("n_roots = {:?}", header.n_roots);
  • edit in sanakirja/src/environment/muttxn.rs at line 172
    [4.83583]
    [4.83583]
    root,
  • replacement in sanakirja/src/environment/muttxn.rs at line 174
    [4.83611][4.83611:83631](),[4.83631][4.12927:12951](),[4.12951][4.83822:83844](),[4.83822][4.83822:83844]()
    rc,
    length,
    free,
    [4.83611]
    [4.83844]
    rc: if header.rc_db == 0 {
    None
    } else {
    Some(btree::Db::from_page(header.rc_db))
    },
    length: if header.length == 0 {
    (PAGE_SIZE as u64) * (header.n_roots as u64)
    } else {
    header.length
    },
    free: header.free_db,
  • replacement in sanakirja/src/environment/muttxn.rs at line 206
    [4.84166][4.84166:84183](),[4.84183][4.16070:16153](),[4.16153][4.84575:85021](),[4.3232][4.84575:85021](),[4.84575][4.84575:85021]()
    unsafe {
    let mut free_db: btree::Db<u64, ()> = btree::Db::from_page(self.free);
    while !self.free_owned_pages.is_empty() || !self.free_pages.is_empty() {
    let mut free_owned_pages =
    std::mem::replace(&mut self.free_owned_pages, Vec::new());
    let mut free_pages = std::mem::replace(&mut self.free_pages, Vec::new());
    for p in free_owned_pages.drain(..).chain(free_pages.drain(..)) {
    btree::put(&mut self, &mut free_db, &p, &())?;
    [4.84166]
    [4.85021]
    debug!("COMMIT");
    // If there's no tree of free pages, and no pages to free,
    // don't bother with free pages at all (don't even allocate a
    // tree).
    let free_db =
    if self.free == 0 && self.free_owned_pages.is_empty() && self.free_pages.is_empty() {
    None
    } else {
    // Else, allocate or load the tree of free pages.
    let mut free_db: btree::Db<u64, ()> = if self.free == 0 {
    btree::create_db(&mut self)?
    } else {
    btree::Db::from_page(self.free)
    };
    debug!("free_db = {:?}", free_db);
    // Adding all the pages freed during the transaction to the
    // tree of free pages. If this call to `btree::put` frees
    // pages, add them again. This converges in at most log n
    // iterations (where n is the total number of free pages).
    // First, set the free table to 0 in this transaction, to
    // avoid recursing in the calls to `put` below (indeed,
    // the table of free pages is used when allocating new
    // pages, which may happen in a call to `put`).
    self.free = 0;
    // Then, while there are pages to free, free them. Since
    // the calls to `put` below might free pages (and add
    // pages to these two vectors), the (outer) loop might run
    // for more than one iteration.
    while !self.free_pages.is_empty() || !self.free_owned_pages.is_empty() {
    while let Some(p) = self.free_pages.pop() {
    let p = p & !0xfff;
    btree::put(&mut self, &mut free_db, &p.to_le(), &())?;
    }
    while let Some(p) = self.free_owned_pages.pop() {
    let p = p & !0xfff;
    btree::put(&mut self, &mut free_db, &p.to_le(), &())?;
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 247
    [4.85039][4.85039:85113](),[4.85113][3.3233:3265]()
    }
    for p in self.occupied_owned_pages.iter_mut() {
    clear_dirty(p);
    [4.85039]
    [4.85146]
    Some(free_db)
    };
    // Clear the dirty bit of all pages we've touched. If they've
    // been freed and have already been flushed by the kernel, we
    // don't want to resurrect them to the main memory, so we
    // check that.
    let mut occ = std::mem::replace(&mut self.occupied_owned_pages, Vec::new());
    for p in occ.iter_mut() {
    if let Some(ref free_db) = free_db {
    if let Some((pp, ())) = btree::get(&self, free_db, &p.0.offset, None)? {
    if *pp == p.0.offset {
    continue;
    }
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 262
    [4.85160][4.85160:85246](),[4.85246][4.4445:4489](),[4.4489][4.3435:3518](),[4.3518][4.85383:85441](),[4.85383][4.85383:85441](),[4.85441][4.16154:16212](),[4.16212][4.3233:3269](),[4.85506][4.3233:3269]()
    let env = self.env.borrow();
    let mut maps = env.mmaps.lock();
    let mut root = env.root.lock();
    let globptr = maps[0].ptr.add(*root * PAGE_SIZE) as *mut GlobalHeader;
    (&mut *globptr).length = self.length.to_le();
    (&mut *globptr).free_db = free_db.db.to_le();
    self.free = free_db.db;
    [4.85160]
    [4.85506]
    clear_dirty(p);
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 265
    [4.85507][4.3519:3663]()
    let root_dbs = std::slice::from_raw_parts_mut(
    maps[0].ptr.add(*root * PAGE_SIZE + GLOBAL_HEADER_SIZE) as *mut u64,
    [4.85507]
    [4.3663]
    let env = self.env.borrow();
    let mut maps = env.mmaps.lock();
    // Get this transaction's root page.
    let globptr =
    unsafe { &mut *(maps[0].ptr.add(self.root * PAGE_SIZE) as *mut GlobalHeader) };
    // Set the length and free database.
    globptr.length = self.length.to_le();
    if let Some(free_db) = free_db {
    debug!("COMMIT: free_db = 0x{:x}", free_db.db);
    globptr.free_db = free_db.db.to_le();
    }
    // Set the "root databases" modified by this transaction.
    let root_dbs = unsafe {
    std::slice::from_raw_parts_mut(
    maps[0].ptr.add(self.root * PAGE_SIZE + GLOBAL_HEADER_SIZE) as *mut u64,
  • replacement in sanakirja/src/environment/muttxn.rs at line 282
    [4.3688][4.3688:3894](),[4.3894][4.12952:13025]()
    );
    for (&r, rr) in self.roots.iter().zip(root_dbs.iter_mut()) {
    debug!("root_db: {:?}", rr as *mut u64);
    debug!("committing root: {:?} {:?}", r, rr);
    if r > 0 {
    *rr = r
    }
    [4.3688]
    [4.3918]
    )
    };
    for (&r, rr) in self.roots.iter().zip(root_dbs.iter_mut()) {
    debug!("root_db: {:?}", rr);
    debug!("committing root: {:?} {:?}", r, rr);
    if r > 0 {
    *rr = r
  • edit in sanakirja/src/environment/muttxn.rs at line 290
    [4.3932]
    [4.3932]
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 292
    [4.3933][3.3266:3323]()
    set_crc(maps[0].ptr.add(*root * PAGE_SIZE));
    [4.3933]
    [3.3323]
    // Set the root page's CRC.
    unsafe {
    set_crc(maps[0].ptr.add(self.root * PAGE_SIZE));
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 297
    [3.3324][4.85507:85584](),[4.3933][4.85507:85584](),[4.85507][4.85507:85584](),[4.85584][3.3325:3400]()
    // Moving the root page, both on page 0, and on the environment.
    (&mut *(maps[0].ptr as *mut GlobalHeader)).root = *root as u8;
    [3.3324]
    [4.85828]
    // Move the current global root page by one page on page 0.
    unsafe {
    (&mut *(maps[0].ptr as *mut GlobalHeader)).root =
    (self.root as u8 + 1) % (env.roots.len() as u8);
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 303
    [4.85829][4.85829:85909](),[4.85909][4.3270:3320](),[4.3320][4.907:1021](),[4.85909][4.907:1021](),[4.1021][4.3321:3368](),[4.3368][4.1021:1076](),[4.1021][4.1021:1076](),[4.1076][4.85909:85966](),[4.85909][4.85909:85966](),[4.85966][4.3369:3420](),[4.3420][4.85966:85985](),[4.85966][4.85966:85985]()
    for m in maps.iter_mut() {
    m.flush()?
    }
    debug!("commit: unlock {:?}", *root);
    env.roots[*root].rw.unlock_exclusive();
    if let Some(ref f) = env.roots[*root].lock_file {
    debug!("commit: unlock file");
    f.unlock().unwrap_or(())
    }
    *env.first_unused_page.lock() = self.length;
    *root = (*root + 1) % env.roots.len();
    Ok(())
    [4.85829]
    [4.85985]
    // Flush all the maps.
    for m in maps.iter_mut() {
    m.flush()?
  • edit in sanakirja/src/environment/muttxn.rs at line 307
    [4.85995]
    [4.85995]
    // And finally, unlock the root page in the environment.
    debug!("commit: unlock {:?}", self.root);
    unsafe { env.roots[self.root].rw.unlock_exclusive() };
    // Unlock the root page on the file lock (if relevant).
    env.unlock(self.root)?;
    // And unlock the global mutable transaction mutex.
    env.mut_txn_unlock()?;
    debug!("/COMMIT");
    Ok(())
  • edit in sanakirja/src/environment/muttxn.rs at line 322
    [4.1116]
    [4.86054]
    /// Setting the `num`th element of the initial page, treated as a
    /// `[u64; 510]`, to `value`. This doesn't actually write anything
    /// to that page, since that page is written during the commit.
    ///
    /// In the current implementation, `value` is probably going to be
    /// the offset in the file of the root page of a B tree.
  • edit in sanakirja/src/environment/muttxn.rs at line 335
    [4.1567]
    [4.1567]
    /// Setting the `num`th element of the initial page, treated as a
    /// [u64; 510].
  • edit in sanakirja/src/environment/muttxn.rs at line 338
    [4.1615]
    [4.1615]
    if self.roots.get(num).is_none() {
    self.roots.resize(num + 1, 0u64);
    }
  • edit in sanakirja/src/environment/muttxn.rs at line 344
    [4.86151]
    [4.86151]
    /// Add the page at offset `offset` to the list of free pages that
    /// were allocated by this `MutTxn` (and hence can be reallocated
    /// by the same transaction).
  • edit in sanakirja/src/environment/muttxn.rs at line 352
    [4.86251]
    [4.86251]
    /// Add the page at offset `offset` to the list of free pages
    /// allocated by a previous transaction, and hence may still be
    /// accessible by other transactions.
  • replacement in sanakirja/src/environment/muttxn.rs at line 360
    [4.86338][4.86338:86463]()
    /// Pop a free page from the list of free pages.
    fn free_pages_pop(&mut self) -> Result<Option<u64>, crate::Error> {
    [4.86338]
    [4.16213]
    /// Pop a free page from the B tree of free pages.
    fn free_pages_pop(&mut self) -> Result<Option<u64>, Error> {
  • edit in sanakirja/src/environment/muttxn.rs at line 363
    [4.16241]
    [4.86498]
    debug!("self.free = 0");
  • replacement in sanakirja/src/environment/muttxn.rs at line 366
    [4.86537][4.27537:27590](),[4.4093][4.86624:86651](),[4.27590][4.86624:86651](),[4.86624][4.86624:86651](),[4.86651][4.53115:53238](),[4.53238][4.86697:86708](),[4.86697][4.86697:86708]()
    let mut db: btree::Db<u64, ()> = btree::Db {
    db: self.free,
    k: std::marker::PhantomData,
    v: std::marker::PhantomData,
    p: std::marker::PhantomData,
    };
    [4.86537]
    [4.16242]
    let mut db: btree::Db<u64, ()> = btree::Db::from_page(self.free);
  • replacement in sanakirja/src/environment/muttxn.rs at line 368
    [4.16305][4.86764:86826](),[4.86764][4.86764:86826]()
    let f = if let Some((f, ())) = curs.set_last(self)? {
    [4.16305]
    [4.86826]
    let mut f = if let Some((f, ())) = curs.set_last(self)? {
  • edit in sanakirja/src/environment/muttxn.rs at line 373
    [4.86898]
    [2.42002]
    // Get the last page that is also free in all other versions.
    loop {
    debug!("trying 0x{:x}", f);
    if self.free_for_all(f)? {
    break;
    } else if let Some((f_, ())) = curs.prev(self)? {
    f = *f_
    } else {
    debug!("no more candidate");
    return Ok(None);
    }
    }
    self.free = 0;
  • edit in sanakirja/src/environment/muttxn.rs at line 391
    [4.87008]
    [4.87008]
    // Check whether this page is also free for the other
    // versions.
    fn free_for_all(&self, f: u64) -> Result<bool, Error> {
    let env = self.env.borrow();
    // We already know it's free for the youngest previous
    // transaction and for the current one (because the tree of
    // free pages was copied from there), so we only have
    // `self.roots.len() - 2` root pages to check.
    for i in 1..env.roots.len() - 1 {
    let db: btree::Db<u64, ()> = unsafe {
    let p = &*(env.mmaps.lock()[0]
    .ptr
    .add(((self.root + i) % env.roots.len()) * PAGE_SIZE)
    as *const GlobalHeader);
    if f >= u64::from_le(p.length) {
    // Page `f` was allocated strictyl after
    // transaction `i`.
    continue;
    }
    if p.free_db == 0 {
    // This version doesn't have any free page.
    return Ok(false);
    }
    btree::Db::from_page(p.free_db)
    };
    if let Some((&f_, ())) = btree::get(self, &db, &f, None)? {
    if f_ != f {
    return Ok(false);
    }
    }
    }
    Ok(true)
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 430
    [4.87180][4.87180:87264]()
    // If we have allocated and freed a page in this transaction, use it first.
    [4.87180]
    [4.37524]
    // If we have allocated and freed a page in this transaction,
    // use it first.
  • replacement in sanakirja/src/environment/muttxn.rs at line 433
    [4.37584][4.13124:13175]()
    debug!("free owned pop {:?}", offset);
    [4.37584]
    [4.37584]
    debug!("free owned pop 0x{:x}", offset);
  • replacement in sanakirja/src/environment/muttxn.rs at line 442
    [4.37871][4.13176:13231]()
    debug!("free pages pop {:?}", offset);
    [4.37871]
    [4.37871]
    debug!("free pages pop 0x{:x}", offset);
  • edit in sanakirja/src/environment/muttxn.rs at line 444
    [4.37948][4.37948:38010]()
    let page = MutPage(CowPage { data, offset });
  • replacement in sanakirja/src/environment/muttxn.rs at line 445
    [4.38052][4.38052:38114](),[4.38114][4.87901:87926](),[4.87901][4.87901:87926]()
    .push(MutPage(CowPage { offset, data }));
    Ok(page)
    [4.38052]
    [4.87926]
    .push(MutPage(CowPage { data, offset }));
    Ok(MutPage(CowPage { data, offset }))
  • replacement in sanakirja/src/environment/muttxn.rs at line 449
    [4.88000][4.88000:88072]()
    debug!("allocate in the free space {:?}", self.length);
    [4.88000]
    [4.38115]
    debug!("allocate in the free space 0x{:x}", self.length);
  • edit in sanakirja/src/environment/muttxn.rs at line 453
    [4.38235][4.4094:4156]()
    let page = MutPage(CowPage { data, offset });
  • replacement in sanakirja/src/environment/muttxn.rs at line 455
    [4.38418][4.88385:88410](),[4.4260][4.88385:88410](),[4.88385][4.88385:88410]()
    Ok(page)
    [4.4260]
    [4.88410]
    Ok(MutPage(CowPage { data, offset }))
  • replacement in sanakirja/src/environment/muttxn.rs at line 460
    [4.88441][4.1645:1707](),[4.1707][4.1509:1560]()
    fn decr_rc(&mut self, off: u64) -> Result<usize, Error> {
    debug!("decr_rc {:?} {:?}", off, self.rc);
    [4.88441]
    [4.88500]
    /// Increment the reference count for page `off`.
    fn incr_rc(&mut self, off: u64) -> Result<usize, Error> {
  • replacement in sanakirja/src/environment/muttxn.rs at line 464
    [4.16374][2.42065:42106](),[2.42106][4.38419:38483](),[4.88658][4.38419:38483]()
    curs.set(self, &off, None)?;
    let rc = if let Some((rc, ())) = curs.next(self)? {
    [4.16374]
    [4.38483]
    let rc = if let Some((rc, _)) = curs.set(self, &off, None)? {
  • replacement in sanakirja/src/environment/muttxn.rs at line 466
    [4.38524][4.38524:38548]()
    *rc
    [4.38524]
    [4.38548]
    *rc & 0xfff
  • edit in sanakirja/src/environment/muttxn.rs at line 473
    [4.38650][4.625:671]()
    debug!("decr_rc, rc = {:?}", rc);
  • replacement in sanakirja/src/environment/muttxn.rs at line 474
    [4.1789][4.38683:38739](),[4.38683][4.38683:38739](),[4.38739][4.672:934](),[4.934][4.1561:1615](),[4.1752][4.1935:1992](),[4.4292][4.1935:1992](),[4.1615][4.1935:1992](),[4.38880][4.1935:1992]()
    btree::del(self, &mut rc_, &rc, None)?;
    if rc & 0xfff > 2 {
    btree::put(self, &mut rc_, &(rc - 1), &())?;
    self.rc = Some(rc_);
    } else {
    // Implicit "1".
    self.rc = Some(rc_)
    }
    return Ok((rc & 0xfff) as usize - 1);
    } else {
    self.rc = Some(rc_)
    [4.1789]
    [4.89113]
    btree::del::del_at_cursor(self, &mut rc_, &mut curs)?;
  • edit in sanakirja/src/environment/muttxn.rs at line 476
    [4.89127]
    [4.89127]
    assert!(rc + 1 <= 0xfff);
    btree::put(self, &mut rc_, &(off | (rc + 1)), &())?;
    self.rc = Some(rc_);
    Ok(rc as usize + 1)
    } else {
    let mut rc = btree::create_db(self)?;
    btree::put(self, &mut rc, &(off | 2), &())?;
    self.rc = Some(rc);
    Ok(2)
  • edit in sanakirja/src/environment/muttxn.rs at line 486
    [4.89137][4.89137:89166](),[4.89166][4.1753:1767]()
    self.free_page(off);
    Ok(0)
  • edit in sanakirja/src/environment/muttxn.rs at line 488
    [4.89188]
    [4.1768]
    fn decr_rc(&mut self, off: u64) -> Result<usize, Error> {
    let rc = self.decr_rc_(off)?;
    if rc == 0 {
    self.free_page(off);
    }
    Ok(rc)
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 497
    [4.1836][4.1616:1673]()
    debug!("decr_rc_owned {:?} {:?}", off, self.rc);
    [4.1836]
    [4.89281]
    let rc = self.decr_rc_(off)?;
    if rc == 0 {
    self.free_owned_page(off);
    }
    Ok(rc)
    }
    }
    impl<E: Borrow<Env>, A> MutTxn<E, A> {
    /// Decrement the reference count of page `off`, freeing that page
    /// if the RC reaches 0 after decrementing it.
    fn decr_rc_(&mut self, off: u64) -> Result<usize, Error> {
    debug!("decr_rc 0x{:x} {:?}", off, self.rc);
    // If there's no RC table, free the page. Also, in order to
    // avoid infinite recursion (since `del` and `put` below might
    // free pages), we `take` the reference counter table.
  • edit in sanakirja/src/environment/muttxn.rs at line 517
    [2.42148]
    [4.38930]
    // The reference count is stored as the 12 LSBs of the
    // keys. If the page isn't in the RC table, the count is
    // 1.
  • replacement in sanakirja/src/environment/muttxn.rs at line 529
    [4.39178][4.2037:2074]()
    debug!("rc = {:?}", rc);
    [4.39178]
    [4.2074]
    debug!("decr_rc, rc = 0x{:x}", rc);
  • replacement in sanakirja/src/environment/muttxn.rs at line 531
    [4.2098][4.1674:1705]()
    debug!("del");
    [4.2098]
    [4.39211]
    // If the reference count is strictly more than 2,
    // replace the reference count with a decremented
    // value.
  • edit in sanakirja/src/environment/muttxn.rs at line 535
    [4.39267][4.1706:1738]()
    debug!("/del");
  • replacement in sanakirja/src/environment/muttxn.rs at line 539
    [4.1102][4.1102:1180]()
    // Implicit "1".
    self.rc = Some(rc_);
    [4.1102]
    [4.1180]
    // Else, we don't free the page, but don't add the
    // page back, since this is an implicit value of
    // "1" for the reference count.
    self.rc = Some(rc_)
  • replacement in sanakirja/src/environment/muttxn.rs at line 546
    [4.2265][4.2265:2302]()
    self.rc = Some(rc_);
    [4.2265]
    [4.89949]
    self.rc = Some(rc_)
  • edit in sanakirja/src/environment/muttxn.rs at line 549
    [4.90006][4.90006:90041]()
    self.free_owned_page(off);
  • edit in sanakirja/src/environment/muttxn.rs at line 550
    [4.1896][4.90056:90063](),[4.90056][4.90056:90063](),[4.90063][4.1897:1959](),[4.1959][4.2303:2340](),[4.90122][4.2303:2340](),[4.2340][4.90122:90170](),[4.90122][4.90122:90170](),[4.90170][4.16444:16512](),[4.16512][2.42149:42223](),[2.42223][4.39474:39547](),[4.39474][4.39474:39547](),[4.39547][4.90461:90486](),[4.90461][4.90461:90486](),[4.90486][4.39548:39570](),[4.39570][4.90552:90591](),[4.90552][4.90552:90591](),[4.90591][4.39571:39628](),[4.39628][2.42224:42295](),[4.39694][4.90653:90667](),[2.42295][4.90653:90667](),[4.90653][4.90653:90667](),[4.90667][4.39695:39798](),[4.39798][4.1960:2025](),[4.2025][4.2341:2408](),[4.90699][4.2341:2408](),[4.2408][4.4325:4382](),[4.4382][4.2463:2580](),[4.2463][4.2463:2580](),[4.2580][4.2026:2044](),[4.2044][4.90699:90709](),[4.2580][4.90699:90709](),[4.90699][4.90699:90709]()
    }
    fn incr_rc(&mut self, off: u64) -> Result<usize, Error> {
    debug!("incr_rc {:?}", off);
    if let Some(mut rc_) = self.rc.take() {
    let mut curs = btree::cursor::Cursor::new(self, &rc_)?;
    let rc = if let Some((rc, _)) = curs.set(self, &off, None)? {
    if *rc & !0xfff == off {
    *rc & 0xfff
    } else {
    1
    }
    } else {
    1
    };
    if rc > 1 {
    btree::del::del_at_cursor(self, &mut rc_, &mut curs)?;
    }
    assert!(rc + 1 <= 0xfff);
    btree::put(self, &mut rc_, &(off | (rc + 1)), &())?;
    self.rc = Some(rc_);
    Ok(rc as usize + 1)
    } else {
    let mut rc = btree::create_db(self)?;
    debug!("put in RC: {:?} {:?}", rc, off | 2);
    btree::put(self, &mut rc, &(off | 2), &())?;
    debug!("done");
    self.rc = Some(rc);
    Ok(2)
    }
  • replacement in sanakirja/src/environment/muttxn.rs at line 554
    [4.1255][4.90812:90843](),[4.90812][4.90812:90843]()
    type Error = crate::Error;
    [4.1255]
    [4.39799]
    type Error = Error;
  • replacement in sanakirja/src/environment/muttxn.rs at line 577
    [4.4392][4.4392:4442](),[4.4442][4.13608:13702](),[4.16648][4.4529:4562](),[4.13702][4.4529:4562](),[4.12945][4.4529:4562](),[4.4529][4.4529:4562](),[4.4562][4.16649:16704]()
    impl<E: Borrow<Env>, T> RootDb for MutTxn<E, T> {
    fn root_db<K: Storable + ?Sized, V: Storable + ?Sized, P: crate::btree::BTreePage<K, V>>(
    &self,
    n: usize,
    ) -> Option<sanakirja_core::btree::Db_<K, V, P>> {
    [4.4392]
    [4.4672]
    impl<E: Borrow<Env>, T> MutTxn<E, T> {
    /// Low-level method to get the root page number `n`, if that page
    /// isn't a B tree (use the [`RootDb`] trait else).
    pub fn root(&self, n: usize) -> Option<u64> {
  • replacement in sanakirja/src/environment/muttxn.rs at line 582
    [4.4718][4.16705:16776](),[4.16776][4.53239:53374](),[4.4808][4.53239:53374](),[4.53374][4.16777:16792]()
    Some(sanakirja_core::btree::Db_ {
    db: *db,
    k: std::marker::PhantomData,
    v: std::marker::PhantomData,
    p: std::marker::PhantomData,
    })
    [4.4718]
    [4.4874]
    Some(*db)
  • edit in sanakirja/src/environment/muttxn.rs at line 587
    [4.4984][4.4984:5032]()
    let root = env.root.lock();
  • replacement in sanakirja/src/environment/muttxn.rs at line 590
    [4.13005][4.13005:13082]()
    .add(*root * PAGE_SIZE + GLOBAL_HEADER_SIZE + 8 * n)
    [4.13005]
    [4.13082]
    .add(self.root * PAGE_SIZE + GLOBAL_HEADER_SIZE + 8 * n)
  • replacement in sanakirja/src/environment/muttxn.rs at line 594
    [4.5228][4.16793:16875](),[4.16875][4.53375:53534](),[4.5333][4.53375:53534](),[4.53534][4.16876:16899]()
    Some(sanakirja_core::btree::Db_ {
    db,
    k: std::marker::PhantomData,
    v: std::marker::PhantomData,
    p: std::marker::PhantomData,
    })
    [4.5228]
    [4.5415]
    Some(db)
  • edit in sanakirja/src/environment/muttxn.rs at line 599
    [4.5501]
    [4.5501]
    }
    }
    }
    impl<E: Borrow<Env>, T> RootDb for MutTxn<E, T> {
    // Just call method `root` and convert the result to a `Db`.
    fn root_db<K: Storable + ?Sized, V: Storable + ?Sized, P: crate::btree::BTreePage<K, V>>(
    &self,
    n: usize,
    ) -> Option<sanakirja_core::btree::Db_<K, V, P>> {
    if let Some(db) = self.root(n) {
    Some(sanakirja_core::btree::Db_::from_page(db))
    } else {
    None
  • replacement in sanakirja/src/environment/mod.rs at line 4
    [4.1299][4.5512:5550]()
    use parking_lot::lock_api::RawRwLock;
    [4.1299]
    [4.5550]
    use parking_lot::lock_api::{RawMutex, RawRwLock};
  • replacement in sanakirja/src/environment/mod.rs at line 21
    [4.91811][4.91811:91833]()
    use global_header::*;
    [4.91811]
    [4.91833]
    pub(crate) use global_header::*;
  • edit in sanakirja/src/environment/mod.rs at line 27
    [4.91909]
    [4.91909]
    /// A chunk of memory, possibly of a memory-mapped file, or allocated
    /// with `std::alloc`.
  • replacement in sanakirja/src/environment/mod.rs at line 50
    [4.93218][4.93218:93379]()
    /// Environment, required to start any transactions. Thread-safe, but
    /// opening the same database several times in the same process is not
    /// cross-platform.
    [4.92836]
    [4.1534]
    /// An environment, which may be either a memory-mapped file, or
    /// memory allocated with [`std::alloc`].
  • replacement in sanakirja/src/environment/mod.rs at line 57
    [4.93699][4.93699:93734]()
    first_unused_page: Mutex<u64>,
    [4.93699]
    [4.93734]
    mut_txn_lock: parking_lot::RawMutex,
  • replacement in sanakirja/src/environment/mod.rs at line 59
    [4.93735][4.1552:1617](),[4.1617][4.5674:5709](),[4.5709][4.93799:93802](),[4.93799][4.93799:93802](),[4.93802][4.1618:1831]()
    roots: Vec<RootLock>,
    // Root number of the next MutTxn.
    pub(crate) root: Mutex<usize>,
    }
    struct RootLock {
    /// It is undefined behavior to have a file mmapped for than once.
    #[cfg(feature = "mmap")]
    lock_file: Option<std::fs::File>,
    rw: parking_lot::RawRwLock,
    n_txn: AtomicUsize,
    [4.93735]
    [4.1831]
    pub(crate) roots: Vec<RootLock>,
  • edit in sanakirja/src/environment/mod.rs at line 65
    [4.93871][4.93871:93896](),[4.93896][4.1891:1911](),[4.1911][4.93922:93947](),[4.93922][4.93922:93947](),[4.93947][4.1912:1961](),[4.1961][4.94005:94033](),[4.94005][4.94005:94033](),[4.94061][4.94061:94080]()
    #[cfg(feature = "mmap")]
    impl Drop for Env {
    fn drop(&mut self) {
    for map in self.mmaps.lock().drain(..) {
    drop(map.mmap);
    }
    }
    }
  • replacement in sanakirja/src/environment/mod.rs at line 75
    [4.94324][4.94324:94354](),[4.94354][4.1983:2016](),[4.2016][4.94393:94405](),[4.94393][4.94393:94405](),[4.94444][4.94444:94461](),[4.94461][4.94461:94464](),[4.94464][3.3457:4240]()
    /// An immutable transaction.
    pub struct Txn<E: Borrow<Env>> {
    env: E,
    root: usize,
    }
    #[cfg(feature = "mmap")]
    #[test]
    #[should_panic]
    fn nroots_test() {
    let path = tempfile::tempdir().unwrap();
    let path = path.path().join("db");
    let l0 = 1 << 15; // 8 pages
    Env::new(&path, l0, 19).unwrap();
    }
    #[cfg(feature = "mmap")]
    #[test]
    fn mmap_growth_test() {
    let path = tempfile::tempdir().unwrap();
    let path = path.path().join("db");
    let l0 = 1 << 15; // 8 pages
    {
    let env = Env::new(&path, l0, 2).unwrap();
    let map1 = env.open_mmap(0, l0).unwrap();
    println!("{:?}", map1);
    let map2 = env.open_mmap(1, l0).unwrap();
    println!("{:?}", map2);
    map1.flush().unwrap();
    map2.flush().unwrap();
    }
    let len = std::fs::metadata(&path).unwrap().len();
    assert_eq!(len, (l0 << 2) - l0);
    }
    [4.94324]
    [3.4240]
    /// A lock on a root page for this process only, because taking
    /// multiple locks on the same file from a single process isn't
    /// cross-platform (or even properly defined).
    ///
    /// Usage is as follows:
    ///
    /// - For read-only transactions, we first take a read lock on the `rw`
    /// field, and increment `n_txn`, locking the file if the former value
    /// is 0.
    ///
    /// - For read-write transactions, we first take a write lock on the
    /// `rw` field, and then take an exclusive lock on the file (this is
    /// valid since only one read-write transaction can be active in a
    /// process at the same time).
    ///
    pub(crate) struct RootLock {
    /// It is undefined behavior to have a file mmapped for than once.
    #[cfg(feature = "mmap")]
    lock_file: Option<std::fs::File>,
  • replacement in sanakirja/src/environment/mod.rs at line 95
    [3.4241][3.4241:4301]()
    #[cfg(not(feature = "crc32"))]
    fn set_crc(_ptr: *mut u8) {}
    [3.4241]
    [3.4301]
    /// Read-write lock.
    rw: parking_lot::RawRwLock,
  • replacement in sanakirja/src/environment/mod.rs at line 98
    [3.4302][3.4302:4618]()
    #[cfg(feature = "crc32")]
    fn set_crc(ptr: *mut u8) {
    unsafe {
    let root_page = std::slice::from_raw_parts(ptr.add(8), PAGE_SIZE - 8);
    let mut h = HASHER.clone();
    h.update(root_page);
    let globptr = ptr as *mut GlobalHeader;
    (&mut *globptr).crc = h.finalize().to_le();
    }
    [3.4302]
    [3.4618]
    /// Count of read-only transactions.
    n_txn: AtomicUsize,
  • replacement in sanakirja/src/environment/mod.rs at line 108
    [4.94716][4.94716:94800]()
    /// Same as [`new`](#new), but does not take a lock on the file
    /// system.
    [4.94716]
    [4.94800]
    /// Same as [`new`](#new), but does not create any lock on the
    /// file system.
  • replacement in sanakirja/src/environment/mod.rs at line 111
    [4.94808][4.94808:94932]()
    /// This method is provided because waiting for a lock on the file
    /// system may block the whole process, whereas.
    [4.94808]
    [4.94932]
    /// The database is very likely to get corrupted if an environment
    /// is opened from multiple processes, or more than once by the
    /// same process, if at least one of these instances can start a
    /// mutable transaction.
  • replacement in sanakirja/src/environment/mod.rs at line 116
    [4.94940][4.94940:95207]()
    /// However, the database is very likely to get corrupted if open
    /// more than once at the same time, even within the same process.
    ///
    /// Therefore, do not use this method without another locking
    /// mechanism in place to avoid that situation.
    [4.94940]
    [4.95207]
    /// The `n_roots` argument is ignored if the database already
    /// exists, and is used to initialise the first `n_roots` pages of
    /// the file else.
  • replacement in sanakirja/src/environment/mod.rs at line 125
    [4.5849][3.4667:4791]()
    let db_exists = std::fs::metadata(&path).is_ok();
    let length = if let Ok(meta) = std::fs::metadata(&path) {
    [4.5849]
    [4.95563]
    let meta = std::fs::metadata(&path);
    let length = if let Ok(ref meta) = meta {
  • edit in sanakirja/src/environment/mod.rs at line 131
    [4.95677]
    [4.13372]
    // Find the next multiple of PAGE_SIZE greater than or equal
    // to `length`.
  • edit in sanakirja/src/environment/mod.rs at line 134
    [4.13444]
    [4.95726]
  • replacement in sanakirja/src/environment/mod.rs at line 143
    [4.95982][3.4820:4897]()
    Self::new_nolock_mmap(Some(file), length, mmap, !db_exists, n_roots)
    [4.95982]
    [4.96139]
    Self::new_nolock_(Some(file), length, mmap, meta.is_err(), n_roots)
  • edit in sanakirja/src/environment/mod.rs at line 146
    [4.96146]
    [4.96146]
    /// Create an environment from a file, without creating any lock
    /// on the filesystem.
  • replacement in sanakirja/src/environment/mod.rs at line 149
    [4.96175][4.96175:96206]()
    unsafe fn new_nolock_mmap(
    [4.96175]
    [4.96206]
    unsafe fn new_nolock_(
  • replacement in sanakirja/src/environment/mod.rs at line 156
    [4.96356][3.4898:4952]()
    assert!(n_roots < ((length >> 12) as usize));
    [4.96356]
    [4.96356]
    assert!(n_roots >= 1);
    assert!(n_roots <= ((length >> 12) as usize));
    assert!(n_roots < 256);
  • replacement in sanakirja/src/environment/mod.rs at line 160
    [4.96393][3.4953:4977](),[3.4977][4.96453:96487](),[4.96453][4.96453:96487](),[4.96487][3.4978:5059](),[3.5059][4.96570:96645](),[4.2384][4.96570:96645](),[4.96570][4.96570:96645](),[4.96645][4.2385:2429](),[4.2429][4.96674:96702](),[4.96674][4.96674:96702](),[4.96702][4.2640:2698](),[4.2458][4.96751:96813](),[4.2698][4.96751:96813](),[4.96751][4.96751:96813](),[4.96813][3.5060:5105]()
    if initialise {
    for i in 0..n_roots {
    *(map.add(i * PAGE_SIZE) as *mut GlobalHeader) = (GlobalHeader {
    version: CURRENT_VERSION,
    root: 0,
    n_roots: n_roots as u8,
    crc: 0,
    length: (n_roots * PAGE_SIZE) as u64,
    free_db: 0,
    rc_db: 0,
    })
    .to_le();
    [4.96393]
    [3.5105]
    let n_roots = if initialise {
    // Initialise the first `n_roots` pages at the start of
    // the file.
    init(map, n_roots);
    // Since the first `n_roots` pages are occupied by roots,
    // the first unused page is found at offset `n_roots *
    // PAGE_SIZE`.
    n_roots
    } else {
    // Read the root and number of roots from the first page's
    // header.
    GlobalHeader::from_le(&*(map as *const GlobalHeader)).n_roots as usize
    };
  • replacement in sanakirja/src/environment/mod.rs at line 174
    [3.5106][3.5106:5155](),[3.5155][4.96832:96846](),[4.96832][4.96832:96846](),[4.96846][3.5156:5240](),[3.5240][4.2593:2909](),[4.97004][4.2593:2909](),[4.2909][4.97157:97167](),[4.97157][4.97157:97167]()
    set_crc(map.add(i * PAGE_SIZE));
    }
    }
    let glob = GlobalHeader::from_le(&*(map as *const GlobalHeader));
    let mut roots = Vec::with_capacity(glob.n_roots as usize);
    for _ in 0..glob.n_roots {
    roots.push(RootLock {
    rw: <parking_lot::RawRwLock as parking_lot::lock_api::RawRwLock>::INIT,
    n_txn: AtomicUsize::new(0),
    lock_file: None,
    })
    }
    [3.5106]
    [4.97167]
    // Finally, create the environment.
  • replacement in sanakirja/src/environment/mod.rs at line 182
    [4.97396][4.97396:97442](),[4.97442][4.2910:2979]()
    first_unused_page: Mutex::new(2),
    roots,
    root: Mutex::new(glob.root as usize),
    [4.97367]
    [4.97514]
    mut_txn_lock: RawMutex::INIT,
    // Initialise a different `RootLock` for each root page.
    roots: (0..n_roots)
    .map(|_| RootLock {
    rw: RawRwLock::INIT,
    n_txn: AtomicUsize::new(0),
    lock_file: None,
    })
    .collect(),
  • edit in sanakirja/src/environment/mod.rs at line 196
    [4.97548]
    [4.97548]
    /// No-mmap version of the same thing.
  • replacement in sanakirja/src/environment/mod.rs at line 198
    [4.97582][4.2980:3065]()
    unsafe fn new_nolock_mmap(length: u64, initialise: bool) -> Result<Env, Error> {
    [4.97582]
    [4.97678]
    unsafe fn new_nolock_(length: u64, initialise: bool, n_roots: usize) -> Result<Env, Error> {
    assert!(n_roots >= 1);
    assert!(n_roots <= ((length >> 12) as usize));
    assert!(n_roots < 256);
  • replacement in sanakirja/src/environment/mod.rs at line 205
    [4.97840][4.97840:98453](),[4.98453][4.3066:3247](),[4.3247][4.98544:98554](),[4.98544][4.98544:98554]()
    let n_roots = 2;
    for i in 0..n_roots {
    *(map.offset((i * PAGE_SIZE) as isize) as *mut GlobalHeader) = (GlobalHeader {
    version: CURRENT_VERSION,
    root: 0,
    n_roots: n_roots as u8,
    crc: 0,
    length: n_roots as u64 * PAGE_SIZE as u64,
    free_db: 0,
    rc_db: 0,
    })
    .to_le();
    }
    let glob = GlobalHeader::from_le(&*(map as *const GlobalHeader));
    let mut roots = Vec::with_capacity(glob.n_roots as usize);
    for _ in 0..glob.n_roots {
    roots.push(RootLock {
    rw: <parking_lot::RawRwLock as parking_lot::lock_api::RawRwLock>::INIT,
    n_txn: AtomicUsize::new(0),
    })
    }
    [4.97840]
    [4.98554]
    init(map, n_roots);
  • replacement in sanakirja/src/environment/mod.rs at line 212
    [4.98759][4.98759:98874]()
    first_unused_page: Mutex::new(2),
    roots,
    root: Mutex::new(glob.root as usize),
    [4.98710]
    [4.98874]
    mut_txn_lock: RawMutex::INIT,
    // Initialise a different `RootLock` for each root page.
    roots: (0..n_roots)
    .map(|_| RootLock {
    rw: RawRwLock::INIT,
    n_txn: AtomicUsize::new(0),
    })
    .collect(),
  • edit in sanakirja/src/environment/mod.rs at line 222
    [4.98901]
    [4.98901]
    }
    }
    unsafe fn init(map: *mut u8, n_roots: usize) {
    for i in 0..n_roots {
    *(map.offset((i * PAGE_SIZE) as isize) as *mut GlobalHeader) = (GlobalHeader {
    version: CURRENT_VERSION,
    root: 0,
    n_roots: n_roots as u8,
    crc: 0,
    length: n_roots as u64 * PAGE_SIZE as u64,
    free_db: 0,
    rc_db: 0,
    })
    .to_le();
    set_crc(map.add(i * PAGE_SIZE));
  • edit in sanakirja/src/environment/mod.rs at line 239
    [4.98907]
    [4.98909]
    }
  • edit in sanakirja/src/environment/mod.rs at line 241
    [4.98910]
    [4.99665]
    impl Env {
  • replacement in sanakirja/src/environment/mod.rs at line 274
    [4.102174][4.4298:4374]()
    unsafe { Self::new_nolock_mmap(None, length, mmap, true, n_roots) }
    [4.102174]
    [4.102241]
    unsafe { Self::new_nolock_(None, length, mmap, true, n_roots) }
  • replacement in sanakirja/src/environment/mod.rs at line 280
    [4.102404][4.4375:4432]()
    pub fn new_anon(length: u64) -> Result<Env, Error> {
    [4.102404]
    [4.13561]
    pub fn new_anon(length: u64, n_roots: usize) -> Result<Env, Error> {
  • replacement in sanakirja/src/environment/mod.rs at line 283
    [4.6116][4.102542:102597](),[4.13676][4.102542:102597](),[4.102542][4.102542:102597]()
    unsafe { Self::new_nolock_mmap(length, true) }
    [4.13676]
    [4.102597]
    unsafe { Self::new_nolock_(length, true, n_roots) }
  • edit in sanakirja/src/environment/mod.rs at line 285
    [4.102603]
    [4.102605]
    /// Now, here is how databases grow: when we run out of space, we
    /// allocate a new chunk of memory/disk space, of a size twice as
    /// large as the last chunk. The size of the first chunk is the
    /// size of the file when we first opened the environment.
  • edit in sanakirja/src/environment/mod.rs at line 291
    [4.102606]
    [4.102623]
    /// Allocate the memory of the appropriate size and offest for the
    /// `i`th chunk.
  • edit in sanakirja/src/environment/mod.rs at line 305
    [4.103005]
    [4.103005]
    /// The same, but for memory-mapped file. If we're doing that, it
    /// means we need to grow the file.
  • edit in sanakirja/src/environment/mod.rs at line 334
    [4.103989]
    [4.103989]
    /// Find an offset in a file, possibly mapping more of the file if
    /// necessary (for example if the file has been grown by another
    /// process since we openend this environment).
  • replacement in sanakirja/src/environment/mod.rs at line 346
    [4.104335][4.104335:104396]()
    return mmaps[i].ptr.offset(offset as isize);
    [4.104335]
    [4.104396]
    return mmaps[i].ptr.add(offset as usize);
  • edit in sanakirja/src/environment/mod.rs at line 350
    [4.104468][4.104468:105034](),[4.105034][4.4433:4480](),[4.4480][4.105090:105116](),[4.105090][4.105090:105116]()
    }
    }
    /// Close this repository, releasing the locks. It is undefined
    /// behaviour to use the environment afterwards. This method can
    /// be used for instance to release the locks before allocating a
    /// new environment (note that `std::mem::replace` followed by
    /// `Drop::drop` of the previous value would not release the locks
    /// in the correct order).
    ///
    /// The safe alternative to this method is to use an `Option<Env>`
    /// instead of an `Env`.
    #[cfg(feature = "mmap")]
    pub unsafe fn close(&mut self) {
    for m in self.mmaps.lock().drain(..) {
    drop(m.mmap);
  • edit in sanakirja/src/environment/mod.rs at line 351
    [4.105126][4.4481:4623](),[4.4623][4.105211:105221](),[4.105211][4.105211:105221]()
    for l in self.roots.drain(..) {
    if let Some(ref f) = l.lock_file {
    f.unlock().unwrap_or(())
    }
    }
  • edit in sanakirja/src/environment/mod.rs at line 362
    [4.105565]
    [4.105565]
    }
    }
    /// If the CRC feature is disabled, we're not checking CRCs.
    #[cfg(not(feature = "crc32"))]
    fn check_crc(&self, _root: usize) -> Result<(), crate::CRCError> {
    Ok(())
    }
    /// Else, we are checking CRCs, so return a CRC error if the check
    /// fails (the CRC is a 32 bit integer encoded little-endian at
    /// bytes [8,12[ of the root pages).
    #[cfg(feature = "crc32")]
    fn check_crc(&self, root: usize) -> Result<(), crate::CRCError> {
    unsafe {
    let maps = self.mmaps.lock();
    let page_ptr = maps[0].ptr.add(root * PAGE_SIZE);
    let crc = (&*(page_ptr as *const GlobalHeader)).crc;
    let root_page = std::slice::from_raw_parts(page_ptr.add(8), PAGE_SIZE - 8);
    let mut h = HASHER.clone();
    h.update(root_page);
    if h.finalize() != crc {
    return Err(crate::CRCError {});
    }
  • edit in sanakirja/src/environment/mod.rs at line 387
    [4.105575]
    [4.105575]
    Ok(())
  • edit in sanakirja/src/environment/mod.rs at line 397
    [3.5838]
    [4.105581]
    }
    #[cfg(feature = "mmap")]
    #[test]
    #[should_panic]
    fn nroots_test() {
    let path = tempfile::tempdir().unwrap();
    let path = path.path().join("db");
    let l0 = 1 << 15; // 8 pages
    Env::new(&path, l0, 19).unwrap();
    }
    #[cfg(feature = "mmap")]
    #[test]
    fn mmap_growth_test() {
    let path = tempfile::tempdir().unwrap();
    let path = path.path().join("db");
    let l0 = 1 << 15; // 8 pages
    {
    let env = Env::new(&path, l0, 2).unwrap();
    let map1 = env.open_mmap(0, l0).unwrap();
    println!("{:?}", map1);
    let map2 = env.open_mmap(1, l0).unwrap();
    println!("{:?}", map2);
    map1.flush().unwrap();
    map2.flush().unwrap();
    }
    let len = std::fs::metadata(&path).unwrap().len();
    assert_eq!(len, (l0 << 2) - l0);
    }
    #[cfg(not(feature = "crc32"))]
    fn set_crc(_ptr: *mut u8) {}
    #[cfg(feature = "crc32")]
    fn set_crc(ptr: *mut u8) {
    unsafe {
    let root_page = std::slice::from_raw_parts(ptr.add(8), PAGE_SIZE - 8);
    let mut h = HASHER.clone();
    h.update(root_page);
    let globptr = ptr as *mut GlobalHeader;
    (&mut *globptr).crc = h.finalize().to_le();
    }
    }
    /// An immutable transaction.
    pub struct Txn<E: Borrow<Env>> {
    env: E,
    pub(crate) root: usize,
  • edit in sanakirja/src/environment/mod.rs at line 451
    [4.4709][4.3421:3441]()
    use log::*;
  • replacement in sanakirja/src/environment/mod.rs at line 453
    [4.105774][4.3442:3475](),[4.3475][4.105774:105889](),[4.105774][4.105774:105889](),[4.105889][4.3476:3517]()
    debug!("root lock");
    let root = env_.root.lock();
    let root = (*root + env_.roots.len() - 1) % env_.roots.len();
    debug!("shared {:?}", root);
    [4.105737]
    [4.3517]
    // Find the youngest committed version and lock it. Note
    // that there may be processes incrementing the version
    // number in parallel to this process. If that happens,
    // then since we take a shared file lock on a root page
    // (at the end of this function), the only thing that may
    // happen is that we don't open the very last version.
    let cur_mut_root =
    unsafe { (&*(env_.mmaps.lock()[0].ptr as *const GlobalHeader)).root as usize };
    // Last committed root page.
    let root = (cur_mut_root + env_.roots.len() - 1) % env_.roots.len();
  • edit in sanakirja/src/environment/mod.rs at line 464
    [4.3564]
    [4.4711]
    // Increase the read-only transaction count for that root,
    // and if the previous value is 0, take a file lock.
  • edit in sanakirja/src/environment/mod.rs at line 467
    [4.4794][4.3565:3615]()
    debug!("old_n_txn: {:?}", old_n_txn);
  • replacement in sanakirja/src/environment/mod.rs at line 468
    [4.4877][4.4877:4943](),[4.4943][4.3616:3664](),[4.3664][4.4943:4999](),[4.4943][4.4943:4999]()
    if let Some(ref f) = env_.roots[root].lock_file {
    debug!("file lock shared");
    f.lock_shared()?;
    }
    [4.4826]
    [4.4999]
    env_.lock_shared(root)?
  • replacement in sanakirja/src/environment/mod.rs at line 476
    [3.5912][3.5912:6018]()
    #[cfg(not(feature = "crc32"))]
    fn check_crc(&self, _root: usize) -> Result<(), crate::CRCError> {
    [3.5912]
    [3.6018]
    #[cfg(feature = "mmap")]
    fn lock_shared(&self, root: usize) -> Result<(), Error> {
    if let Some(ref f) = self.roots[root].lock_file {
    f.lock_shared()?;
    }
  • replacement in sanakirja/src/environment/mod.rs at line 484
    [3.6040][3.6040:6586]()
    #[cfg(feature = "crc32")]
    fn check_crc(&self, root: usize) -> Result<(), crate::CRCError> {
    unsafe {
    let maps = self.mmaps.lock();
    let page_ptr = maps[0].ptr.add(root * PAGE_SIZE);
    let crc = (&*(page_ptr as *const GlobalHeader)).crc;
    let root_page = std::slice::from_raw_parts(page_ptr.add(8), PAGE_SIZE - 8);
    let mut h = HASHER.clone();
    h.update(root_page);
    if h.finalize() != crc {
    return Err(crate::CRCError {});
    }
    [3.6040]
    [3.6586]
    #[cfg(not(feature = "mmap"))]
    fn lock_shared(&self, _root: usize) -> Result<(), Error> {
    Ok(())
    }
    #[cfg(feature = "mmap")]
    fn lock_exclusive(&self, root: usize) -> Result<(), Error> {
    if let Some(ref f) = self.roots[root].lock_file {
    f.lock_exclusive()?;
    }
    Ok(())
    }
    #[cfg(not(feature = "mmap"))]
    fn lock_exclusive(&self, _root: usize) -> Result<(), Error> {
    Ok(())
    }
    #[cfg(feature = "mmap")]
    fn unlock(&self, root: usize) -> Result<(), Error> {
    if let Some(ref f) = self.roots[root].lock_file {
    f.unlock()?
  • edit in sanakirja/src/environment/mod.rs at line 507
    [3.6596]
    [3.6596]
    Ok(())
    }
    #[cfg(not(feature = "mmap"))]
    fn unlock(&self, _root: usize) -> Result<(), Error> {
  • replacement in sanakirja/src/environment/mod.rs at line 522
    [4.5259][4.5259:5380]()
    if let Some(ref f) = env.roots[self.root].lock_file {
    f.unlock().unwrap_or(())
    }
    [4.5196]
    [4.5380]
    env.unlock(self.root).unwrap_or(())
  • replacement in sanakirja/src/environment/mod.rs at line 528
    [4.5450][4.106320:106351](),[4.106320][4.106320:106351]()
    type Error = crate::Error;
    [4.5450]
    [4.106351]
    type Error = Error;
  • edit in sanakirja/src/environment/mod.rs at line 543
    [4.106701]
    [4.16926]
    /// The trait, implemented by [`Txn`] and [`MutTxn`], for treating the
    /// 4064 bytes after the header of root pages as pointers to B trees
    /// (well, actually `Option` of pointers to databases, where `None` is
    /// encoded by 0).
  • edit in sanakirja/src/environment/mod.rs at line 548
    [4.16945]
    [4.13745]
    /// Return the database stored in the root page of the current
    /// transaction at index `n`, if any.
  • edit in sanakirja/src/environment/mod.rs at line 557
    [4.17211]
    [4.13840]
    /// This is a straightforward implementation of just accessing index `n`.
  • replacement in sanakirja/src/environment/mod.rs at line 572
    [4.17729][4.17729:17969]()
    Some(sanakirja_core::btree::Db_ {
    db,
    k: std::marker::PhantomData,
    v: std::marker::PhantomData,
    p: std::marker::PhantomData,
    })
    [4.17729]
    [4.17969]
    Some(sanakirja_core::btree::Db_::from_page(db))
  • edit in sanakirja/src/debug.rs at line 1
    [4.11][4.12:39]()
    use sanakirja_core::btree;
  • replacement in sanakirja/src/debug.rs at line 36
    [4.14112][4.924:955](),[4.924][4.924:955]()
    P: btree::BTreePage<K, V>,
    [4.14112]
    [4.955]
    P: BTreePage<K, V>,
  • replacement in sanakirja/src/debug.rs at line 78
    [4.14201][4.1996:2027](),[4.1996][4.1996:2027]()
    P: btree::BTreePage<K, V>,
    [4.14201]
    [4.2027]
    P: BTreePage<K, V>,
  • replacement in sanakirja/src/debug.rs at line 81
    [4.2043][4.2043:2077]()
    buf: &mut dyn std::io::Write,
    [4.2043]
    [4.2077]
    buf: &mut dyn Write,
  • edit in sanakirja/Cargo.toml at line 16
    [4.84]
    [4.5528]
    sanakirja-core = { path = "../sanakirja-core", version = "1.0" }
  • edit in sanakirja/Cargo.toml at line 19
    [4.5617][4.85:150]()
    sanakirja-core = { path = "../sanakirja-core", version = "1.0" }