dynamo_llm/
tokens.rs

1// SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4#![allow(dead_code)]
5
6//! Types and utilities for handling sequences of tokens, including block creation and hashing.
7
8use bytemuck::cast_slice;
9use derive_getters::Dissolve;
10use rayon::prelude::*;
11use std::ops::Range;
12
13pub mod blocks;
14
15/// A token is represented as a 32-bit unsigned integer.
16pub type Token = u32;
17
18/// A salt used for hashing, represented as a vector of bytes.
19/// This might encode model architecture, weights, PEFT info, etc.
20pub type Salt = Vec<u8>;
21
22/// A 64-bit hash of the salt, computed using [`compute_hash_v2`] with a seed of 0.
23/// Used as the initial seed for subsequent block hashes.
24pub type SaltHash = u64;
25
26/// A 64-bit hash computed only from the tokens within a single block.
27/// It uses [`compute_hash_v2`] with the [`SaltHash`] as the seed.
28pub type BlockHash = u64;
29
30/// A 64-bit sequence-aware hash.
31/// It combines the previous block's [`SequenceHash`] (or the [`SaltHash`] for the first block)
32/// with the current block's [`BlockHash`] using [`compute_hash_v2`] and the [`SaltHash`] as the seed.
33pub type SequenceHash = u64;
34
35/// Computes a hash of the data using the given seed.
36pub fn compute_hash_v2(data: &[u8], seed: u64) -> u64 {
37    xxhash_rust::xxh3::xxh3_64_with_seed(data, seed)
38}
39
40/// A collection of tokens, represented as a `Vec<Token>`.
41///
42/// Provides convenience methods for conversion and manipulation.
43#[derive(Debug, Clone, Dissolve, Default, Eq)]
44pub struct Tokens(Vec<Token>);
45
46impl AsRef<[Token]> for Tokens {
47    fn as_ref(&self) -> &[Token] {
48        &self.0
49    }
50}
51
52impl std::ops::Deref for Tokens {
53    type Target = [Token];
54
55    fn deref(&self) -> &Self::Target {
56        &self.0
57    }
58}
59
60impl std::borrow::Borrow<[Token]> for Tokens {
61    fn borrow(&self) -> &[Token] {
62        &self.0
63    }
64}
65
66impl From<Vec<Token>> for Tokens {
67    fn from(tokens: Vec<Token>) -> Self {
68        Tokens(tokens)
69    }
70}
71
72impl From<&[Token]> for Tokens {
73    fn from(tokens: &[Token]) -> Self {
74        Tokens(tokens.to_vec())
75    }
76}
77
78impl From<Vec<usize>> for Tokens {
79    fn from(tokens: Vec<usize>) -> Self {
80        Tokens(tokens.into_iter().map(|t| t as u32).collect())
81    }
82}
83
84impl From<Vec<i32>> for Tokens {
85    /// Converts `Vec<i32>` to `Tokens`, casting each `i32` to `u32`.
86    fn from(tokens: Vec<i32>) -> Self {
87        Tokens(tokens.into_iter().map(|t| t as u32).collect())
88    }
89}
90
91impl From<&[i32]> for Tokens {
92    /// Converts `&[i32]` to `Tokens`, casting each `i32` to `u32`.
93    fn from(tokens: &[i32]) -> Self {
94        Tokens(tokens.iter().map(|&t| t as u32).collect())
95    }
96}
97
98impl From<Tokens> for Vec<Token> {
99    fn from(tokens: Tokens) -> Self {
100        tokens.0
101    }
102}
103
104// PartialEq implementations for comparing Tokens with Vec<Token> and &[Token]
105// (Generated implementations are usually sufficient, but explicit ones can be clearer)
106impl PartialEq<Vec<Token>> for Tokens {
107    fn eq(&self, other: &Vec<Token>) -> bool {
108        self.0 == *other
109    }
110}
111
112impl PartialEq<Tokens> for Vec<Token> {
113    fn eq(&self, other: &Tokens) -> bool {
114        *self == other.0
115    }
116}
117
118impl PartialEq<[Token]> for Tokens {
119    fn eq(&self, other: &[Token]) -> bool {
120        self.0.as_slice() == other
121    }
122}
123
124impl PartialEq<Tokens> for &[Token] {
125    fn eq(&self, other: &Tokens) -> bool {
126        *self == other.0.as_slice()
127    }
128}
129
130impl PartialEq for Tokens {
131    fn eq(&self, other: &Self) -> bool {
132        self.0 == other.0
133    }
134}
135
136// Add PartialEq<&[T]> where T: Into<Token> + Copy could be more general,
137// but specifically implementing for &[Token] is sufficient for the tests.
138impl PartialEq<&[Token]> for Tokens {
139    fn eq(&self, other: &&[Token]) -> bool {
140        self.0.as_slice() == *other
141    }
142}
143
144impl Tokens {
145    /// Consumes the [`Tokens`] object and creates a [`TokenBlockSequence`].
146    ///
147    /// The sequence is initialized with the provided tokens, splitting them into blocks
148    /// of the specified `block_size` using the given `salt_hash` (or 0 if `None`).
149    ///
150    /// # Arguments
151    ///
152    /// * `block_size` - The fixed size for each [`TokenBlock`].
153    /// * `salt_hash` - An optional [`SaltHash`] used as the base seed for hashing. Defaults to 0.
154    pub fn into_sequence(self, block_size: u32, salt_hash: Option<SaltHash>) -> TokenBlockSequence {
155        TokenBlockSequence::new(self, block_size, salt_hash)
156    }
157}
158
159/// Errors that can occur during [`PartialTokenBlock`] operations.
160#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
161pub enum TokenBlockError {
162    /// The operation could not be completed because the block is full.
163    #[error("TokenBlock is full")]
164    Full,
165
166    /// The operation requires a full block, but the block is incomplete.
167    #[error("TokenBlock is incomplete")]
168    Incomplete,
169
170    /// The operation could not be completed because the block is empty.
171    #[error("TokenBlock is empty")]
172    Empty,
173
174    /// The operation requires more tokens than are currently in the block.
175    #[error("TokenBlock has insufficient tokens")]
176    InsufficientTokens,
177}
178
179/// Represents a partially filled block of tokens within a sequence.
180///
181/// This structure accumulates tokens until it reaches the specified `block_size`,
182/// at which point it can be [`commit`](PartialTokenBlock::commit)ted into a full [`TokenBlock`].
183#[derive(Debug, PartialEq)] // No Clone: intended to be unique within a sequence
184pub struct PartialTokenBlock {
185    tokens: Tokens,
186    block_size: u32,
187    salt_hash: SaltHash,
188    parent_sequence_hash: Option<SequenceHash>,
189}
190
191impl PartialTokenBlock {
192    /// Creates the first partial block (root) for a new sequence.
193    ///
194    /// # Arguments
195    ///
196    /// * `block_size` - The fixed size for blocks in this sequence.
197    /// * `salt_hash` - The [`SaltHash`] for the sequence.
198    pub(crate) fn create_sequence_root(block_size: u32, salt_hash: SaltHash) -> Self {
199        Self {
200            tokens: Tokens::default(),
201            block_size,
202            salt_hash,
203            parent_sequence_hash: None, // Root has no parent
204        }
205    }
206
207    /// Attempts to push a single token onto the block.
208    ///
209    /// # Arguments
210    ///
211    /// * `token` - The [`Token`] to push.
212    ///
213    /// # Returns
214    ///
215    /// * `Ok(())` - If the token was successfully added.
216    /// * `Err(TokenBlockError::Full)` - If the block already contains `block_size` tokens.
217    pub(crate) fn push_token(&mut self, token: Token) -> Result<(), TokenBlockError> {
218        if self.tokens.0.len() >= self.block_size as usize {
219            return Err(TokenBlockError::Full);
220        }
221        self.tokens.0.push(token);
222        Ok(())
223    }
224
225    /// Attempts to push multiple tokens onto the block from a [`Tokens`] object.
226    ///
227    /// Tokens are added until the block is full or all input tokens are consumed.
228    ///
229    /// # Arguments
230    ///
231    /// * `tokens` - The [`Tokens`] to push.
232    ///
233    /// # Returns
234    ///
235    /// A new [`Tokens`] object containing any tokens that did not fit,
236    /// if all tokens were added, the returned object will be empty.
237    pub(crate) fn push_tokens(&mut self, tokens: Tokens) -> Tokens {
238        let remaining_space = self.remaining();
239
240        if remaining_space == 0 {
241            return tokens; // Block is already full
242        }
243
244        if tokens.0.len() <= remaining_space {
245            // All tokens fit
246            self.tokens.0.extend(tokens.0);
247            Tokens::default() // No remaining tokens
248        } else {
249            // Only some tokens fit
250            let (to_add, remaining) = tokens.0.split_at(remaining_space);
251            self.tokens.0.extend_from_slice(to_add);
252            Tokens(remaining.to_vec()) // Return the leftover tokens
253        }
254    }
255
256    /// Attempts to remove the last token from the block.
257    ///
258    /// # Returns
259    ///
260    /// * `Ok(())` - If a token was successfully removed.
261    /// * `Err(TokenBlockError::Empty)` - If the block was already empty.
262    pub(crate) fn pop_token(&mut self) -> Result<(), TokenBlockError> {
263        if self.tokens.0.is_empty() {
264            return Err(TokenBlockError::Empty);
265        }
266        self.tokens.0.pop();
267        Ok(())
268    }
269
270    /// Attempts to remove the last `count` tokens from the block.
271    ///
272    /// # Arguments
273    ///
274    /// * `count` - The number of tokens to remove.
275    ///
276    /// # Returns
277    ///
278    /// * `Ok(())` - If the specified number of tokens were successfully removed.
279    /// * `Err(TokenBlockError::InsufficientTokens)` - If `count` is greater than the number of tokens in the block.
280    pub(crate) fn pop_tokens(&mut self, count: usize) -> Result<(), TokenBlockError> {
281        if self.tokens.0.len() < count {
282            return Err(TokenBlockError::InsufficientTokens);
283        }
284        self.tokens.0.truncate(self.tokens.0.len() - count);
285        Ok(())
286    }
287
288    /// Attempts to commit the current partial block into a full [`TokenBlock`].
289    ///
290    /// This operation consumes the tokens within the partial block.
291    /// After a successful commit, this `PartialTokenBlock` instance is reset
292    /// to represent the *next* partial block in the sequence, inheriting the
293    /// sequence hash from the block just committed.
294    ///
295    /// # Returns
296    ///
297    /// * `Ok(TokenBlock)` - The newly created full [`TokenBlock`].
298    /// * `Err(TokenBlockError::Incomplete)` - If the block does not contain exactly `block_size` tokens.
299    pub(crate) fn commit(&mut self) -> Result<TokenBlock, TokenBlockError> {
300        if self.tokens.0.len() != self.block_size as usize {
301            // Check for exact size match for committing
302            return Err(TokenBlockError::Incomplete);
303        }
304
305        // Take ownership of the tokens, leaving the internal tokens empty
306        let tokens = std::mem::take(&mut self.tokens);
307
308        let chunk = TokenBlockChunk::new(tokens, self.salt_hash);
309        let block = TokenBlock::from_chunk(chunk, self.parent_sequence_hash);
310
311        // Reset self to be the next block in the sequence
312        self.parent_sequence_hash = Some(block.sequence_hash());
313        // self.tokens is already empty due to mem::take
314        // self.block_size and self.salt_hash remain the same
315
316        Ok(block)
317    }
318
319    /// Returns the number of additional tokens required to fill the block.
320    pub fn remaining(&self) -> usize {
321        // Use saturating_sub to prevent underflow if len somehow exceeds block_size
322        (self.block_size as usize).saturating_sub(self.tokens.0.len())
323    }
324
325    /// Returns the number of tokens currently in the block.
326    pub fn len(&self) -> usize {
327        self.tokens.0.len()
328    }
329
330    /// Returns `true` if the block contains no tokens.
331    pub fn is_empty(&self) -> bool {
332        self.tokens.0.is_empty()
333    }
334
335    /// Returns a reference to the tokens currently in the block.
336    pub fn tokens(&self) -> &Tokens {
337        &self.tokens
338    }
339}
340
341// Deref allows treating &PartialTokenBlock like &Tokens for read-only access.
342impl std::ops::Deref for PartialTokenBlock {
343    type Target = Tokens;
344
345    fn deref(&self) -> &Self::Target {
346        &self.tokens
347    }
348}
349
350/// An intermediate structure holding a chunk of tokens destined to become a [`TokenBlock`].
351///
352/// This calculates the [`BlockHash`] but does not compute the final [`SequenceHash`],
353/// allowing chunks to be processed independently (e.g., in parallel).
354#[derive(Debug)] // No Clone: temporary intermediate value
355struct TokenBlockChunk {
356    tokens: Tokens,
357    salt_hash: SaltHash,
358    block_hash: BlockHash,
359}
360
361impl TokenBlockChunk {
362    /// Creates a new chunk from [`Tokens`], calculating the [`BlockHash`].
363    fn new(tokens: Tokens, salt_hash: SaltHash) -> Self {
364        let block_hash = compute_hash_v2(cast_slice(&tokens), salt_hash);
365        Self {
366            tokens,
367            salt_hash,
368            block_hash,
369        }
370    }
371
372    /// Creates a new chunk from a slice of `&[Token]`, calculating the [`BlockHash`].
373    fn from_tokens(tokens: &[Token], salt_hash: SaltHash) -> Self {
374        let block_hash = compute_hash_v2(cast_slice(tokens), salt_hash);
375        Self {
376            tokens: tokens.into(), // Converts slice to owned Tokens
377            salt_hash,
378            block_hash,
379        }
380    }
381}
382
383/// Represents a completed, immutable block of tokens with associated hashes.
384///
385/// Contains exactly `block_size` tokens and includes the [`SaltHash`], [`BlockHash`],
386/// [`SequenceHash`], and optionally the parent's [`SequenceHash`].
387#[derive(Debug, Clone, Default, PartialEq)] // Add PartialEq for tests
388pub struct TokenBlock {
389    tokens: Tokens,
390    salt_hash: SaltHash,
391    block_hash: BlockHash,
392    sequence_hash: SequenceHash,
393    parent_sequence_hash: Option<SequenceHash>,
394}
395
396impl TokenBlock {
397    /// Creates a new [`PartialTokenBlock`] representing the block immediately following this one.
398    ///
399    /// The new partial block will have the correct `parent_sequence_hash` set.
400    pub fn next_block(&self) -> PartialTokenBlock {
401        PartialTokenBlock {
402            tokens: Tokens::default(),
403            block_size: self.tokens.len() as u32, // Should be == self.block_size
404            salt_hash: self.salt_hash,
405            parent_sequence_hash: Some(self.sequence_hash), // Link to this block
406        }
407    }
408
409    /// Finalizes a [`TokenBlock`] from a [`TokenBlockChunk`] and the parent's sequence hash.
410    ///
411    /// This computes the final [`SequenceHash`] for the block.
412    fn from_chunk(chunk: TokenBlockChunk, parent_sequence_hash: Option<SequenceHash>) -> Self {
413        let sequence_hash = match parent_sequence_hash {
414            Some(parent) => {
415                // Combine parent sequence hash and current block hash
416                compute_hash_v2(cast_slice(&[parent, chunk.block_hash]), chunk.salt_hash)
417            }
418            None => {
419                // First block: sequence hash is just the block hash
420                chunk.block_hash
421            }
422        };
423
424        Self {
425            tokens: chunk.tokens,
426            salt_hash: chunk.salt_hash,
427            block_hash: chunk.block_hash,
428            sequence_hash,
429            parent_sequence_hash,
430        }
431    }
432
433    /// Returns a reference to the tokens in this block.
434    pub fn tokens(&self) -> &Tokens {
435        &self.tokens
436    }
437
438    /// Returns the salt hash used for this block's hashing.
439    pub fn salt_hash(&self) -> SaltHash {
440        self.salt_hash
441    }
442
443    /// Returns the hash of only the tokens within this block.
444    pub fn block_hash(&self) -> BlockHash {
445        self.block_hash
446    }
447
448    /// Returns the sequence-aware hash for this block.
449    pub fn sequence_hash(&self) -> SequenceHash {
450        self.sequence_hash
451    }
452
453    /// Returns the sequence hash of the preceding block, if any.
454    pub fn parent_sequence_hash(&self) -> Option<SequenceHash> {
455        self.parent_sequence_hash
456    }
457
458    /// Returns the number of tokens in the block.
459    pub fn block_size(&self) -> usize {
460        self.tokens.0.len()
461    }
462}
463
464/// Represents a sequence of tokens, segmented into fixed-size, hashed blocks.
465///
466/// This structure manages a series of completed [`TokenBlock`]s and one
467/// [`PartialTokenBlock`] for accumulating incoming tokens.
468/// It provides methods for appending tokens (`append`, `extend`), removing tokens
469/// (`pop`, `truncate`, `unwind`), and accessing sequence information.
470///
471/// Hashing incorporates an initial [`SaltHash`] to ensure uniqueness across different
472/// contexts (e.g., different models, PEFTs).
473///
474/// Key Hashes:
475/// - [`BlockHash`]: Hash of tokens within a single block (seeded by [`SaltHash`]).
476/// - [`SequenceHash`]: Hash combining the previous block's [`SequenceHash`] and the current
477///   block's [`BlockHash`] (also seeded by [`SaltHash`]).
478#[derive(Debug, PartialEq)]
479pub struct TokenBlockSequence {
480    blocks: Vec<TokenBlock>,
481    current_block: PartialTokenBlock,
482    salt_hash: SaltHash,
483    block_size: usize,
484}
485
486impl TokenBlockSequence {
487    /// Creates a new [`TokenBlockSequence`] from an initial set of tokens.
488    ///
489    /// The tokens are split into blocks of `block_size`. Any remaining tokens
490    /// form the initial `current_block`.
491    ///
492    /// # Arguments
493    ///
494    /// * `tokens` - The initial [`Tokens`] for the sequence.
495    /// * `block_size` - The fixed size for each [`TokenBlock`]. Must be greater than 0.
496    /// * `salt_hash` - An optional [`SaltHash`]. Defaults to 0 if `None`.
497    ///
498    /// # Panics
499    ///
500    /// Panics if `block_size` is 0.
501    pub fn new(tokens: Tokens, block_size: u32, salt_hash: Option<SaltHash>) -> Self {
502        assert!(block_size > 0, "block_size must be greater than 0");
503        let salt_hash = salt_hash.unwrap_or(0);
504        let (blocks, current_block) = Self::split_tokens(&tokens, block_size, salt_hash);
505
506        Self {
507            blocks,
508            current_block,
509            salt_hash,
510            block_size: block_size as usize,
511        }
512    }
513
514    /// Extends the sequence with the given tokens, potentially completing multiple blocks.
515    ///
516    /// This method processes all tokens from the input [`Tokens`] object.
517    /// If adding tokens causes one or more blocks to become full, they are committed
518    /// and added to the internal list of completed blocks.
519    ///
520    /// # Arguments
521    ///
522    /// * `tokens` - The [`Tokens`] object containing the tokens to extend the sequence with.
523    ///
524    /// # Returns
525    ///
526    /// * `Ok(Some(Range<usize>))` - The range of indices in the `blocks` vector corresponding
527    ///   to the blocks completed during this `extend` operation.
528    /// * `Ok(None)` - If no blocks were completed.
529    /// * `Err(TokenBlockError)` - If an internal error occurs during commit.
530    pub fn extend(&mut self, tokens: Tokens) -> Result<Option<Range<usize>>, TokenBlockError> {
531        let start_block_index = self.blocks.len();
532        let mut tokens_to_append = tokens;
533
534        while !tokens_to_append.is_empty() {
535            let remaining_in_current = self.current_block.remaining();
536
537            if remaining_in_current == 0 {
538                // Current block is full, commit it first
539                let new_block = self.current_block.commit()?;
540                self.blocks.push(new_block);
541                // Continue loop to add tokens to the *new* current_block
542            }
543
544            // Push as many tokens as possible into the current (potentially new) block
545            let available_tokens = tokens_to_append;
546            tokens_to_append = self.current_block.push_tokens(available_tokens);
547
548            // Check if the current block *became* full after pushing tokens
549            if self.current_block.remaining() == 0 {
550                // If it became full AND there are still more tokens to append,
551                // commit it now so the next loop iteration starts with a fresh block.
552                let new_block = self.current_block.commit()?;
553                self.blocks.push(new_block);
554            }
555        }
556
557        let end_block_index = self.blocks.len();
558        if start_block_index == end_block_index {
559            Ok(None) // No blocks were completed
560        } else {
561            Ok(Some(start_block_index..end_block_index))
562        }
563    }
564
565    /// Appends a single token to the sequence.
566    ///
567    /// If adding this token completes the current partial block, the block is committed,
568    /// and the index of the newly completed block is returned.
569    ///
570    /// This method is equivalent to calling [`extend`] with a single-token [`Tokens`] object.
571    ///
572    /// # Arguments
573    ///
574    /// * `token` - The [`Token`] to append.
575    ///
576    /// # Returns
577    ///
578    /// * `Ok(Some(usize))` - The index of the block that was just completed.
579    /// * `Ok(None)` - No block was completed by adding this token.
580    /// * `Err(TokenBlockError)` - If an internal error occurs during processing.
581    pub fn append(&mut self, token: Token) -> Result<Option<usize>, TokenBlockError> {
582        // Create a single-token Tokens object
583        let tokens = Tokens::from(vec![token]);
584
585        // Call extend
586        let range_option = self.extend(tokens)?;
587
588        // Convert the range to Option<usize>
589        match range_option {
590            None => Ok(None),
591            Some(range) => {
592                // Since we only added one token, the range can only be empty or have one element.
593                // If it's not empty, it must be `n..(n+1)`.
594                assert_eq!(
595                    range.len(),
596                    1,
597                    "Appending a single token completed more than one block, which should be impossible."
598                );
599                Ok(Some(range.start))
600            }
601        }
602    }
603
604    /// Shortens the sequence, keeping the first `len` tokens and removing the rest.
605    ///
606    /// If `len` is greater than the sequence's current length, this has no effect.
607    ///
608    /// This operation is analogous to `Vec::truncate`.
609    /// It may involve removing tokens from the current partial block, removing entire
610    /// completed blocks, and adjusting the current partial block
611    /// to reflect the new end of the sequence.
612    ///
613    /// # Arguments
614    ///
615    /// * `len` - The number of tokens to keep.
616    ///
617    /// # Returns
618    ///
619    /// * `Ok(())` - If the sequence was successfully truncated.
620    /// * `Err(TokenBlockError::InsufficientTokens)` - This error should ideally not occur if `len`
621    ///   is correctly checked against `total_tokens`, but the underlying `pop_tokens` might return it.
622    pub fn truncate(&mut self, len: usize) -> Result<(), TokenBlockError> {
623        let current_total_len = self.total_tokens();
624        if len >= current_total_len {
625            return Ok(()); // Nothing to truncate
626        }
627
628        let n = current_total_len - len; // Number of tokens to remove
629
630        // This inner block handles the actual removal logic based on `n` tokens to remove.
631        {
632            let current_len = self.current_block.len();
633            // Avoid division by zero if block_size is somehow 0 (though asserted in new)
634            let block_size = self.current_block.block_size.max(1);
635
636            if n <= current_len {
637                // Only need to pop from the current partial block
638                self.current_block.pop_tokens(n)?;
639            } else {
640                // Need to pop from full blocks as well
641                let tokens_to_pop_from_blocks = n - current_len;
642
643                // Calculate how many blocks are affected (including the one partially popped)
644                let num_blocks_to_affect = tokens_to_pop_from_blocks.div_ceil(block_size as usize);
645
646                // Check if we need to pop more blocks than available (should be prevented by initial len check)
647                if num_blocks_to_affect > self.blocks.len() {
648                    // This indicates an inconsistency between total_tokens() and internal state.
649                    debug_assert!(
650                        false,
651                        "Truncate calculation error: trying to pop too many blocks."
652                    );
653                    return Err(TokenBlockError::InsufficientTokens);
654                }
655
656                // Determine the index of the block that will be the source for the new partial block
657                let source_block_index = self.blocks.len() - num_blocks_to_affect;
658
659                // Calculate how many tokens to keep from that source block
660                let num_full_blocks_completely_popped = num_blocks_to_affect - 1;
661                let num_tokens_to_pop_from_source_block = tokens_to_pop_from_blocks
662                    - num_full_blocks_completely_popped * block_size as usize;
663                let num_tokens_to_keep_in_new_partial =
664                    (block_size as usize).saturating_sub(num_tokens_to_pop_from_source_block);
665
666                // Get the tokens for the new partial block
667                let new_partial_tokens = if num_tokens_to_keep_in_new_partial > 0 {
668                    self.blocks[source_block_index].tokens().as_ref()
669                        [..num_tokens_to_keep_in_new_partial]
670                        .to_vec()
671                } else {
672                    Vec::new()
673                };
674
675                // Truncate the blocks vector to remove popped blocks
676                self.blocks.truncate(source_block_index);
677
678                // Update the current_block state
679                self.current_block.tokens = Tokens(new_partial_tokens);
680                // Correctly set the parent hash based on the *new* last block
681                self.current_block.parent_sequence_hash =
682                    self.blocks.last().map(|b| b.sequence_hash());
683                // salt_hash and block_size remain the same for current_block
684            }
685        }
686        Ok(())
687    }
688
689    /// Removes the last `count` tokens from the sequence.
690    ///
691    /// This is a convenience method that calculates the required length and calls [`truncate`].
692    ///
693    /// # Arguments
694    ///
695    /// * `count` - The number of tokens to remove from the end.
696    ///
697    /// # Returns
698    ///
699    /// * `Ok(())` - If the tokens were successfully removed.
700    /// * `Err(TokenBlockError::InsufficientTokens)` - If `count` is greater than or equal to
701    ///   the total number of tokens in the sequence.
702    pub fn unwind(&mut self, count: usize) -> Result<(), TokenBlockError> {
703        let current_total_len = self.total_tokens();
704        if count > current_total_len {
705            // Allow count == current_total_len, which truncates to 0.
706            return Err(TokenBlockError::InsufficientTokens);
707        }
708
709        // number of tokens remaining in the sequence after undoing the given count
710        let len = current_total_len - count;
711        self.truncate(len)
712    }
713
714    /// Resets the sequence to the initial state.
715    pub fn reset(&mut self) {
716        self.blocks.clear();
717        self.current_block =
718            PartialTokenBlock::create_sequence_root(self.block_size as u32, self.salt_hash);
719    }
720
721    /// Removes the last token from the sequence and returns it, or [`None`] if it is empty.
722    ///
723    /// This operation is analogous to `Vec::pop`.
724    ///
725    /// # Returns
726    ///
727    /// * `Some(Token)` - The last token, if the sequence was not empty.
728    /// * `None` - If the sequence was empty.
729    pub fn pop(&mut self) -> Option<Token> {
730        let current_total_len = self.total_tokens();
731        if current_total_len == 0 {
732            return None;
733        }
734
735        // Determine the last token. It must be in the current_block if current_block is not empty.
736        // If current_block is empty, it must be the last token of the last full block.
737        let last_token = if !self.current_block.tokens.is_empty() {
738            // Last token is in the partial block
739            *self
740                .current_block
741                .tokens
742                .last()
743                .expect("Current block checked for non-empty")
744        } else {
745            // Current block is empty, sequence is not. Must be in the last full block.
746            let last_block = self
747                .blocks
748                .last()
749                .expect("Sequence is not empty but has no blocks and empty current block?");
750            *last_block
751                .tokens()
752                .last()
753                .expect("Last block cannot be empty")
754        };
755
756        // Truncate the sequence by one element.
757        // We expect this to succeed since we know the length > 0.
758        match self.truncate(current_total_len - 1) {
759            Ok(_) => Some(last_token),
760            Err(_) => {
761                // This should be logically impossible if total_tokens() and truncate() are correct.
762                // Panic in debug, return None in release as a fallback, though it indicates a bug.
763                debug_assert!(
764                    false,
765                    "truncate failed unexpectedly after checking length in pop"
766                );
767                None
768            }
769        }
770    }
771
772    /// Returns a slice containing all the completed [`TokenBlock`]s in the sequence.
773    pub fn blocks(&self) -> &[TokenBlock] {
774        &self.blocks
775    }
776
777    /// Returns a reference to the last completed [`TokenBlock`] in the sequence, if any.
778    pub fn last_complete_block(&self) -> Option<&TokenBlock> {
779        self.blocks.last()
780    }
781
782    /// Returns a reference to the current [`PartialTokenBlock`] where new tokens are added.
783    pub fn current_block(&self) -> &PartialTokenBlock {
784        &self.current_block
785    }
786
787    /// Consumes the sequence and returns its parts: a `Vec` of completed blocks and the final partial block.
788    pub fn into_parts(self) -> (Vec<TokenBlock>, PartialTokenBlock) {
789        (self.blocks, self.current_block)
790    }
791
792    /// Returns the block size used for this sequence.
793    pub fn block_size(&self) -> usize {
794        self.block_size
795    }
796
797    /// Returns the [`SaltHash`] used for this sequence.
798    pub fn salt_hash(&self) -> SaltHash {
799        self.salt_hash
800    }
801
802    /// Returns the total number of tokens in the sequence (sum of tokens in all completed blocks
803    /// plus tokens in the current partial block).
804    pub fn total_tokens(&self) -> usize {
805        let block_size = self.current_block.block_size as usize;
806        (self.blocks.len() * block_size) + self.current_block.len()
807    }
808
809    /// Extract the token with the range
810    pub fn tokens_at(&self, range: Range<usize>) -> Tokens {
811        let total = self.total_tokens();
812
813        // Validate range - return empty tokens for invalid ranges
814        if range.start > range.end || range.end > total {
815            return Tokens::default();
816        }
817
818        // Handle empty range
819        if range.is_empty() {
820            return Tokens::default();
821        }
822
823        let mut result = Vec::with_capacity(range.len());
824
825        for i in range {
826            if i < self.blocks.len() * self.block_size {
827                // Token is in a completed block
828                let block_index = i / self.block_size;
829                let token_index = i % self.block_size;
830                result.push(self.blocks[block_index].tokens()[token_index]);
831            } else {
832                // Token is in the current partial block
833                let current_block_index = i - (self.blocks.len() * self.block_size);
834                result.push(self.current_block.tokens()[current_block_index]);
835            }
836        }
837
838        Tokens::from(result)
839    }
840
841    /// Splits a [`Tokens`] object into a vector of completed blocks and a final partial block.
842    ///
843    /// This is primarily used internally by [`TokenBlockSequence::new`] but can be used externally.
844    ///
845    /// # Arguments
846    ///
847    /// * `tokens` - The [`Tokens`] to split.
848    /// * `block_size` - The size of each block.
849    /// * `salt_hash` - The [`SaltHash`] to use for hashing.
850    ///
851    /// # Returns
852    ///
853    /// A tuple containing `(Vec<TokenBlock>, PartialTokenBlock)`.
854    ///
855    /// # Panics
856    ///
857    /// Panics if `block_size` is 0.
858    pub fn split_tokens(
859        tokens: &[Token],
860        block_size: u32,
861        salt_hash: u64,
862    ) -> (Vec<TokenBlock>, PartialTokenBlock) {
863        assert!(block_size > 0, "block_size must be greater than 0");
864        // Use Rayon for parallel computation of block chunks (hashes)
865        let chunks: Vec<TokenBlockChunk> = tokens
866            .as_ref()
867            .par_chunks_exact(block_size as usize)
868            .map(|chunk| TokenBlockChunk::from_tokens(chunk, salt_hash))
869            .collect();
870
871        let mut result_blocks = Vec::with_capacity(chunks.len());
872        let mut last_sequence_hash: Option<SequenceHash> = None;
873
874        // Sequentially combine chunks to compute sequence hashes
875        for chunk in chunks {
876            let new_block = TokenBlock::from_chunk(chunk, last_sequence_hash);
877            last_sequence_hash = Some(new_block.sequence_hash());
878            result_blocks.push(new_block);
879        }
880
881        // Handle any remaining tokens
882        let remainder = tokens
883            .as_ref()
884            .chunks_exact(block_size as usize)
885            .remainder();
886
887        let current_block = PartialTokenBlock {
888            tokens: remainder.into(),
889            block_size,
890            salt_hash,
891            // Parent hash is the sequence hash of the last *full* block computed
892            parent_sequence_hash: last_sequence_hash,
893        };
894
895        (result_blocks, current_block)
896    }
897
898    pub fn from_slice(tokens: &[Token], block_size: u32, salt_hash: Option<SaltHash>) -> Self {
899        assert!(block_size > 0, "block_size must be greater than 0");
900        let salt_hash = salt_hash.unwrap_or(0);
901        let (blocks, current_block) = Self::split_tokens(tokens, block_size, salt_hash);
902
903        Self {
904            blocks,
905            current_block,
906            salt_hash,
907            block_size: block_size as usize,
908        }
909    }
910}
911
912#[cfg(test)]
913mod tests {
914    use super::*;
915    use bytemuck::cast_slice;
916
917    // Helper to create a sequence for testing
918    fn create_test_sequence(
919        initial_tokens: &[Token],
920        block_size: u32,
921        salt_hash: Option<SaltHash>,
922    ) -> TokenBlockSequence {
923        TokenBlockSequence::new(Tokens::from(initial_tokens), block_size, salt_hash)
924    }
925
926    // Helper to get expected hashes (replace with actual calculated values if needed)
927    const TEST_SALT_HASH: SaltHash = 1337;
928    const HASH_1_4: BlockHash = 14643705804678351452; // hash([1,2,3,4], 1337)
929    const SEQ_HASH_1_4: SequenceHash = HASH_1_4;
930    const HASH_5_8: BlockHash = 16777012769546811212; // hash([5,6,7,8], 1337)
931    const SEQ_HASH_5_8: SequenceHash = 4945711292740353085; // hash([SEQ_HASH_1_4, HASH_5_8], 1337)
932    const HASH_9_12: BlockHash = 483935686894639516; // hash([9,10,11,12], 1337)
933    const SEQ_HASH_9_12: SequenceHash = 12583592247330656132; // hash([SEQ_HASH_5_8, HASH_9_12], 1337)
934
935    #[test]
936    fn test_validate_hash_constants() {
937        let salt = TEST_SALT_HASH;
938
939        // Block 1: [1, 2, 3, 4]
940        let tokens_1_4 = &[1u32, 2, 3, 4];
941        let computed_hash_1_4 = compute_hash_v2(cast_slice(tokens_1_4), salt);
942        assert_eq!(computed_hash_1_4, HASH_1_4, "Mismatch for HASH_1_4");
943        // First block's sequence hash is its block hash
944        assert_eq!(computed_hash_1_4, SEQ_HASH_1_4, "Mismatch for SEQ_HASH_1_4");
945
946        // Block 2: [5, 6, 7, 8]
947        let tokens_5_8 = &[5u32, 6, 7, 8];
948        let computed_hash_5_8 = compute_hash_v2(cast_slice(tokens_5_8), salt);
949        assert_eq!(computed_hash_5_8, HASH_5_8, "Mismatch for HASH_5_8");
950        let computed_seq_hash_5_8 = compute_hash_v2(cast_slice(&[SEQ_HASH_1_4, HASH_5_8]), salt);
951        assert_eq!(
952            computed_seq_hash_5_8, SEQ_HASH_5_8,
953            "Mismatch for SEQ_HASH_5_8"
954        );
955
956        // Block 3: [9, 10, 11, 12]
957        let tokens_9_12 = &[9u32, 10, 11, 12];
958        let computed_hash_9_12 = compute_hash_v2(cast_slice(tokens_9_12), salt);
959        assert_eq!(computed_hash_9_12, HASH_9_12, "Mismatch for HASH_9_12");
960        let computed_seq_hash_9_12 = compute_hash_v2(cast_slice(&[SEQ_HASH_5_8, HASH_9_12]), salt);
961        assert_eq!(
962            computed_seq_hash_9_12, SEQ_HASH_9_12,
963            "Mismatch for SEQ_HASH_9_12"
964        );
965    }
966
967    #[test]
968    fn test_tokens_from() {
969        let vec_u32: Vec<u32> = vec![1, 2, 3];
970        let tokens_u32: Tokens = vec_u32.clone().into();
971        assert_eq!(tokens_u32.0, vec_u32);
972
973        let slice_u32: &[u32] = &[4, 5];
974        let tokens_slice_u32: Tokens = slice_u32.into();
975        assert_eq!(tokens_slice_u32.0, vec![4, 5]);
976
977        let vec_i32: Vec<i32> = vec![-1, 0, 1]; // Note: -1 becomes large u32
978        let tokens_i32: Tokens = vec_i32.into();
979        assert_eq!(tokens_i32.0, vec![u32::MAX, 0, 1]);
980
981        let slice_i32: &[i32] = &[100, 200];
982        let tokens_slice_i32: Tokens = slice_i32.into();
983        assert_eq!(tokens_slice_i32.0, vec![100, 200]);
984
985        let into_vec: Vec<u32> = tokens_slice_i32.into();
986        assert_eq!(into_vec, vec![100, 200]);
987    }
988
989    #[test]
990    fn test_tokens_equality() {
991        let tokens = Tokens::from(vec![1, 2, 3]);
992        assert_eq!(tokens, vec![1, 2, 3]);
993        assert_eq!(vec![1, 2, 3], tokens);
994        assert_eq!(tokens, &[1, 2, 3][..]);
995        assert_eq!(&[1, 2, 3][..], tokens);
996        assert_eq!(tokens, Tokens::from(vec![1, 2, 3]));
997        assert_ne!(tokens, Tokens::from(vec![1, 2, 4]));
998    }
999
1000    #[test]
1001    fn test_tokens_deref_asref() {
1002        let tokens = Tokens::from(vec![10, 20, 30]);
1003
1004        // Deref to &[Token]
1005        assert_eq!(tokens.len(), 3);
1006        assert_eq!(tokens[1], 20);
1007        let slice: &[Token] = &tokens;
1008        assert_eq!(slice, &[10, 20, 30]);
1009
1010        // AsRef<[Token]>
1011        let as_ref_slice: &[Token] = tokens.as_ref();
1012        assert_eq!(as_ref_slice, &[10, 20, 30]);
1013
1014        // Borrow<[Token]>
1015        let borrowed_slice: &[Token] = std::borrow::Borrow::borrow(&tokens);
1016        assert_eq!(borrowed_slice, &[10, 20, 30]);
1017    }
1018
1019    #[test]
1020    fn test_tokens_into_sequence() {
1021        let tokens = Tokens::from(vec![1, 2, 3, 4, 5]);
1022        let seq = tokens.into_sequence(3, Some(TEST_SALT_HASH));
1023        assert_eq!(seq.blocks().len(), 1);
1024        assert_eq!(seq.blocks[0].tokens().as_ref(), &[1, 2, 3]);
1025        assert_eq!(seq.current_block().tokens().as_ref(), &[4, 5]);
1026        assert_eq!(seq.salt_hash(), TEST_SALT_HASH);
1027    }
1028
1029    #[test]
1030    fn test_partial_block_ops() {
1031        let mut partial = PartialTokenBlock::create_sequence_root(3, TEST_SALT_HASH);
1032        assert_eq!(partial.len(), 0);
1033        assert_eq!(partial.remaining(), 3);
1034        assert!(partial.is_empty());
1035
1036        // Push tokens
1037        assert!(partial.push_token(1).is_ok());
1038        assert_eq!(partial.len(), 1);
1039        assert_eq!(partial.remaining(), 2);
1040        let remaining = partial.push_tokens(Tokens::from(vec![2, 3, 4]));
1041        assert_eq!(partial.len(), 3);
1042        assert_eq!(partial.remaining(), 0);
1043        assert_eq!(remaining.as_ref(), &[4]); // Token 4 didn't fit
1044        assert_eq!(partial.tokens().as_ref(), &[1, 2, 3]);
1045
1046        // Push when full
1047        assert_eq!(partial.push_token(5), Err(TokenBlockError::Full));
1048        let remaining_full = partial.push_tokens(Tokens::from(vec![5]));
1049        assert_eq!(remaining_full.as_ref(), &[5]);
1050
1051        // Pop tokens
1052        assert!(partial.pop_token().is_ok());
1053        assert_eq!(partial.len(), 2);
1054        assert_eq!(partial.tokens().as_ref(), &[1, 2]);
1055        assert!(partial.pop_tokens(2).is_ok());
1056        assert!(partial.is_empty());
1057
1058        // Pop when empty
1059        assert_eq!(partial.pop_token(), Err(TokenBlockError::Empty));
1060        assert_eq!(
1061            partial.pop_tokens(1),
1062            Err(TokenBlockError::InsufficientTokens)
1063        );
1064
1065        // Commit incomplete
1066        assert!(partial.push_token(10).is_ok());
1067        assert_eq!(partial.commit(), Err(TokenBlockError::Incomplete));
1068
1069        // Commit complete
1070        assert!(partial.push_token(11).is_ok());
1071        assert!(partial.push_token(12).is_ok());
1072        assert_eq!(partial.len(), 3);
1073        let commit_result = partial.commit();
1074        assert!(commit_result.is_ok());
1075        let committed_block = commit_result.unwrap();
1076        assert_eq!(committed_block.tokens().as_ref(), &[10, 11, 12]);
1077
1078        // Check state after commit (partial block is now the next one)
1079        assert!(partial.is_empty());
1080        assert_eq!(
1081            partial.parent_sequence_hash,
1082            Some(committed_block.sequence_hash())
1083        );
1084        assert_eq!(partial.block_size, 3);
1085    }
1086
1087    #[test]
1088    fn test_token_block_creation_and_hashes() {
1089        let salt = TEST_SALT_HASH;
1090        let tokens1 = Tokens::from(vec![1, 2, 3, 4]);
1091        let chunk1 = TokenBlockChunk::new(tokens1.clone(), salt);
1092        let block1 = TokenBlock::from_chunk(chunk1, None);
1093
1094        assert_eq!(block1.tokens(), &tokens1);
1095        assert_eq!(block1.salt_hash(), salt);
1096        assert_eq!(block1.parent_sequence_hash(), None);
1097        assert_eq!(block1.block_hash(), HASH_1_4);
1098        assert_eq!(block1.sequence_hash(), SEQ_HASH_1_4); // First block seq_hash == block_hash
1099
1100        let tokens2 = Tokens::from(vec![5, 6, 7, 8]);
1101        let chunk2 = TokenBlockChunk::new(tokens2.clone(), salt);
1102        let block2 = TokenBlock::from_chunk(chunk2, block1.parent_sequence_hash()); // Incorrect parent
1103        // Sequence hash should differ if parent is wrong
1104        assert_ne!(block2.sequence_hash(), SEQ_HASH_5_8);
1105
1106        let chunk2_correct = TokenBlockChunk::new(tokens2.clone(), salt);
1107        let block2_correct = TokenBlock::from_chunk(chunk2_correct, Some(block1.sequence_hash()));
1108
1109        assert_eq!(block2_correct.tokens(), &tokens2);
1110        assert_eq!(block2_correct.salt_hash(), salt);
1111        assert_eq!(
1112            block2_correct.parent_sequence_hash(),
1113            Some(block1.sequence_hash())
1114        );
1115        assert_eq!(block2_correct.block_hash(), HASH_5_8);
1116        assert_eq!(block2_correct.sequence_hash(), SEQ_HASH_5_8);
1117    }
1118
1119    #[test]
1120    fn test_new_sequence() {
1121        // Empty initial tokens
1122        let seq_empty = create_test_sequence(&[], 4, Some(TEST_SALT_HASH));
1123        assert!(seq_empty.blocks().is_empty());
1124        assert!(seq_empty.current_block().is_empty());
1125        assert_eq!(seq_empty.total_tokens(), 0);
1126        assert_eq!(seq_empty.salt_hash(), TEST_SALT_HASH);
1127        assert_eq!(seq_empty.current_block().parent_sequence_hash, None);
1128
1129        // Less than one block
1130        let seq_partial = create_test_sequence(&[1, 2], 4, Some(TEST_SALT_HASH));
1131        assert!(seq_partial.blocks().is_empty());
1132        assert_eq!(seq_partial.current_block().tokens().as_ref(), &[1, 2]);
1133        assert_eq!(seq_partial.total_tokens(), 2);
1134        assert_eq!(seq_partial.current_block().parent_sequence_hash, None);
1135
1136        // Exactly one block
1137        let seq_one_block = create_test_sequence(&[1, 2, 3, 4], 4, Some(TEST_SALT_HASH));
1138        assert_eq!(seq_one_block.blocks().len(), 1);
1139        assert!(seq_one_block.current_block().is_empty());
1140        assert_eq!(seq_one_block.total_tokens(), 4);
1141        assert_eq!(seq_one_block.blocks[0].tokens().as_ref(), &[1, 2, 3, 4]);
1142        assert_eq!(seq_one_block.blocks[0].sequence_hash(), SEQ_HASH_1_4);
1143        assert_eq!(
1144            seq_one_block.current_block().parent_sequence_hash,
1145            Some(SEQ_HASH_1_4)
1146        );
1147
1148        // More than one block
1149        let seq_multi = create_test_sequence(&[1, 2, 3, 4, 5, 6, 7, 8, 9], 4, Some(TEST_SALT_HASH));
1150        assert_eq!(seq_multi.blocks().len(), 2);
1151        assert_eq!(seq_multi.current_block().tokens().as_ref(), &[9]);
1152        assert_eq!(seq_multi.total_tokens(), 9);
1153        assert_eq!(seq_multi.blocks[0].sequence_hash(), SEQ_HASH_1_4);
1154        assert_eq!(seq_multi.blocks[1].sequence_hash(), SEQ_HASH_5_8);
1155        assert_eq!(
1156            seq_multi.current_block().parent_sequence_hash,
1157            Some(SEQ_HASH_5_8)
1158        );
1159
1160        // Test tokens_at across blocks and partial block
1161        assert_eq!(seq_multi.tokens_at(0..4).as_ref(), &[1, 2, 3, 4]); // First complete block
1162        assert_eq!(seq_multi.tokens_at(4..8).as_ref(), &[5, 6, 7, 8]); // Second complete block
1163        assert_eq!(seq_multi.tokens_at(8..9).as_ref(), &[9]); // Current partial block
1164        assert_eq!(seq_multi.tokens_at(2..6).as_ref(), &[3, 4, 5, 6]); // Spanning blocks
1165        assert_eq!(seq_multi.tokens_at(6..9).as_ref(), &[7, 8, 9]); // Spanning to partial
1166        assert_eq!(seq_multi.tokens_at(5..5).as_ref(), &[0u32; 0]); // Empty range
1167        assert_eq!(seq_multi.tokens_at(10..15).as_ref(), &[0u32; 0]); // Out of bounds
1168
1169        // No salt hash
1170        let seq_no_salt = create_test_sequence(&[1, 2, 3, 4, 5], 4, None);
1171        assert_eq!(seq_no_salt.salt_hash(), 0);
1172        assert_eq!(seq_no_salt.blocks().len(), 1);
1173        assert_ne!(seq_no_salt.blocks[0].block_hash(), HASH_1_4); // Hash differs with salt 0
1174        assert_eq!(seq_no_salt.current_block().tokens().as_ref(), &[5]);
1175    }
1176
1177    #[test]
1178    #[should_panic]
1179    fn test_new_sequence_zero_block_size() {
1180        let _ = create_test_sequence(&[1], 0, None);
1181    }
1182
1183    #[test]
1184    fn test_append_single_token() {
1185        let mut sequence =
1186            create_test_sequence(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 4, Some(TEST_SALT_HASH));
1187        assert_eq!(sequence.blocks().len(), 2);
1188        assert_eq!(sequence.current_block().tokens.len(), 2);
1189        assert_eq!(sequence.current_block().tokens, vec![9, 10]);
1190        assert_eq!(
1191            sequence.current_block().parent_sequence_hash,
1192            Some(SEQ_HASH_5_8)
1193        );
1194
1195        // Append token 11 - should not complete a block
1196        let completed_idx = sequence.append(11).unwrap();
1197        assert_eq!(completed_idx, None);
1198        assert_eq!(sequence.blocks().len(), 2);
1199        assert_eq!(sequence.current_block().tokens.as_ref(), &[9, 10, 11]);
1200
1201        // Append token 12 - should complete block 2 (index 2)
1202        // This will also commit block 2
1203        let completed_idx = sequence.append(12).unwrap();
1204        assert_eq!(completed_idx, Some(2));
1205        assert_eq!(sequence.blocks().len(), 3);
1206        assert_eq!(sequence.current_block.tokens.as_ref(), &[0u32; 0]);
1207        assert_eq!(sequence.current_block.remaining(), 4);
1208        assert_eq!(
1209            sequence.current_block().parent_sequence_hash,
1210            Some(SEQ_HASH_9_12)
1211        ); // Still linked to block 1
1212
1213        // Append token 13 - should not complete a block
1214        let completed_idx_13 = sequence.append(13).unwrap();
1215        assert_eq!(completed_idx_13, None);
1216        assert_eq!(sequence.blocks().len(), 3);
1217        assert_eq!(sequence.blocks[2].tokens().as_ref(), &[9, 10, 11, 12]);
1218        assert_eq!(sequence.blocks[2].sequence_hash(), SEQ_HASH_9_12);
1219        assert_eq!(sequence.current_block.tokens.as_ref(), &[13]); // New current block has 13
1220        assert_eq!(sequence.current_block.remaining(), 3);
1221        assert_eq!(
1222            sequence.current_block.parent_sequence_hash,
1223            Some(SEQ_HASH_9_12)
1224        ); // Linked to new block 2
1225    }
1226
1227    #[test]
1228    fn test_extend() {
1229        let block_size = 4;
1230        let salt_hash = Some(TEST_SALT_HASH);
1231
1232        // Case 1: Extend less than block size
1233        let mut seq1 = create_test_sequence(&[], block_size, salt_hash);
1234        let tokens1 = Tokens::from(vec![1, 2]);
1235        let completed1 = seq1.extend(tokens1).unwrap();
1236        assert_eq!(completed1, None); // No blocks completed
1237        assert_eq!(seq1.blocks.len(), 0);
1238        assert_eq!(seq1.current_block.tokens.as_ref(), &[1, 2]);
1239        assert_eq!(seq1.current_block.remaining(), 2);
1240        assert_eq!(seq1.current_block.parent_sequence_hash, None); // Still the root block
1241
1242        // Case 2: Extend exactly block size
1243        let mut seq2 = create_test_sequence(&[], block_size, salt_hash);
1244        let tokens2 = Tokens::from(vec![1, 2, 3, 4]);
1245        let completed2 = seq2.extend(tokens2).unwrap();
1246        assert_eq!(completed2, Some(0..1));
1247        assert_eq!(seq2.blocks.len(), 1);
1248        assert_eq!(seq2.current_block.tokens.as_ref(), &[0u32; 0]); // Current block is empty
1249        assert_eq!(seq2.current_block.remaining(), 4);
1250        assert_eq!(seq2.current_block.parent_sequence_hash, Some(SEQ_HASH_1_4)); // Still the root block
1251
1252        // Case 3: Extend more than block size, less than two blocks
1253        let mut seq3 = create_test_sequence(&[], block_size, salt_hash);
1254        let tokens3 = Tokens::from(vec![1, 2, 3, 4, 5, 6]);
1255        let completed3 = seq3.extend(tokens3).unwrap();
1256        assert_eq!(completed3, Some(0..1)); // Block at index 0 completed
1257        assert_eq!(seq3.blocks.len(), 1);
1258        assert_eq!(seq3.current_block.tokens.as_ref(), &[5, 6]); // Partial block has remainder
1259        assert_eq!(seq3.blocks[0].tokens().as_ref(), &[1, 2, 3, 4]);
1260        assert_eq!(seq3.current_block.parent_sequence_hash, Some(SEQ_HASH_1_4));
1261        assert_eq!(seq3.current_block.remaining(), 2);
1262
1263        // Case 4: Extend exactly two blocks
1264        let mut seq4 = create_test_sequence(&[], block_size, salt_hash);
1265        let tokens4 = Tokens::from(vec![1, 2, 3, 4, 5, 6, 7, 8]);
1266        let completed4 = seq4.extend(tokens4).unwrap();
1267        assert_eq!(completed4, Some(0..2)); // Only block 0 is committed
1268        assert_eq!(seq4.blocks.len(), 2); // Only 1 block committed
1269        assert_eq!(seq4.current_block.tokens.as_ref(), &[0u32; 0]);
1270        assert_eq!(seq4.current_block.remaining(), 4);
1271        assert_eq!(seq4.blocks[0].tokens().as_ref(), &[1, 2, 3, 4]);
1272        assert_eq!(seq4.blocks[0].sequence_hash(), SEQ_HASH_1_4);
1273        assert_eq!(seq4.current_block.parent_sequence_hash, Some(SEQ_HASH_5_8)); // Parent is the first block
1274
1275        // Case 5: Extend multiple times, completing blocks across calls
1276        let mut seq5 = create_test_sequence(&[], block_size, salt_hash);
1277        let tokens5a = Tokens::from(vec![1, 2]);
1278        let completed5a = seq5.extend(tokens5a).unwrap();
1279        assert_eq!(completed5a, None);
1280        assert_eq!(seq5.blocks.len(), 0);
1281        assert_eq!(seq5.current_block.tokens.as_ref(), &[1, 2]);
1282
1283        let tokens5b = Tokens::from(vec![3, 4, 5]);
1284        let completed5b = seq5.extend(tokens5b).unwrap();
1285        assert_eq!(completed5b, Some(0..1)); // Block at index 0 completed
1286        assert_eq!(seq5.blocks.len(), 1);
1287        assert_eq!(seq5.current_block.tokens.as_ref(), &[5]);
1288        assert_eq!(seq5.blocks[0].tokens().as_ref(), &[1, 2, 3, 4]);
1289        assert_eq!(seq5.current_block.parent_sequence_hash, Some(SEQ_HASH_1_4));
1290        assert_eq!(seq5.current_block.remaining(), 3);
1291
1292        let tokens5c = Tokens::from(vec![6, 7, 8, 9, 10]);
1293        let completed5c = seq5.extend(tokens5c).unwrap();
1294        assert_eq!(completed5c, Some(1..2)); // Block at index 1 completed
1295        assert_eq!(seq5.blocks.len(), 2);
1296        assert_eq!(seq5.current_block.tokens.as_ref(), &[9, 10]);
1297        assert_eq!(seq5.blocks[1].tokens().as_ref(), &[5, 6, 7, 8]);
1298        assert_eq!(seq5.current_block.parent_sequence_hash, Some(SEQ_HASH_5_8));
1299        assert_eq!(seq5.current_block.remaining(), 2);
1300
1301        // Case 6: Extend empty tokens
1302        let mut seq6 = create_test_sequence(&[1], block_size, salt_hash);
1303        let completed6 = seq6.extend(Tokens::default()).unwrap();
1304        assert_eq!(completed6, None);
1305        assert_eq!(seq6.blocks.len(), 0);
1306        assert_eq!(seq6.current_block.tokens.as_ref(), &[1]);
1307        assert_eq!(seq6.total_tokens(), 1);
1308
1309        // Case 7: Extend fills current exactly, no remainder
1310        let mut seq7 = create_test_sequence(&[1, 2], block_size, salt_hash);
1311        let tokens7 = Tokens::from(vec![3, 4]);
1312        let completed7 = seq7.extend(tokens7).unwrap();
1313        assert_eq!(completed7, Some(0..1)); // Block is full but not committed yet
1314        assert_eq!(seq7.blocks.len(), 1);
1315        assert_eq!(seq7.current_block.tokens.as_ref(), &[0u32; 0]); // Current block is full
1316        assert_eq!(seq7.current_block.remaining(), 4);
1317        assert_eq!(seq7.total_tokens(), 4);
1318        assert_eq!(seq7.current_block.parent_sequence_hash, Some(SEQ_HASH_1_4)); // Still the root block
1319
1320        // Test tokens_at extraction
1321        assert_eq!(seq7.tokens_at(0..2).as_ref(), &[1, 2]);
1322        assert_eq!(seq7.tokens_at(1..3).as_ref(), &[2, 3]);
1323        assert_eq!(seq7.tokens_at(0..4).as_ref(), &[1, 2, 3, 4]);
1324        assert_eq!(seq7.tokens_at(2..2).as_ref(), &[0u32; 0]); // Empty range
1325    }
1326
1327    #[test]
1328    fn test_truncate() {
1329        let block_size = 4;
1330        let salt_hash = Some(TEST_SALT_HASH);
1331        let initial_tokens = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 10 tokens
1332
1333        // Case 1: Truncate within current block (len 9)
1334        let mut seq1 = create_test_sequence(initial_tokens, block_size, salt_hash);
1335        assert!(seq1.truncate(9).is_ok());
1336        assert_eq!(seq1.total_tokens(), 9);
1337        assert_eq!(seq1.blocks().len(), 2);
1338        assert_eq!(seq1.current_block().tokens.as_ref(), &[9]);
1339        assert_eq!(
1340            seq1.current_block().parent_sequence_hash,
1341            Some(SEQ_HASH_5_8)
1342        );
1343
1344        // Case 2: Truncate to exact block boundary (len 8)
1345        let mut seq2 = create_test_sequence(initial_tokens, block_size, salt_hash);
1346        assert!(seq2.truncate(8).is_ok());
1347        assert_eq!(seq2.total_tokens(), 8);
1348        assert_eq!(seq2.blocks().len(), 2);
1349        assert!(seq2.current_block().tokens.is_empty());
1350        assert_eq!(
1351            seq2.current_block().parent_sequence_hash,
1352            Some(SEQ_HASH_5_8)
1353        );
1354
1355        // Case 3: Truncate into last full block (len 7)
1356        let mut seq3 = create_test_sequence(initial_tokens, block_size, salt_hash);
1357        assert!(seq3.truncate(7).is_ok());
1358        assert_eq!(seq3.total_tokens(), 7);
1359        assert_eq!(seq3.blocks().len(), 1); // Block [5,6,7,8] removed conceptually
1360        assert_eq!(seq3.current_block().tokens.as_ref(), &[5, 6, 7]); // Kept 3 from [5,6,7,8]
1361        assert_eq!(
1362            seq3.current_block().parent_sequence_hash,
1363            Some(SEQ_HASH_1_4)
1364        ); // Parent is hash of [1,2,3,4]
1365        assert_eq!(seq3.blocks()[0].tokens().as_ref(), &[1, 2, 3, 4]);
1366
1367        // Case 4: Truncate removing full block(s) exactly (len 4)
1368        let mut seq4 = create_test_sequence(initial_tokens, block_size, salt_hash);
1369        assert!(seq4.truncate(4).is_ok());
1370        assert_eq!(seq4.total_tokens(), 4);
1371        assert_eq!(seq4.blocks().len(), 1); // Block [5,6,7,8] removed
1372        assert!(seq4.current_block().tokens.is_empty()); // New partial based on block [1,2,3,4]
1373        assert_eq!(
1374            seq4.current_block().parent_sequence_hash,
1375            Some(SEQ_HASH_1_4)
1376        );
1377        assert_eq!(seq4.blocks()[0].tokens().as_ref(), &[1, 2, 3, 4]);
1378
1379        // Case 5: Truncate into first block (len 3)
1380        let mut seq5 = create_test_sequence(initial_tokens, block_size, salt_hash);
1381        assert!(seq5.truncate(3).is_ok());
1382        assert_eq!(seq5.total_tokens(), 3);
1383        assert!(seq5.blocks().is_empty()); // Both blocks removed conceptually
1384        assert_eq!(seq5.current_block().tokens.as_ref(), &[1, 2, 3]); // Kept 3 from [1,2,3,4]
1385        assert_eq!(seq5.current_block().parent_sequence_hash, None); // No parent
1386
1387        // Case 6: Truncate to zero length (len 0)
1388        let mut seq6 = create_test_sequence(initial_tokens, block_size, salt_hash);
1389        assert!(seq6.truncate(0).is_ok());
1390        assert_eq!(seq6.total_tokens(), 0);
1391        assert!(seq6.blocks().is_empty());
1392        assert!(seq6.current_block().tokens.is_empty());
1393        assert_eq!(seq6.current_block().parent_sequence_hash, None);
1394
1395        // Case 7: Truncate to length greater than current (len 11)
1396        let mut seq7 = create_test_sequence(initial_tokens, block_size, salt_hash);
1397        let original_state = (seq7.blocks.clone(), seq7.current_block.tokens.clone()); // Clone for state check
1398        assert!(seq7.truncate(11).is_ok()); // Should have no effect
1399        assert_eq!(seq7.total_tokens(), 10);
1400        assert_eq!(seq7.blocks, original_state.0);
1401        assert_eq!(seq7.current_block.tokens, original_state.1);
1402
1403        // Case 8: Truncate to current length (len 10)
1404        let mut seq8 = create_test_sequence(initial_tokens, block_size, salt_hash);
1405        let original_state = (seq8.blocks.clone(), seq8.current_block.tokens.clone());
1406        assert!(seq8.truncate(10).is_ok());
1407        assert_eq!(seq8.total_tokens(), 10);
1408        assert_eq!(seq8.blocks, original_state.0);
1409        assert_eq!(seq8.current_block.tokens, original_state.1);
1410
1411        // Case 9: Truncate an empty sequence to 0
1412        let mut seq9 = create_test_sequence(&[], block_size, salt_hash);
1413        assert!(seq9.truncate(0).is_ok());
1414        assert_eq!(seq9.total_tokens(), 0);
1415        assert!(seq9.blocks().is_empty());
1416        assert!(seq9.current_block().tokens.is_empty());
1417
1418        // Case 10: Truncate on exact block boundary when current is empty (len 4)
1419        let tokens10 = &[1, 2, 3, 4, 5, 6, 7, 8]; // 8 tokens
1420        let mut seq10 = create_test_sequence(tokens10, block_size, salt_hash);
1421        assert_eq!(seq10.total_tokens(), 8);
1422        assert!(seq10.current_block().is_empty());
1423        assert!(seq10.truncate(4).is_ok()); // Remove block [5, 6, 7, 8]
1424        assert_eq!(seq10.total_tokens(), 4);
1425        assert_eq!(seq10.blocks().len(), 1);
1426        assert!(seq10.current_block().tokens.is_empty());
1427        assert_eq!(
1428            seq10.current_block().parent_sequence_hash,
1429            Some(SEQ_HASH_1_4)
1430        );
1431
1432        // Case 11: Truncate into first block when current is empty (len 3)
1433        let tokens11 = &[1, 2, 3, 4, 5, 6, 7, 8]; // 8 tokens
1434        let mut seq11 = create_test_sequence(tokens11, block_size, salt_hash);
1435        assert!(seq11.truncate(3).is_ok()); // Pop block [5,6,7,8] + 1 from [1,2,3,4]
1436        assert_eq!(seq11.total_tokens(), 3);
1437        assert!(seq11.blocks().is_empty());
1438        assert_eq!(seq11.current_block().tokens.as_ref(), &[1, 2, 3]); // Kept 3 from [1,2,3,4]
1439        assert_eq!(seq11.current_block().parent_sequence_hash, None);
1440    }
1441
1442    #[test]
1443    fn test_unwind() {
1444        let block_size = 4;
1445        let salt_hash = Some(TEST_SALT_HASH);
1446        let initial_tokens = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 10 tokens
1447
1448        // Unwind 0
1449        let mut seq = create_test_sequence(initial_tokens, block_size, salt_hash);
1450        assert!(seq.unwind(0).is_ok());
1451        assert_eq!(seq.total_tokens(), 10);
1452
1453        // Unwind 1
1454        let mut seq = create_test_sequence(initial_tokens, block_size, salt_hash);
1455        assert!(seq.unwind(1).is_ok());
1456        assert_eq!(seq.total_tokens(), 9);
1457        assert_eq!(seq.current_block.tokens.as_ref(), &[9]);
1458
1459        // Unwind 3 (crosses boundary)
1460        let mut seq = create_test_sequence(initial_tokens, block_size, salt_hash);
1461        assert!(seq.unwind(3).is_ok());
1462        assert_eq!(seq.total_tokens(), 7);
1463        assert_eq!(seq.blocks.len(), 1);
1464        assert_eq!(seq.current_block.tokens.as_ref(), &[5, 6, 7]);
1465
1466        // Unwind all (10)
1467        let mut seq = create_test_sequence(initial_tokens, block_size, salt_hash);
1468        assert!(seq.unwind(10).is_ok());
1469        assert_eq!(seq.total_tokens(), 0);
1470        assert!(seq.blocks.is_empty());
1471        assert!(seq.current_block.is_empty());
1472
1473        // Unwind more than available (11)
1474        let mut seq = create_test_sequence(initial_tokens, block_size, salt_hash);
1475        assert_eq!(seq.unwind(11), Err(TokenBlockError::InsufficientTokens));
1476        assert_eq!(seq.total_tokens(), 10); // State unchanged
1477
1478        // Unwind from empty
1479        let mut seq_empty = create_test_sequence(&[], block_size, salt_hash);
1480        assert_eq!(
1481            seq_empty.unwind(1),
1482            Err(TokenBlockError::InsufficientTokens)
1483        );
1484    }
1485
1486    #[test]
1487    fn test_pop() {
1488        let block_size = 4;
1489        let salt_hash = Some(TEST_SALT_HASH);
1490        let initial_tokens = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 10 tokens
1491
1492        let mut seq = create_test_sequence(initial_tokens, block_size, salt_hash);
1493
1494        // Pop 10
1495        assert_eq!(seq.pop(), Some(10));
1496        assert_eq!(seq.total_tokens(), 9);
1497        assert_eq!(seq.current_block.tokens.as_ref(), &[9]);
1498        assert_eq!(seq.blocks.len(), 2);
1499
1500        // Pop 9
1501        assert_eq!(seq.pop(), Some(9));
1502        assert_eq!(seq.total_tokens(), 8);
1503        assert!(seq.current_block.is_empty());
1504        assert_eq!(seq.blocks.len(), 2);
1505        assert_eq!(seq.current_block.parent_sequence_hash, Some(SEQ_HASH_5_8));
1506
1507        // Pop 8 (crosses boundary)
1508        assert_eq!(seq.pop(), Some(8));
1509        assert_eq!(seq.total_tokens(), 7);
1510        assert_eq!(seq.current_block.tokens.as_ref(), &[5, 6, 7]);
1511        assert_eq!(seq.blocks.len(), 1);
1512        assert_eq!(seq.current_block.parent_sequence_hash, Some(SEQ_HASH_1_4));
1513
1514        // Pop remaining partial (7, 6, 5)
1515        assert_eq!(seq.pop(), Some(7));
1516        assert_eq!(seq.pop(), Some(6));
1517        assert_eq!(seq.pop(), Some(5));
1518        assert_eq!(seq.total_tokens(), 4);
1519        assert!(seq.current_block.is_empty());
1520        assert_eq!(seq.blocks.len(), 1);
1521        assert_eq!(seq.current_block.parent_sequence_hash, Some(SEQ_HASH_1_4));
1522
1523        // Pop 4 (crosses boundary)
1524        assert_eq!(seq.pop(), Some(4));
1525        assert_eq!(seq.total_tokens(), 3);
1526        assert_eq!(seq.current_block.tokens.as_ref(), &[1, 2, 3]);
1527        assert!(seq.blocks.is_empty());
1528        assert_eq!(seq.current_block.parent_sequence_hash, None);
1529
1530        // Pop 3, 2, 1
1531        assert_eq!(seq.pop(), Some(3));
1532        assert_eq!(seq.pop(), Some(2));
1533        assert_eq!(seq.pop(), Some(1));
1534        assert_eq!(seq.total_tokens(), 0);
1535        assert!(seq.current_block.is_empty());
1536        assert!(seq.blocks.is_empty());
1537
1538        // Pop from empty
1539        assert_eq!(seq.pop(), None);
1540        assert_eq!(seq.total_tokens(), 0);
1541    }
1542
1543    #[test]
1544    fn test_total_tokens() {
1545        let block_size = 3;
1546        let salt_hash = Some(TEST_SALT_HASH);
1547
1548        let mut seq = create_test_sequence(&[], block_size, salt_hash);
1549        assert_eq!(seq.total_tokens(), 0);
1550
1551        seq.extend(Tokens::from(vec![1, 2])).unwrap();
1552        assert_eq!(seq.total_tokens(), 2);
1553
1554        seq.append(3).unwrap(); // Completes block 0
1555        assert_eq!(seq.total_tokens(), 3);
1556
1557        seq.extend(Tokens::from(vec![4, 5, 6, 7])).unwrap(); // Completes block 1, partial [7]
1558        assert_eq!(seq.total_tokens(), 7);
1559
1560        seq.pop().unwrap(); // Removes 7
1561        assert_eq!(seq.total_tokens(), 6);
1562
1563        seq.truncate(4).unwrap(); // Keep [1,2,3,4]
1564        assert_eq!(seq.total_tokens(), 4);
1565
1566        seq.unwind(2).unwrap(); // Keep [1,2]
1567        assert_eq!(seq.total_tokens(), 2);
1568    }
1569
1570    #[test]
1571    fn test_push_tokens_partial_block() {
1572        let mut partial = PartialTokenBlock::create_sequence_root(4, 1337);
1573
1574        let tokens = Tokens(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1575
1576        let remaining = partial.push_tokens(tokens);
1577        assert_eq!(partial.tokens.len(), 4);
1578        assert_eq!(remaining.len(), 6);
1579    }
1580}