Skip to main content

kvbm_engine/offload/
source.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Source block types for the offload engine.
5//!
6//! Blocks can be provided to the offload engine in three forms:
7//! - External: BlockId + SequenceHash, block is held elsewhere
8//! - Strong: RAII ImmutableBlock reference
9//! - Weak: WeakBlock that may have been evicted
10
11use std::marker::PhantomData;
12
13use crate::{BlockId, SequenceHash};
14use kvbm_logical::blocks::{BlockMetadata, ImmutableBlock, WeakBlock};
15
16/// External block reference with sequence hash for registration.
17///
18/// Used when the caller holds the actual block but wants to provide
19/// the offload engine with enough information to register blocks
20/// in the destination tier after transfer.
21#[derive(Debug, Clone, Copy)]
22pub struct ExternalBlock<T: BlockMetadata> {
23    /// The block ID in the source tier
24    pub block_id: BlockId,
25    /// The sequence hash for registration in destination tier
26    pub sequence_hash: SequenceHash,
27    _marker: PhantomData<T>,
28}
29
30impl<T: BlockMetadata> ExternalBlock<T> {
31    /// Create a new external block reference.
32    pub fn new(block_id: BlockId, sequence_hash: SequenceHash) -> Self {
33        Self {
34            block_id,
35            sequence_hash,
36            _marker: PhantomData,
37        }
38    }
39}
40
41/// Represents a single block source for offloading.
42///
43/// The source type determines how the block is resolved:
44/// - `External`: Caller holds the block, we have ID + SequenceHash for registration
45/// - `Strong`: We hold a strong RAII reference
46/// - `Weak`: We hold a weak reference that may need upgrading
47#[derive(Debug)]
48pub enum SourceBlock<T: BlockMetadata> {
49    /// External block reference with ID and sequence hash
50    External(ExternalBlock<T>),
51    /// Strong RAII reference to an immutable block
52    Strong(ImmutableBlock<T>),
53    /// Weak reference that may have been evicted
54    Weak(WeakBlock<T>),
55}
56
57impl<T: BlockMetadata> SourceBlock<T> {
58    /// Get the block ID if available without upgrading.
59    ///
60    /// For External and Strong variants, returns Some(id).
61    /// For Weak variant, returns None (would need upgrade to get ID).
62    pub fn block_id(&self) -> Option<BlockId> {
63        match self {
64            SourceBlock::External(ext) => Some(ext.block_id),
65            SourceBlock::Strong(block) => Some(block.block_id()),
66            SourceBlock::Weak(_) => None,
67        }
68    }
69
70    /// Get the sequence hash if available without upgrading.
71    ///
72    /// All variants can provide sequence_hash without upgrading:
73    /// - External: stored in ExternalBlock
74    /// - Strong: from ImmutableBlock
75    /// - Weak: WeakBlock stores sequence_hash directly
76    pub fn sequence_hash(&self) -> Option<SequenceHash> {
77        match self {
78            SourceBlock::External(ext) => Some(ext.sequence_hash),
79            SourceBlock::Strong(block) => Some(block.sequence_hash()),
80            SourceBlock::Weak(weak) => Some(weak.sequence_hash()),
81        }
82    }
83
84    /// Check if this is an external block reference.
85    pub fn is_external(&self) -> bool {
86        matches!(self, SourceBlock::External(_))
87    }
88
89    /// Check if this is a strong reference.
90    pub fn is_strong(&self) -> bool {
91        matches!(self, SourceBlock::Strong(_))
92    }
93
94    /// Check if this is a weak reference.
95    pub fn is_weak(&self) -> bool {
96        matches!(self, SourceBlock::Weak(_))
97    }
98}
99
100impl<T: BlockMetadata> From<ExternalBlock<T>> for SourceBlock<T> {
101    fn from(ext: ExternalBlock<T>) -> Self {
102        SourceBlock::External(ext)
103    }
104}
105
106impl<T: BlockMetadata> From<ImmutableBlock<T>> for SourceBlock<T> {
107    fn from(block: ImmutableBlock<T>) -> Self {
108        SourceBlock::Strong(block)
109    }
110}
111
112impl<T: BlockMetadata> From<WeakBlock<T>> for SourceBlock<T> {
113    fn from(block: WeakBlock<T>) -> Self {
114        SourceBlock::Weak(block)
115    }
116}
117
118/// Collection of source blocks for batch operations.
119///
120/// Blocks are grouped by their source type for efficient processing.
121/// All blocks in a SourceBlocks must be of the same type.
122#[derive(Debug)]
123pub enum SourceBlocks<T: BlockMetadata> {
124    /// External block references with IDs and sequence hashes
125    External(Vec<ExternalBlock<T>>),
126    /// Strong RAII references
127    Strong(Vec<ImmutableBlock<T>>),
128    /// Weak references that may need upgrading
129    Weak(Vec<WeakBlock<T>>),
130}
131
132impl<T: BlockMetadata> SourceBlocks<T> {
133    /// Create an empty collection of external blocks.
134    pub fn empty_external() -> Self {
135        SourceBlocks::External(Vec::new())
136    }
137
138    /// Create an empty collection of strong blocks.
139    pub fn empty_strong() -> Self {
140        SourceBlocks::Strong(Vec::new())
141    }
142
143    /// Create an empty collection of weak blocks.
144    pub fn empty_weak() -> Self {
145        SourceBlocks::Weak(Vec::new())
146    }
147
148    /// Get the number of blocks in this collection.
149    pub fn len(&self) -> usize {
150        match self {
151            SourceBlocks::External(blocks) => blocks.len(),
152            SourceBlocks::Strong(blocks) => blocks.len(),
153            SourceBlocks::Weak(blocks) => blocks.len(),
154        }
155    }
156
157    /// Check if the collection is empty.
158    pub fn is_empty(&self) -> bool {
159        self.len() == 0
160    }
161
162    /// Get external blocks, or None for other types.
163    pub fn external_blocks(&self) -> Option<&[ExternalBlock<T>]> {
164        match self {
165            SourceBlocks::External(blocks) => Some(blocks),
166            _ => None,
167        }
168    }
169
170    /// Get strong blocks, or None for other types.
171    pub fn strong_blocks(&self) -> Option<&[ImmutableBlock<T>]> {
172        match self {
173            SourceBlocks::Strong(blocks) => Some(blocks),
174            _ => None,
175        }
176    }
177
178    /// Get weak blocks, or None for other types.
179    pub fn weak_blocks(&self) -> Option<&[WeakBlock<T>]> {
180        match self {
181            SourceBlocks::Weak(blocks) => Some(blocks),
182            _ => None,
183        }
184    }
185
186    /// Check if this is external blocks.
187    pub fn is_external(&self) -> bool {
188        matches!(self, SourceBlocks::External(_))
189    }
190
191    /// Check if this is strong blocks.
192    pub fn is_strong(&self) -> bool {
193        matches!(self, SourceBlocks::Strong(_))
194    }
195
196    /// Check if this is weak blocks.
197    pub fn is_weak(&self) -> bool {
198        matches!(self, SourceBlocks::Weak(_))
199    }
200}
201
202impl<T: BlockMetadata> From<Vec<ExternalBlock<T>>> for SourceBlocks<T> {
203    fn from(blocks: Vec<ExternalBlock<T>>) -> Self {
204        SourceBlocks::External(blocks)
205    }
206}
207
208impl<T: BlockMetadata> From<Vec<ImmutableBlock<T>>> for SourceBlocks<T> {
209    fn from(blocks: Vec<ImmutableBlock<T>>) -> Self {
210        SourceBlocks::Strong(blocks)
211    }
212}
213
214impl<T: BlockMetadata> From<Vec<WeakBlock<T>>> for SourceBlocks<T> {
215    fn from(blocks: Vec<WeakBlock<T>>) -> Self {
216        SourceBlocks::Weak(blocks)
217    }
218}
219
220// Allow converting a single SourceBlock into SourceBlocks
221impl<T: BlockMetadata> From<SourceBlock<T>> for SourceBlocks<T> {
222    fn from(block: SourceBlock<T>) -> Self {
223        match block {
224            SourceBlock::External(ext) => SourceBlocks::External(vec![ext]),
225            SourceBlock::Strong(b) => SourceBlocks::Strong(vec![b]),
226            SourceBlock::Weak(b) => SourceBlocks::Weak(vec![b]),
227        }
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use kvbm_common::tokens::TokenBlockSequence;
235    use kvbm_logical::KvbmSequenceHashProvider;
236
237    /// Create a test sequence hash at a given position.
238    fn test_seq_hash(position: usize) -> SequenceHash {
239        let tokens_per_block = 4;
240        let total_tokens = (position + 1) * tokens_per_block;
241        let tokens: Vec<u32> = (0..total_tokens as u32).collect();
242        let seq = TokenBlockSequence::from_slice(&tokens, tokens_per_block as u32, Some(1337));
243        seq.blocks()[position].kvbm_sequence_hash()
244    }
245
246    #[test]
247    fn test_external_block_creation() {
248        let hash = test_seq_hash(0);
249        let ext: ExternalBlock<()> = ExternalBlock::new(42, hash);
250        assert_eq!(ext.block_id, 42);
251        assert_eq!(ext.sequence_hash, hash);
252    }
253
254    #[test]
255    fn test_source_blocks_from_vec_external() {
256        let ext1: ExternalBlock<()> = ExternalBlock::new(1, test_seq_hash(0));
257        let ext2: ExternalBlock<()> = ExternalBlock::new(2, test_seq_hash(1));
258        let ext3: ExternalBlock<()> = ExternalBlock::new(3, test_seq_hash(2));
259        let blocks: SourceBlocks<()> = vec![ext1, ext2, ext3].into();
260        assert!(blocks.is_external());
261        assert_eq!(blocks.len(), 3);
262        let external = blocks.external_blocks().unwrap();
263        assert_eq!(external[0].block_id, 1);
264        assert_eq!(external[1].block_id, 2);
265        assert_eq!(external[2].block_id, 3);
266    }
267
268    #[test]
269    fn test_source_blocks_empty() {
270        let blocks: SourceBlocks<()> = SourceBlocks::empty_external();
271        assert!(blocks.is_empty());
272        assert!(blocks.is_external());
273    }
274
275    #[test]
276    fn test_source_block_accessors() {
277        let hash = test_seq_hash(5);
278        let ext: ExternalBlock<()> = ExternalBlock::new(42, hash);
279        let block: SourceBlock<()> = ext.into();
280        assert_eq!(block.block_id(), Some(42));
281        assert_eq!(block.sequence_hash(), Some(hash));
282        assert!(block.is_external());
283    }
284}