chore: move helper methods from DatabaseProvider to DBProvider as defaults (#12367)

This commit is contained in:
joshieDo
2024-11-07 21:06:53 +09:00
committed by GitHub
parent eab1a72577
commit cf72b6f38d
8 changed files with 153 additions and 148 deletions

View File

@ -23,8 +23,8 @@ use reth_primitives::{
use reth_provider::{
providers::ProviderNodeTypes, BlockExecutionWriter, BlockNumReader, BlockWriter,
CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications,
ChainSpecProvider, ChainSplit, ChainSplitTarget, DisplayBlocksChain, HeaderProvider,
ProviderError, StaticFileProviderFactory,
ChainSpecProvider, ChainSplit, ChainSplitTarget, DBProvider, DisplayBlocksChain,
HeaderProvider, ProviderError, StaticFileProviderFactory,
};
use reth_stages_api::{MetricEvent, MetricEventsSender};
use reth_storage_errors::provider::{ProviderResult, RootMismatch};

View File

@ -18,7 +18,8 @@ use reth_execution_types::{Chain, ExecutionOutcome};
use reth_primitives::{GotExpected, SealedBlockWithSenders, SealedHeader};
use reth_provider::{
providers::{BundleStateProvider, ConsistentDbView, ProviderNodeTypes},
FullExecutionDataProvider, ProviderError, StateRootProvider, TryIntoHistoricalStateProvider,
DBProvider, FullExecutionDataProvider, ProviderError, StateRootProvider,
TryIntoHistoricalStateProvider,
};
use reth_revm::database::StateProviderDatabase;
use reth_trie::{updates::TrieUpdates, HashedPostState, TrieInput};

View File

@ -6,7 +6,7 @@ use reth_db::{DatabaseEnv, RawKey, RawTable, RawValue, TableViewer, Tables};
use reth_db_api::{cursor::DbCursorRO, table::Table, transaction::DbTx};
use reth_db_common::DbTool;
use reth_node_builder::{NodeTypesWithDB, NodeTypesWithDBAdapter, NodeTypesWithEngine};
use reth_provider::providers::ProviderNodeTypes;
use reth_provider::{providers::ProviderNodeTypes, DBProvider};
use std::{
hash::{BuildHasher, Hasher},
sync::Arc,

View File

@ -12,7 +12,7 @@ use reth_db_api::{
};
use reth_fs_util as fs;
use reth_node_types::NodeTypesWithDB;
use reth_provider::{providers::ProviderNodeTypes, ChainSpecProvider, ProviderFactory};
use reth_provider::{providers::ProviderNodeTypes, ChainSpecProvider, DBProvider, ProviderFactory};
use std::{path::Path, rc::Rc, sync::Arc};
use tracing::info;

View File

@ -621,7 +621,8 @@ mod tests {
use crate::{
providers::{StaticFileProvider, StaticFileWriter},
test_utils::{blocks::TEST_BLOCK, create_test_provider_factory, MockNodeTypesWithDB},
BlockHashReader, BlockNumReader, BlockWriter, HeaderSyncGapProvider, TransactionsProvider,
BlockHashReader, BlockNumReader, BlockWriter, DBProvider, HeaderSyncGapProvider,
TransactionsProvider,
};
use alloy_primitives::{TxNumber, B256, U256};
use assert_matches::assert_matches;

View File

@ -24,7 +24,6 @@ use reth_db::{
cursor::DbDupCursorRW, tables, BlockNumberList, PlainAccountState, PlainStorageState,
};
use reth_db_api::{
common::KeyValue,
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO},
database::Database,
models::{
@ -63,7 +62,7 @@ use std::{
cmp::Ordering,
collections::{hash_map, BTreeMap, BTreeSet, HashMap, HashSet},
fmt::Debug,
ops::{Bound, Deref, DerefMut, Range, RangeBounds, RangeInclusive},
ops::{Deref, DerefMut, Range, RangeBounds, RangeInclusive},
sync::{mpsc, Arc},
time::{Duration, Instant},
};
@ -145,7 +144,7 @@ impl<TX, N: NodeTypes> DatabaseProvider<TX, N> {
}
}
impl<TX: DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
/// State provider for latest block
pub fn latest<'a>(&'a self) -> ProviderResult<Box<dyn StateProvider + 'a>> {
trace!(target: "providers::db", "Returning latest state provider");
@ -364,7 +363,7 @@ where
Ok(Vec::new())
}
impl<TX: DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
/// Creates a provider with an inner read-only transaction.
pub const fn new(
tx: TX,
@ -395,75 +394,6 @@ impl<TX: DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
&self.chain_spec
}
/// Disables long-lived read transaction safety guarantees for leaks prevention and
/// observability improvements.
///
/// CAUTION: In most of the cases, you want the safety guarantees for long read transactions
/// enabled. Use this only if you're sure that no write transaction is open in parallel, meaning
/// that Reth as a node is offline and not progressing.
pub fn disable_long_read_transaction_safety(mut self) -> Self {
self.tx.disable_long_read_transaction_safety();
self
}
/// Return full table as Vec
pub fn table<T: Table>(&self) -> Result<Vec<KeyValue<T>>, DatabaseError>
where
T::Key: Default + Ord,
{
self.tx
.cursor_read::<T>()?
.walk(Some(T::Key::default()))?
.collect::<Result<Vec<_>, DatabaseError>>()
}
/// Return a list of entries from the table, based on the given range.
#[inline]
pub fn get<T: Table>(
&self,
range: impl RangeBounds<T::Key>,
) -> Result<Vec<KeyValue<T>>, DatabaseError> {
self.tx.cursor_read::<T>()?.walk_range(range)?.collect::<Result<Vec<_>, _>>()
}
/// Iterates over read only values in the given table and collects them into a vector.
///
/// Early-returns if the range is empty, without opening a cursor transaction.
fn cursor_read_collect<T: Table<Key = u64>>(
&self,
range: impl RangeBounds<T::Key>,
) -> ProviderResult<Vec<T::Value>> {
let capacity = match range_size_hint(&range) {
Some(0) | None => return Ok(Vec::new()),
Some(capacity) => capacity,
};
let mut cursor = self.tx.cursor_read::<T>()?;
self.cursor_collect_with_capacity(&mut cursor, range, capacity)
}
/// Iterates over read only values in the given table and collects them into a vector.
fn cursor_collect<T: Table<Key = u64>>(
&self,
cursor: &mut impl DbCursorRO<T>,
range: impl RangeBounds<T::Key>,
) -> ProviderResult<Vec<T::Value>> {
let capacity = range_size_hint(&range).unwrap_or(0);
self.cursor_collect_with_capacity(cursor, range, capacity)
}
fn cursor_collect_with_capacity<T: Table<Key = u64>>(
&self,
cursor: &mut impl DbCursorRO<T>,
range: impl RangeBounds<T::Key>,
capacity: usize,
) -> ProviderResult<Vec<T::Value>> {
let mut items = Vec::with_capacity(capacity);
for entry in cursor.walk_range(range)? {
items.push(entry?.1);
}
Ok(items)
}
fn transactions_by_tx_range_with_cursor<C>(
&self,
range: impl RangeBounds<TxNumber>,
@ -852,44 +782,12 @@ impl<TX: DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
}
}
impl<TX: DbTxMut + DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
/// Commit database transaction.
pub fn commit(self) -> ProviderResult<bool> {
Ok(self.tx.commit()?)
}
/// Remove list of entries from the table. Returns the number of entries removed.
#[inline]
pub fn remove<T: Table>(
&self,
range: impl RangeBounds<T::Key>,
) -> Result<usize, DatabaseError> {
let mut entries = 0;
let mut cursor_write = self.tx.cursor_write::<T>()?;
let mut walker = cursor_write.walk_range(range)?;
while walker.next().transpose()?.is_some() {
walker.delete_current()?;
entries += 1;
}
Ok(entries)
}
/// Return a list of entries from the table, and remove them, based on the given range.
#[inline]
pub fn take<T: Table>(
&self,
range: impl RangeBounds<T::Key>,
) -> Result<Vec<KeyValue<T>>, DatabaseError> {
let mut cursor_write = self.tx.cursor_write::<T>()?;
let mut walker = cursor_write.walk_range(range)?;
let mut items = Vec::new();
while let Some(i) = walker.next().transpose()? {
walker.delete_current()?;
items.push(i)
}
Ok(items)
}
/// Remove requested block transactions, without returning them.
///
/// This will remove block data for the given range from the following tables:
@ -1299,7 +1197,7 @@ impl<TX: DbTx, N: NodeTypes> ChangeSetReader for DatabaseProvider<TX, N> {
}
}
impl<TX: DbTx, N: NodeTypes> HeaderSyncGapProvider for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> HeaderSyncGapProvider for DatabaseProvider<TX, N> {
fn sync_gap(
&self,
tip: watch::Receiver<B256>,
@ -1343,7 +1241,7 @@ impl<TX: DbTx, N: NodeTypes> HeaderSyncGapProvider for DatabaseProvider<TX, N> {
}
}
impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> HeaderProvider
impl<TX: DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks>> HeaderProvider
for DatabaseProvider<TX, N>
{
fn header(&self, block_hash: &BlockHash) -> ProviderResult<Option<Header>> {
@ -1443,7 +1341,7 @@ impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> HeaderProvider
}
}
impl<TX: DbTx, N: NodeTypes> BlockHashReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> BlockHashReader for DatabaseProvider<TX, N> {
fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
self.static_file_provider.get_with_static_file_or_database(
StaticFileSegment::Headers,
@ -1470,7 +1368,7 @@ impl<TX: DbTx, N: NodeTypes> BlockHashReader for DatabaseProvider<TX, N> {
}
}
impl<TX: DbTx, N: NodeTypes> BlockNumReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> BlockNumReader for DatabaseProvider<TX, N> {
fn chain_info(&self) -> ProviderResult<ChainInfo> {
let best_number = self.best_block_number()?;
let best_hash = self.block_hash(best_number)?.unwrap_or_default();
@ -1501,7 +1399,9 @@ impl<TX: DbTx, N: NodeTypes> BlockNumReader for DatabaseProvider<TX, N> {
}
}
impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> BlockReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks>> BlockReader
for DatabaseProvider<TX, N>
{
fn find_block_by_hash(&self, hash: B256, source: BlockSource) -> ProviderResult<Option<Block>> {
if source.is_canonical() {
self.block(hash.into())
@ -1676,7 +1576,7 @@ impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> BlockReader for Datab
}
}
impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> TransactionsProviderExt
impl<TX: DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks>> TransactionsProviderExt
for DatabaseProvider<TX, N>
{
/// Recovers transaction hashes by walking through `Transactions` table and
@ -1746,7 +1646,7 @@ impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> TransactionsProviderE
}
// Calculates the hash of the given transaction
impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> TransactionsProvider
impl<TX: DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks>> TransactionsProvider
for DatabaseProvider<TX, N>
{
fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
@ -1906,7 +1806,7 @@ impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> TransactionsProvider
}
}
impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> ReceiptProvider
impl<TX: DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks>> ReceiptProvider
for DatabaseProvider<TX, N>
{
fn receipt(&self, id: TxNumber) -> ProviderResult<Option<Receipt>> {
@ -1954,7 +1854,7 @@ impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> ReceiptProvider
}
}
impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> WithdrawalsProvider
impl<TX: DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks>> WithdrawalsProvider
for DatabaseProvider<TX, N>
{
fn withdrawals_by_block(
@ -1984,7 +1884,7 @@ impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> WithdrawalsProvider
}
}
impl<TX: DbTx, N: NodeTypes<ChainSpec: EthereumHardforks>> EvmEnvProvider
impl<TX: DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks>> EvmEnvProvider
for DatabaseProvider<TX, N>
{
fn fill_env_at<EvmConfig>(
@ -2110,7 +2010,7 @@ impl<TX: DbTxMut, N: NodeTypes> StageCheckpointWriter for DatabaseProvider<TX, N
}
}
impl<TX: DbTx, N: NodeTypes> StorageReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> StorageReader for DatabaseProvider<TX, N> {
fn plain_state_storages(
&self,
addresses_with_keys: impl IntoIterator<Item = (Address, impl IntoIterator<Item = B256>)>,
@ -2173,7 +2073,7 @@ impl<TX: DbTx, N: NodeTypes> StorageReader for DatabaseProvider<TX, N> {
}
}
impl<TX: DbTxMut + DbTx, N: NodeTypes> StateChangeWriter for DatabaseProvider<TX, N> {
impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> StateChangeWriter for DatabaseProvider<TX, N> {
fn write_state_reverts(
&self,
reverts: PlainStateReverts,
@ -2550,7 +2450,7 @@ impl<TX: DbTxMut + DbTx, N: NodeTypes> StateChangeWriter for DatabaseProvider<TX
}
}
impl<TX: DbTxMut + DbTx, N: NodeTypes> TrieWriter for DatabaseProvider<TX, N> {
impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> TrieWriter for DatabaseProvider<TX, N> {
/// Writes trie updates. Returns the number of entries modified.
fn write_trie_updates(&self, trie_updates: &TrieUpdates) -> ProviderResult<usize> {
if trie_updates.is_empty() {
@ -2600,7 +2500,7 @@ impl<TX: DbTxMut + DbTx, N: NodeTypes> TrieWriter for DatabaseProvider<TX, N> {
}
}
impl<TX: DbTxMut + DbTx, N: NodeTypes> StorageTrieWriter for DatabaseProvider<TX, N> {
impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> StorageTrieWriter for DatabaseProvider<TX, N> {
/// Writes storage trie updates from the given storage trie map. First sorts the storage trie
/// updates by the hashed address, writing in sorted order.
fn write_storage_trie_updates(
@ -2637,7 +2537,7 @@ impl<TX: DbTxMut + DbTx, N: NodeTypes> StorageTrieWriter for DatabaseProvider<TX
}
}
impl<TX: DbTxMut + DbTx, N: NodeTypes> HashingWriter for DatabaseProvider<TX, N> {
impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HashingWriter for DatabaseProvider<TX, N> {
fn unwind_account_hashing<'a>(
&self,
changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
@ -2862,7 +2762,7 @@ impl<TX: DbTxMut + DbTx, N: NodeTypes> HashingWriter for DatabaseProvider<TX, N>
}
}
impl<TX: DbTxMut + DbTx, N: NodeTypes> HistoryWriter for DatabaseProvider<TX, N> {
impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HistoryWriter for DatabaseProvider<TX, N> {
fn unwind_account_history_indices<'a>(
&self,
changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
@ -2996,7 +2896,7 @@ impl<TX: DbTxMut + DbTx, N: NodeTypes> HistoryWriter for DatabaseProvider<TX, N>
}
}
impl<TX: DbTx, N: NodeTypes> StateReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> StateReader for DatabaseProvider<TX, N> {
fn get_state(&self, block: BlockNumber) -> ProviderResult<Option<ExecutionOutcome>> {
self.get_state(block..=block)
}
@ -3417,7 +3317,7 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes<ChainSpec: EthereumHardforks> +
}
}
impl<TX: DbTx, N: NodeTypes> PruneCheckpointReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> PruneCheckpointReader for DatabaseProvider<TX, N> {
fn get_prune_checkpoint(
&self,
segment: PruneSegment,
@ -3444,7 +3344,7 @@ impl<TX: DbTxMut, N: NodeTypes> PruneCheckpointWriter for DatabaseProvider<TX, N
}
}
impl<TX: DbTx, N: NodeTypes> StatsReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> StatsReader for DatabaseProvider<TX, N> {
fn count_entries<T: Table>(&self) -> ProviderResult<usize> {
let db_entries = self.tx.entries::<T>()?;
let static_file_entries = match self.static_file_provider.count_entries::<T>() {
@ -3457,7 +3357,7 @@ impl<TX: DbTx, N: NodeTypes> StatsReader for DatabaseProvider<TX, N> {
}
}
impl<TX: DbTx, N: NodeTypes> ChainStateBlockReader for DatabaseProvider<TX, N> {
impl<TX: DbTx + 'static, N: NodeTypes> ChainStateBlockReader for DatabaseProvider<TX, N> {
fn last_finalized_block_number(&self) -> ProviderResult<Option<BlockNumber>> {
let mut finalized_blocks = self
.tx
@ -3592,17 +3492,3 @@ fn recover_block_senders(
Ok(())
}
fn range_size_hint(range: &impl RangeBounds<TxNumber>) -> Option<usize> {
let start = match range.start_bound().cloned() {
Bound::Included(start) => start,
Bound::Excluded(start) => start.checked_add(1)?,
Bound::Unbounded => 0,
};
let end = match range.end_bound().cloned() {
Bound::Included(end) => end.saturating_add(1),
Bound::Excluded(end) => end,
Bound::Unbounded => return None,
};
end.checked_sub(start).map(|x| x as _)
}

View File

@ -1,5 +1,5 @@
//! Dummy blocks and data for tests
use crate::{DatabaseProviderRW, ExecutionOutcome};
use crate::{DBProvider, DatabaseProviderRW, ExecutionOutcome};
use alloy_consensus::{TxLegacy, EMPTY_OMMER_ROOT_HASH};
use alloy_primitives::{
b256, hex_literal::hex, map::HashMap, Address, BlockNumber, Bytes, Log, Sealable, TxKind, B256,

View File

@ -1,6 +1,14 @@
use reth_db_api::{database::Database, transaction::DbTx};
use reth_db_api::{
common::KeyValue,
cursor::DbCursorRO,
database::Database,
table::Table,
transaction::{DbTx, DbTxMut},
DatabaseError,
};
use reth_prune_types::PruneModes;
use reth_storage_errors::provider::ProviderResult;
use std::ops::{Bound, RangeBounds};
/// Database provider.
pub trait DBProvider: Send + Sync + Sized + 'static {
@ -34,6 +42,101 @@ pub trait DBProvider: Send + Sync + Sized + 'static {
/// Returns a reference to prune modes.
fn prune_modes_ref(&self) -> &PruneModes;
/// Return full table as Vec
fn table<T: Table>(&self) -> Result<Vec<KeyValue<T>>, DatabaseError>
where
T::Key: Default + Ord,
{
self.tx_ref()
.cursor_read::<T>()?
.walk(Some(T::Key::default()))?
.collect::<Result<Vec<_>, DatabaseError>>()
}
/// Return a list of entries from the table, based on the given range.
#[inline]
fn get<T: Table>(
&self,
range: impl RangeBounds<T::Key>,
) -> Result<Vec<KeyValue<T>>, DatabaseError> {
self.tx_ref().cursor_read::<T>()?.walk_range(range)?.collect::<Result<Vec<_>, _>>()
}
/// Iterates over read only values in the given table and collects them into a vector.
///
/// Early-returns if the range is empty, without opening a cursor transaction.
fn cursor_read_collect<T: Table<Key = u64>>(
&self,
range: impl RangeBounds<T::Key>,
) -> ProviderResult<Vec<T::Value>> {
let capacity = match range_size_hint(&range) {
Some(0) | None => return Ok(Vec::new()),
Some(capacity) => capacity,
};
let mut cursor = self.tx_ref().cursor_read::<T>()?;
self.cursor_collect_with_capacity(&mut cursor, range, capacity)
}
/// Iterates over read only values in the given table and collects them into a vector.
fn cursor_collect<T: Table<Key = u64>>(
&self,
cursor: &mut impl DbCursorRO<T>,
range: impl RangeBounds<T::Key>,
) -> ProviderResult<Vec<T::Value>> {
let capacity = range_size_hint(&range).unwrap_or(0);
self.cursor_collect_with_capacity(cursor, range, capacity)
}
/// Iterates over read only values in the given table and collects them into a vector with
/// capacity.
fn cursor_collect_with_capacity<T: Table<Key = u64>>(
&self,
cursor: &mut impl DbCursorRO<T>,
range: impl RangeBounds<T::Key>,
capacity: usize,
) -> ProviderResult<Vec<T::Value>> {
let mut items = Vec::with_capacity(capacity);
for entry in cursor.walk_range(range)? {
items.push(entry?.1);
}
Ok(items)
}
/// Remove list of entries from the table. Returns the number of entries removed.
#[inline]
fn remove<T: Table>(&self, range: impl RangeBounds<T::Key>) -> Result<usize, DatabaseError>
where
Self::Tx: DbTxMut,
{
let mut entries = 0;
let mut cursor_write = self.tx_ref().cursor_write::<T>()?;
let mut walker = cursor_write.walk_range(range)?;
while walker.next().transpose()?.is_some() {
walker.delete_current()?;
entries += 1;
}
Ok(entries)
}
/// Return a list of entries from the table, and remove them, based on the given range.
#[inline]
fn take<T: Table>(
&self,
range: impl RangeBounds<T::Key>,
) -> Result<Vec<KeyValue<T>>, DatabaseError>
where
Self::Tx: DbTxMut,
{
let mut cursor_write = self.tx_ref().cursor_write::<T>()?;
let mut walker = cursor_write.walk_range(range)?;
let mut items = Vec::new();
while let Some(i) = walker.next().transpose()? {
walker.delete_current()?;
items.push(i)
}
Ok(items)
}
}
/// Database provider factory.
@ -54,3 +157,17 @@ pub trait DatabaseProviderFactory: Send + Sync {
/// Create new read-write database provider.
fn database_provider_rw(&self) -> ProviderResult<Self::ProviderRW>;
}
fn range_size_hint(range: &impl RangeBounds<u64>) -> Option<usize> {
let start = match range.start_bound().cloned() {
Bound::Included(start) => start,
Bound::Excluded(start) => start.checked_add(1)?,
Bound::Unbounded => 0,
};
let end = match range.end_bound().cloned() {
Bound::Included(end) => end.saturating_add(1),
Bound::Excluded(end) => end,
Bound::Unbounded => return None,
};
end.checked_sub(start).map(|x| x as _)
}