1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::{fmt::Debug, marker::PhantomData};

/// Configuration parameters to tune the behavior of an IDR.
///
/// The capacity of an IDR is determined by the configuration parameters:
/// ```text
///     (2**MAX_PAGES - 1) * INITIAL_PAGE_SIZE
/// ```
///
/// [`Idr::new()`] checks that the configuration is valid at compile time.
/// These checks are triggered by `cargo build` or `cargo test`, but not
/// by `cargo check`.
///
/// [`Idr::new()`]: crate::Idr::new
pub trait Config: Sized {
    /// The capacity of the first page.
    ///
    /// When a page in an underlying slab has been filled with values, a new
    /// page will be allocated that is twice as large as the previous page.
    /// Thus, the second page will be twice this size, and the third will be
    /// four times this size, and so on.
    ///
    /// **Must** be a power of two.
    const INITIAL_PAGE_SIZE: u32 = DefaultConfig::INITIAL_PAGE_SIZE;

    /// The maximum number of pages in an underlying slab of an IDR.
    ///
    /// This value, in combination with `INITIAL_PAGE_SIZE`, determines how many
    /// bits of each key are used to represent slot indices.
    ///
    /// **Must** be positive.
    const MAX_PAGES: u32 = DefaultConfig::MAX_PAGES;

    /// A number of **high-order** bits which are reserved from user code.
    ///
    /// Note: these bits are taken from the generation counter; reserving
    /// additional bits will decrease the period of the generation counter.
    /// These should thus be used relatively sparingly, to ensure that
    /// generation counters are able to effectively prevent the ABA problem.
    ///
    /// **Must** be less than or equal to 32.
    const RESERVED_BITS: u32 = DefaultConfig::RESERVED_BITS;

    /// Returns a debug representation of the configuration, which includes all
    /// internally calculated values and limits.
    #[must_use]
    fn debug() -> impl Debug {
        DebugConfig::<Self>(PhantomData)
    }
}

/// A default configuration:
/// * No bits reserved for user code.
/// * A capacity is 4,294,967,264.
/// * A generation counter with a period of 4,294,967,296.
#[allow(missing_debug_implementations)] // `Config::debug()` instead
pub struct DefaultConfig;

impl Config for DefaultConfig {
    const INITIAL_PAGE_SIZE: u32 = 32;
    const MAX_PAGES: u32 = 27;
    const RESERVED_BITS: u32 = 0;
}

pub(crate) trait ConfigPrivate: Config {
    const USED_BITS: u32 = 64 - Self::RESERVED_BITS;
    const SLOT_BITS: u32 = Self::MAX_PAGES + Self::INITIAL_PAGE_SIZE.trailing_zeros();
    const SLOT_MASK: u32 = ((1u64 << Self::SLOT_BITS) - 1) as u32;
    const GENERATION_BITS: u32 = Self::USED_BITS - Self::SLOT_BITS;
    const GENERATION_MASK: u32 = ((1u64 << Self::GENERATION_BITS) - 1) as u32;

    // For debugging and tests, both values are `<= u32::MAX + 1`.
    const MAX_SLOTS: u64 = ((1u64 << Self::MAX_PAGES) - 1) * Self::INITIAL_PAGE_SIZE as u64;
    const MAX_GENERATIONS: u64 = 1u64 << Self::GENERATION_BITS;

    // Compile-time (only test/build, not check) postmono constraints.
    // https://t.me/terriblerust/38
    const ENSURE_VALID: bool = {
        assert!(Self::INITIAL_PAGE_SIZE.is_power_of_two());
        assert!(Self::MAX_PAGES > 0);
        assert!(Self::RESERVED_BITS <= 32);
        assert!(Self::SLOT_BITS <= 32);
        assert!(Self::GENERATION_BITS <= 32);
        true
    };
}

impl<C: Config> ConfigPrivate for C {}

struct DebugConfig<C>(PhantomData<C>);

impl<C: Config> Debug for DebugConfig<C> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct(std::any::type_name::<C>())
            .field("INITIAL_PAGE_SIZE", &C::INITIAL_PAGE_SIZE)
            .field("MAX_PAGES", &C::MAX_PAGES)
            .field("RESERVED_BITS", &C::RESERVED_BITS)
            .field("USED_BITS", &C::USED_BITS)
            .field("SLOT_BITS", &C::SLOT_BITS)
            .field("GENERATION_BITS", &C::GENERATION_BITS)
            .field("MAX_SLOTS", &C::MAX_SLOTS)
            .field("MAX_GENERATIONS", &C::MAX_GENERATIONS)
            .finish()
    }
}

#[test]
fn test_default_config() {
    assert_eq!(DefaultConfig::USED_BITS, 64);
    assert_eq!(DefaultConfig::SLOT_BITS, 32);
    assert_eq!(DefaultConfig::SLOT_MASK, u32::MAX);
    assert_eq!(DefaultConfig::GENERATION_BITS, 32);
    assert_eq!(DefaultConfig::GENERATION_MASK, u32::MAX);
    assert_eq!(DefaultConfig::MAX_SLOTS, 4_294_967_264);
    assert_eq!(DefaultConfig::MAX_GENERATIONS, 4_294_967_296);
}