store.rs 7 KB
Newer Older
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Shared three-collection lifecycle store backing both
//! [`ExternalBlockAssignments`](super::assignments::ExternalBlockAssignments) and
//! [`LogicalBlockAssignments`](super::assignments::LogicalBlockAssignments).

use indexmap::IndexMap;

use crate::BlockId;

/// Generic three-phase lifecycle store keyed by [`BlockId`].
///
/// Manages three ordered `IndexMap` collections representing the lifecycle
/// phases: **unassigned** (`U`) → **staged** (`S`) → **assigned** (`A`).
///
/// Both [`ExternalBlockAssignments`](super::assignments::ExternalBlockAssignments) and
/// [`LogicalBlockAssignments`](super::assignments::LogicalBlockAssignments) compose this
/// type internally and add their own type-specific transition logic on top.
pub(crate) struct BlockStore<U, S, A> {
    assigned: IndexMap<BlockId, A>,
    staged: IndexMap<BlockId, S>,
    unassigned: IndexMap<BlockId, U>,
}

impl<U, S, A> BlockStore<U, S, A> {
    /// Creates an empty store.
    pub fn new() -> Self {
        Self {
            assigned: IndexMap::new(),
            staged: IndexMap::new(),
            unassigned: IndexMap::new(),
        }
    }

    // -- Counts ---------------------------------------------------------------

    /// Returns the number of assigned entries.
    pub fn assigned_count(&self) -> usize {
        self.assigned.len()
    }

    /// Returns the number of staged entries.
    pub fn staged_count(&self) -> usize {
        self.staged.len()
    }

    /// Returns the number of unassigned entries.
    pub fn unassigned_count(&self) -> usize {
        self.unassigned.len()
    }

    // -- Queries --------------------------------------------------------------

    /// Returns `true` if all three collections are empty.
    pub fn is_empty(&self) -> bool {
        self.assigned.is_empty() && self.staged.is_empty() && self.unassigned.is_empty()
    }

    /// Checks whether a `BlockId` is present in any of the three collections.
    pub fn contains(&self, block_id: &BlockId) -> bool {
        self.assigned.contains_key(block_id)
            || self.staged.contains_key(block_id)
            || self.unassigned.contains_key(block_id)
    }

    /// Checks whether a `BlockId` is present in the assigned collection.
    #[allow(dead_code)]
    pub fn contains_assigned(&self, block_id: &BlockId) -> bool {
        self.assigned.contains_key(block_id)
    }

    /// Checks whether a `BlockId` is present in the staged collection.
    #[allow(dead_code)]
    pub fn contains_staged(&self, block_id: &BlockId) -> bool {
        self.staged.contains_key(block_id)
    }

    /// Checks whether a `BlockId` is present in the unassigned collection.
    #[allow(dead_code)]
    pub fn contains_unassigned(&self, block_id: &BlockId) -> bool {
        self.unassigned.contains_key(block_id)
    }

    // -- Index Access ---------------------------------------------------------

    /// Returns the assigned entry at the given index (insertion order).
    pub fn get_assigned(&self, index: usize) -> Option<(&BlockId, &A)> {
        self.assigned.get_index(index)
    }

    /// Returns the staged entry at the given index (staging order).
    pub fn get_staged(&self, index: usize) -> Option<(&BlockId, &S)> {
        self.staged.get_index(index)
    }

    /// Returns the unassigned entry at the given index (FIFO order).
    pub fn get_unassigned(&self, index: usize) -> Option<(&BlockId, &U)> {
        self.unassigned.get_index(index)
    }

    // -- Iteration ------------------------------------------------------------

    /// Iterates over assigned entries in positional order.
    pub fn assigned_iter(&self) -> impl Iterator<Item = (&BlockId, &A)> {
        self.assigned.iter()
    }

    /// Iterates over staged entries in staging order.
    pub fn staged_iter(&self) -> impl Iterator<Item = (&BlockId, &S)> {
        self.staged.iter()
    }

    /// Iterates over unassigned entries in FIFO order.
    pub fn unassigned_iter(&self) -> impl Iterator<Item = (&BlockId, &U)> {
        self.unassigned.iter()
    }

    // -- FIFO Pop -------------------------------------------------------------

    /// Removes and returns the first unassigned entry (FIFO).
    pub fn shift_unassigned(&mut self) -> Option<(BlockId, U)> {
        self.unassigned.shift_remove_index(0)
    }

    /// Removes and returns the first staged entry (FIFO).
    pub fn shift_staged(&mut self) -> Option<(BlockId, S)> {
        self.staged.shift_remove_index(0)
    }

    // -- LIFO Pop -------------------------------------------------------------

    /// Removes and returns the last unassigned entry (LIFO).
    pub fn pop_unassigned(&mut self) -> Option<(BlockId, U)> {
        self.unassigned.pop()
    }

    // -- Insert ---------------------------------------------------------------

    /// Inserts into the assigned collection.
    pub fn insert_assigned(&mut self, id: BlockId, val: A) {
        self.assigned.insert(id, val);
    }

    /// Inserts into the staged collection.
    pub fn insert_staged(&mut self, id: BlockId, val: S) {
        self.staged.insert(id, val);
    }

    /// Inserts into the unassigned collection.
    pub fn insert_unassigned(&mut self, id: BlockId, val: U) {
        self.unassigned.insert(id, val);
    }

    // -- Bulk -----------------------------------------------------------------

    /// Iterates over all block IDs across all three collections in lifecycle
    /// order: assigned → staged → unassigned.
    pub fn all_block_ids(&self) -> impl Iterator<Item = &BlockId> {
        self.assigned
            .keys()
            .chain(self.staged.keys())
            .chain(self.unassigned.keys())
    }

    /// Validates that none of the given `ids` collide with existing entries
    /// or with each other.
    ///
    /// Returns `Ok(())` if all IDs are unique, or `Err(id)` with the first
    /// duplicate found.
    pub fn validate_no_duplicates(
        &self,
        ids: impl Iterator<Item = BlockId>,
        count_hint: usize,
    ) -> Result<(), BlockId> {
        let mut seen = indexmap::IndexSet::with_capacity(count_hint);
        for id in ids {
            if self.contains(&id) || !seen.insert(id) {
                return Err(id);
            }
        }
        Ok(())
    }

    /// Clears all three collections.
    pub fn clear(&mut self) {
        self.assigned.clear();
        self.staged.clear();
        self.unassigned.clear();
    }

    /// Takes all assigned entries, returning them as a `Vec`.
    pub fn take_assigned(&mut self) -> Vec<(BlockId, A)> {
        std::mem::take(&mut self.assigned).into_iter().collect()
    }

    /// Takes all staged entries, returning them as a `Vec`.
    pub fn take_staged(&mut self) -> Vec<(BlockId, S)> {
        std::mem::take(&mut self.staged).into_iter().collect()
    }

    /// Takes all unassigned entries, returning them as a `Vec`.
    pub fn take_unassigned(&mut self) -> Vec<(BlockId, U)> {
        std::mem::take(&mut self.unassigned).into_iter().collect()
    }
}