You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: bucket_map_holder.rs
    path of file: ./repos/solana/accounts-db/src
    
    the code of the file until where you have to start completion: use {
    crate::{
        accounts_index::{
            in_mem_accounts_index::{InMemAccountsIndex, StartupStats},
            AccountsIndexConfig, DiskIndexValue, IndexLimitMb, IndexValue,
        },
        bucket_map_holder_stats::BucketMapHolderStats,
        waitable_condvar::WaitableCondvar,
    },
    solana_bucket_map::bucket_map::{BucketMap, BucketMapConfig},
    solana_measure::measure::Measure,
    solana_sdk::{
        clock::{Slot, DEFAULT_MS_PER_SLOT},
        timing::AtomicInterval,
    },
    std::{
        fmt::Debug,
        marker::PhantomData,
        sync::{
            atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
            Arc,
        },
        time::Duration,
    },
};
pub type Age = u8;
pub type AtomicAge = AtomicU8;
const _: () = assert!(std::mem::size_of::<Age>() == std::mem::size_of::<AtomicAge>());

const AGE_MS: u64 = DEFAULT_MS_PER_SLOT; // match one age per slot time

// 10 GB limit for in-mem idx. In practice, we don't get this high. This tunes how aggressively to save items we expect to use soon.
pub const DEFAULT_DISK_INDEX: Option<usize> = Some(10_000);

pub struct BucketMapHolder<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> {
    pub disk: Option<BucketMap<(Slot, U)>>,

    pub count_buckets_flushed: AtomicUsize,

    /// These three ages are individual atomics because their values are read many times from code during runtime.
    /// Instead of accessing the single age and doing math each time, each value is incremented each time the age occurs, which is ~400ms.
    /// Callers can ask for the precomputed value they already want.
    /// rolling 'current' age
    pub age: AtomicAge,
    /// rolling age that is 'ages_to_stay_in_cache' + 'age'
    pub future_age_to_flush: AtomicAge,
    /// rolling age that is effectively 'age' - 1
    /// these items are expected to be flushed from the accounts write cache or otherwise modified before this age occurs
    pub future_age_to_flush_cached: AtomicAge,

    pub stats: BucketMapHolderStats,

    age_timer: AtomicInterval,

    // used by bg processing to know when any bucket has become dirty
    pub wait_dirty_or_aged: Arc<WaitableCondvar>,
    next_bucket_to_flush: AtomicUsize,
    bins: usize,

    pub threads: usize,

    // how much mb are we allowed to keep in the in-mem index?
    // Rest goes to disk.
    pub mem_budget_mb: Option<usize>,

    /// how many ages should elapse from the last time an item is used where the item will remain in the cache
    pub ages_to_stay_in_cache: Age,

    /// startup is a special time for flush to focus on moving everything to disk as fast and efficiently as possible
    /// with less thread count limitations. LRU and access patterns are not important. Freeing memory
    /// and writing to disk in parallel are.
    /// Note startup is an optimization and is not required for correctness.
    startup: AtomicBool,
    _phantom: PhantomData<T>,

    pub(crate) startup_stats: Arc<StartupStats>,
}

impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> Debug for BucketMapHolder<T, U> {
    fn f
    
    pub fn new(bins: usize, config: &Option<AccountsIndexConfig>, threads: usize) -> Self {
        const DEFAULT_AGE_TO_STAY_IN_CACHE: Age = 5;
        let ages_to_stay_in_cache = config
            .as_ref()
            .and_then(|config| config.ages_to_stay_in_cache)
            .unwrap_or(DEFAULT_AGE_TO_STAY_IN_CACHE);

        let mut bucket_config = BucketMapConfig::new(bins);
        bucket_config.drives = config.as_ref().and_then(|config| {
            bucket_config.restart_config_file = config.drives.as_ref().and_then(|drives| {
                drives
                    .first()
                    .map(|drive| drive.join("accounts_index_restart"))
            });
            config.drives.clone()
        });

        let mem_budget_mb = match config
            .as_ref()
            .map(|config| &config.index_limit_mb)
            .unwrap_or(&IndexLimitMb::Unspecified)
        {
            // creator said to use disk idx with a specific limit
            IndexLimitMb::Limit(mb) => Some(*mb),
            // creator said InMemOnly, so no disk index
            IndexLimitMb::InMemOnly => None,
            // whatever started us didn't specify whether to use the acct idx
            IndexLimitMb::Unspecified => {
                // check env var if we were not started from a validator
                let mut use_default = true;
                if !config
                    .as_ref()
                    .map(|config| config.started_from_validator)
                    .unwrap_or_default()
                {
                    if let Ok(_limit) = std::env::var("SOLANA_TEST_ACCOUNTS_INDEX_MEMORY_LIMIT_MB")
                    {
                        // Note this env var means the opposite of the default. The default now is disk index is on.
                        // So, if this env var is set, DO NOT allocate with disk buckets if mem budget was not set, we were NOT started from validator, and env var was set
                        // we do not want the env var to have an effect when running the validator (only tests, benches, etc.)
                        use_default = false;
                    }
                }
                if use_default {
                    // if validator does not specify disk index limit or specify in mem only, then this is the default
                    DEFAULT_DISK_INDEX
                } else {
                    None
                }
            }
        };

        // only allocate if mem_budget_mb is Some
        let disk = mem_budget_mb.map(|_| BucketMap::new(bucket_config));
        Self {
            disk,
            ages_to_stay_in_cache,
            count_buckets_flushed: AtomicUsize::default(),
            // age = 0
            age: AtomicAge::default(),
            // future age = age (=0) + ages_to_stay_in_cache
            future_age_to_flush: AtomicAge::new(ages_to_stay_in_cache),
            // effectively age (0) - 1. So, the oldest possible age from 'now'
            future_age_to_flush_cached: AtomicAge::new(Age::MAX),
            stats: BucketMapHolderStats::new(bins),
            wait_dirty_or_aged: Arc::default(),
            next_bucket_to_flush: AtomicUsize::new(0),
            age_timer: AtomicInterval::default(),
            bins,
            startup: AtomicBool::default(),
            mem_budget_mb,
            threads,
            _phantom: PhantomData,
            startup_stats: Arc::default(),
        }
    }