diff --git a/src/table.rs b/src/table.rs index 97c76494..5e838505 100644 --- a/src/table.rs +++ b/src/table.rs @@ -275,6 +275,14 @@ impl<'txn, K: Key + 'static, V: Value + 'static> ReadableTable for Table<' .range(&range) .map(|x| Range::new(x, self.transaction.transaction_guard())) } + + fn first(&self) -> Result, AccessGuard)>> { + self.tree.first() + } + + fn last(&self) -> Result, AccessGuard)>> { + self.tree.last() + } } impl Sealed for Table<'_, K, V> {} @@ -392,14 +400,10 @@ pub trait ReadableTable: ReadableTableMeta KR: Borrow> + 'a; /// Returns the first key-value pair in the table, if it exists - fn first(&self) -> Result, AccessGuard)>> { - self.iter()?.next().transpose() - } + fn first(&self) -> Result, AccessGuard)>>; /// Returns the last key-value pair in the table, if it exists - fn last(&self) -> Result, AccessGuard)>> { - self.iter()?.next_back().transpose() - } + fn last(&self) -> Result, AccessGuard)>>; /// Returns a double-ended iterator over all elements in the table fn iter(&self) -> Result> { @@ -520,6 +524,14 @@ impl ReadableTable for ReadOnlyTable .range(&range) .map(|x| Range::new(x, self.transaction_guard.clone())) } + + fn first(&self) -> Result, AccessGuard)>> { + self.tree.first() + } + + fn last(&self) -> Result, AccessGuard)>> { + self.tree.last() + } } impl Sealed for ReadOnlyTable {} diff --git a/src/tree_store/btree.rs b/src/tree_store/btree.rs index 03d62553..7182afae 100644 --- a/src/tree_store/btree.rs +++ b/src/tree_store/btree.rs @@ -474,6 +474,18 @@ impl BtreeMut<'_, K, V> { self.read_tree()?.get(key) } + pub(crate) fn first( + &self, + ) -> Result, AccessGuard<'static, V>)>> { + self.read_tree()?.first() + } + + pub(crate) fn last( + &self, + ) -> Result, AccessGuard<'static, V>)>> { + self.read_tree()?.last() + } + pub(crate) fn range<'a0, T: RangeBounds + 'a0, KR: Borrow> + 'a0>( &self, range: &'_ T, @@ -726,6 +738,71 @@ impl Btree { } } + pub(crate) fn first( + &self, + ) -> Result, AccessGuard<'static, V>)>> { + if let Some(ref root) = self.cached_root { + self.first_helper(root.clone()) + } else { + Ok(None) + } + } + + fn first_helper( + &self, + page: PageImpl, + ) -> Result, AccessGuard<'static, V>)>> { + let node_mem = page.memory(); + match node_mem[0] { + LEAF => { + let accessor = LeafAccessor::new(page.memory(), K::fixed_width(), V::fixed_width()); + let (key_range, value_range) = accessor.entry_ranges(0).unwrap(); + let key_guard = AccessGuard::with_page(page.clone(), key_range); + let value_guard = AccessGuard::with_page(page, value_range); + Ok(Some((key_guard, value_guard))) + } + BRANCH => { + let accessor = BranchAccessor::new(&page, K::fixed_width()); + let child_page = accessor.child_page(0).unwrap(); + self.first_helper(self.mem.get_page_extended(child_page, self.hint)?) + } + _ => unreachable!(), + } + } + + pub(crate) fn last( + &self, + ) -> Result, AccessGuard<'static, V>)>> { + if let Some(ref root) = self.cached_root { + self.last_helper(root.clone()) + } else { + Ok(None) + } + } + + fn last_helper( + &self, + page: PageImpl, + ) -> Result, AccessGuard<'static, V>)>> { + let node_mem = page.memory(); + match node_mem[0] { + LEAF => { + let accessor = LeafAccessor::new(page.memory(), K::fixed_width(), V::fixed_width()); + let (key_range, value_range) = + accessor.entry_ranges(accessor.num_pairs() - 1).unwrap(); + let key_guard = AccessGuard::with_page(page.clone(), key_range); + let value_guard = AccessGuard::with_page(page, value_range); + Ok(Some((key_guard, value_guard))) + } + BRANCH => { + let accessor = BranchAccessor::new(&page, K::fixed_width()); + let child_page = accessor.child_page(accessor.count_children() - 1).unwrap(); + self.last_helper(self.mem.get_page_extended(child_page, self.hint)?) + } + _ => unreachable!(), + } + } + pub(crate) fn range<'a0, T: RangeBounds, KR: Borrow>>( &self, range: &'_ T, diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index e96d5c7b..be811f05 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1797,6 +1797,14 @@ impl> ReadableTable { self.inner.range(range) } + + fn first(&self) -> redb::Result, AccessGuard)>> { + self.inner.first() + } + + fn last(&self) -> redb::Result, AccessGuard)>> { + self.inner.last() + } } impl> ReadableTableMetadata