FZBLNBGNQPNTLBNPNZ2C6DJ5323MZQ2PH54F6ZEKPFCK7TGJFGWAC
52X5P7NDBQHIJDIYNY3XUPDHHOO3PDPPNKGO2PGLXKVNM3EVECTQC
E4MD6T3LNOYWVFTFFWCUKRNS4M2XVSKRLDWPYHMZHGDNO2T5JREQC
OP6SVMOD2GTQ7VNJ4E5KYFG4MIYA7HBMXJTADALMZH4PY7OQRMZQC
RV2L6CZWTMUQ2A52YDAFVHDFGURZL3H4SSCDC347UGN23D3J5KZQC
H3FVSQIQGFCFKCPXVOSFHP4OSUOBBURJESCZNGQTNDAAD3WQSBEQC
DV4A2LR7Q5LAEGAQHLO34PZCHGJUHPAMRZFGT7GUFNKVQKPJNOYQC
EHJFNMB2R4MYG6ZSHHEENRFCSPFVWKVHLD5DXAE5HXUGXP5ZVHKQC
QEUTVAZ4F4EJXRDMWDMYXF6XEDMX7YPVG4IIXEKPIR3K54E5W5OAC
QYDGYIZRNFRIQD7RUCY5YAN3F2THZA74E5UOHPIFWSULEJFAFVJQC
OFINGD26ZWCRDVVDI2ZIBLMHXKEMJA6MRNLANJYUHQPIJLPA7J2AC
73Z2UB3JGRLFNFORE7D64O4IHIFSZASD4G4FLJ4FJLHANT75MGIAC
W2MIZD5BNL7A5HVFWTESF57QU7T6QMEF4RBSLFQXMEEU3XD2NU2QC
UAQX27N4PI4LHEW6LSHJETIE5MV7JTEMPLTJFYUBMYVPC43H7VOAC
UUUVNC4DWEEL7WV5IRPKPZ6HZMYCPA53XM7LJWICUD4E6GN37IRQC
6DMPXOAT5GQ3BQQOMUZN2GMBQPRA4IB7CCPHTQTIFGO3KWWAKF3QC
Q7DRIBBRE4MNG4NP3PVIXAJF5PQYLFWYIVK2O4VVLEO6XY3BOSFQC
MSRWB47YP6L5BVTS53QQPBOHY5SXTSTR5KD6IIF35UWCTEUOCQWQC
LSQ6V7M66TEGLJ7QBLRVDX4E7UKJTDQTEXZOS3KGPGFKVXNLPKBQC
X3QVVQIS7B7L3XYZAWL3OOBUXOJ6RMOKQ45YMLLGAHYPEEKZ45ZAC
G4JEQLLX6Q7VVFVAEJZAVQXX33MQ36CSCYSMJ5NQM5VZ76DXKU6QC
S4V4QZ5CF5LUDYWNR2UMWH6CHJDJ5FPGAZCQYM5GY7FJMJV4NN4QC
OTWDDJE7TTE73D6BGF4ZN6BH2NFUFLPME2VJ3CPALH463UGWLEIQC
XEU2QVLCHPYOOD4TQIPEEVYOVSFMKFPLJYWEJYXYJAZ7S54KWDZAC
YWFYZNLZ5JHLIFVBRKZK4TSWVPROUPRG77ZB5M7UHT2OKPL4ZSRQC
AOX2XQISHGWNNAFBYRN44Q6AWG7H5DPBK5YMFHK42HQNZ2TMHEJQC
6DCQHIFPEH4GZKSRRS32GMKDRPZH4MTCGOUEI7YEUVKWENBF3JWAC
KM3JAFGPFV7MP7M2LJIYRVAUTU646B3IRXADTRZKOU2RF7LUB62QC
LROAI3NBBSCU4T2YA6EHJYKKKL75AU5A7C7WIRCGIQ56S6HPLRXQC
NXMFNPZ7VWJRLC3M5QJJVTICXCMGE24F3HVIZA7A7RLVMLQMLDVQC
YXKP4AIWDBIWBBUDWF66YIPG5ECMHNKEV3PX6KYXOVXY3EWG3WGQC
T7QB6QEPWBXAU3RL7LE4GRDWWNQ65ZU2YNNTWBYLORJOABAQFEZQC
}
#[test]
pub fn empty_last_cursor() {
env_logger::try_init().unwrap_or(());
let env = Env::new_anon(409600000, 1).unwrap();
let mut txn = Env::mut_txn_begin(&env).unwrap();
let db: Db<u64, ()> = create_db(&mut txn).unwrap();
let mut curs = btree::cursor::Cursor::new(&txn, &db).unwrap();
assert!(curs.set_last(&txn).unwrap().is_none());
}
fn check_free(txn: &mut MutTxn<&Env, ()>, refs: &BTreeMap<u64, usize>) {
if txn.free > 0 {
let db_free = Db {
db: txn.free,
k: std::marker::PhantomData,
v: std::marker::PhantomData,
p: std::marker::PhantomData,
};
let mut curs: Cursor<_, _, B> = btree::cursor::Cursor::new(txn, &db_free).unwrap();
debug!("{:?}", db_free);
while let Some((k, _)) = curs.next(txn).unwrap() {
debug!("free: {:?}", k);
assert!(refs.get(k).is_none())
}
}
for (r, _) in refs.iter() {
assert!(*r < txn.length)
}
fn add_refs<T: LoadPage, K: Storable, V: Storable, P: BTreePage<K, V>>(
txn: &T,
db: &Db_<K, V, P>,
pages: &mut BTreeMap<u64, usize>,
) -> Result<(), T::Error> {
debug!("------ add_refs {:?}", db.db);
let mut stack = vec![db.db];
while let Some(p) = stack.pop() {
debug!("-- p = {:?}", p);
match pages.entry(p) {
Entry::Vacant(e) => {
e.insert(1);
let p = txn.load_page(p)?;
let mut c = P::cursor_first(&p);
let l = P::left_child(p.as_page(), &c);
debug!("l = {:?}", l);
if l > 0 {
stack.push(l);
while let Some((_, _, r)) = P::next(txn, p.as_page(), &mut c) {
debug!("r = {:?}", r);
stack.push(r);
}
}
}
Entry::Occupied(mut e) => {
debug!("already there");
e.insert(e.get() + 1);
}
}
}
Ok(())
}
/// std::fs::File size of the database path, if it exists.
pub fn file_size<P: AsRef<Path>>(path: P) -> Result<u64, Error> {
Ok(std::fs::metadata(&path)?.len())
}
/// Initialize an environment. `length` must be a strictly
/// positive multiple of 4096.
/// Initialize an environment. If `length` is not a multiple of
/// `4096`, it is rounded to the next multiple of the page size
/// (4096 bytes).
///
/// The `n_roots` parameter is the maximum number of versions that
/// can be alive at the same time, before `mut_txn_begin` must
/// wait for old readers to stop.
/// The `n_roots` parameter is the maximum number of mutable
/// transactions that can commit during a single immutable
/// transaction, and must be at most 255. If it is 1, mutable
/// transactions exclude all immutable transactions.
/// If `n_roots` is 1, mutable transactions exclude all readers.
}
}
type B = btree::page::Page<u64, ()>;
pub fn check_free_mut(
txn: &crate::MutTxn<&crate::Env, ()>,
refs: &std::collections::BTreeMap<u64, usize>,
) {
let db_free = if txn.free > 0 {
let db_free = Db::from_page(txn.free);
let mut curs: Cursor<_, _, B> = btree::cursor::Cursor::new(txn, &db_free).unwrap();
while let Some((k, _)) = curs.next(txn).unwrap() {
assert!(refs.get(k).is_none())
}
Some(db_free)
} else {
None
};
debug!("{:?}", db_free);
for (r, _) in refs.iter() {
assert!(*r < txn.length)
}
let env = txn.env;
let len = txn.length;
for i in env.roots.len() as u64..(len >> 12) {
let page = i << 12;
if refs.contains_key(&page) {
continue;
} else if let Some(ref f) = db_free {
if let Some((x, _)) = get(txn, f, &page, None).unwrap() {
if *x == page {
continue;
}
}
}
panic!("page not found: 0x{:x} (total length 0x{:x})", page, len);
}
}
pub fn check_free<B: std::borrow::Borrow<crate::Env>>(
txn: &crate::Txn<B>,
refs: &std::collections::BTreeMap<u64, usize>,
) {
let env = txn.env.borrow();
let (db_free, length): (Option<Db<u64, ()>>, _) = unsafe {
let hdr = &*(env.mmaps.lock()[0].ptr.add(txn.root * PAGE_SIZE)
as *const crate::environment::GlobalHeader);
(
if hdr.free_db != 0 {
Some(Db::from_page(u64::from_le(hdr.free_db)))
} else {
None
},
u64::from_le(hdr.length),
)
};
debug!("db_free: {:?}", db_free);
for (r, _) in refs.iter() {
debug!("r = 0x{:x}, length = 0x{:x}", r, length);
assert!(*r < length)
}
for i in env.roots.len() as u64..(length >> 12) {
let page = i << 12;
if refs.contains_key(&page) {
continue;
} else if let Some(ref f) = db_free {
if let Some((x, _)) = get(txn, f, &page, None).unwrap() {
if *x == page {
continue;
}
}
}
panic!("page not found: 0x{:x} (total length 0x{:x})", page, length);
}
}
pub fn add_refs<T: LoadPage, K: Storable + ?Sized, V: Storable + ?Sized, P: BTreePage<K, V>>(
txn: &T,
db: &Db_<K, V, P>,
pages: &mut std::collections::BTreeMap<u64, usize>,
) -> Result<(), T::Error> {
use std::collections::btree_map::Entry;
let mut stack = vec![db.db];
while let Some(p) = stack.pop() {
match pages.entry(p) {
Entry::Vacant(e) => {
debug!("add_refs: 0x{:x}", p);
e.insert(1);
let p = txn.load_page(p)?;
let mut c = P::cursor_first(&p);
let l = P::left_child(p.as_page(), &c);
if l > 0 {
stack.push(l);
while let Some((_, _, r)) = P::next(txn, p.as_page(), &mut c) {
stack.push(r);
}
}
}
Entry::Occupied(mut e) => {
e.insert(e.get() + 1);
}
}
pub fn add_free_refs<B: std::borrow::Borrow<crate::Env>>(
txn: &crate::Txn<B>,
pages: &mut std::collections::BTreeMap<u64, usize>,
) -> Result<(), crate::Error> {
let env = txn.env.borrow();
unsafe {
let p = &*(env.mmaps.lock()[0].ptr.add(txn.root * PAGE_SIZE)
as *const crate::environment::GlobalHeader);
if p.free_db != 0 {
debug!("add_free_refs: free = 0x{:x}", p.free_db);
let free_db: Db<u64, ()> = btree::Db::from_page(p.free_db);
add_refs(txn, &free_db, pages)?;
}
if p.rc_db != 0 {
debug!("add_free_refs: rc = 0x{:x}", p.rc_db);
let rc_db: Db<u64, ()> = btree::Db::from_page(p.rc_db);
add_refs(txn, &rc_db, pages)?;
}
};
Ok(())
}
pub fn add_free_refs_mut<B: std::borrow::Borrow<crate::Env>, T>(
txn: &crate::MutTxn<B, T>,
pages: &mut std::collections::BTreeMap<u64, usize>,
) -> Result<(), crate::Error> {
let env = txn.env.borrow();
unsafe {
let p = &*(env.mmaps.lock()[0].ptr.add(txn.root * PAGE_SIZE)
as *const crate::environment::GlobalHeader);
if p.free_db != 0 {
debug!("add_free_refs: free = 0x{:x}", p.free_db);
let free_db: Db<u64, ()> = btree::Db::from_page(p.free_db);
add_refs(txn, &free_db, pages)?;
}
if p.rc_db != 0 {
debug!("add_free_refs: rc = 0x{:x}", p.rc_db);
let rc_db: Db<u64, ()> = btree::Db::from_page(p.rc_db);
add_refs(txn, &rc_db, pages)?;
}
};
Ok(())
}