Skip to main content

ik_llama_cpp_2/context/
session.rs

1//! utilities for working with session files
2
3use crate::context::LlamaContext;
4use crate::token::LlamaToken;
5use std::ffi::{CString, NulError};
6use std::fmt::{Debug, Formatter};
7use std::path::{Path, PathBuf};
8
9/// Flags for state sequence operations.
10///
11/// These flags control what parts of the state are included when saving/restoring
12/// sequence state.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct LlamaStateSeqFlags(pub(crate) ik_llama_cpp_sys::llama_state_seq_flags);
15
16impl LlamaStateSeqFlags {
17    /// Work only with partial states, such as SWA KV cache or recurrent cache (e.g. Mamba).
18    ///
19    /// This flag is useful when you only want to save/restore the recurrent state
20    /// without affecting the KV cache.
21    pub const PARTIAL_ONLY: LlamaStateSeqFlags = LlamaStateSeqFlags(1);
22
23    /// Keep the copied data on device (GPU) memory rather than host.
24    ///
25    /// Getting the state for a `seq_id` with this flag invalidates all
26    /// prior states obtained for that `seq_id` with this flag set.
27    pub const ON_DEVICE: LlamaStateSeqFlags = LlamaStateSeqFlags(2);
28
29    /// Create an empty flags set.
30    pub const fn empty() -> LlamaStateSeqFlags {
31        LlamaStateSeqFlags(0)
32    }
33
34    /// Get the raw flags value.
35    pub const fn bits(&self) -> u32 {
36        self.0
37    }
38
39    /// Construct from raw bits.
40    pub const fn from_bits(bits: u32) -> LlamaStateSeqFlags {
41        LlamaStateSeqFlags(bits)
42    }
43
44    /// Check if a flag is set.
45    pub const fn contains(&self, other: LlamaStateSeqFlags) -> bool {
46        (self.0 & other.0) != 0
47    }
48}
49
50impl Default for LlamaStateSeqFlags {
51    fn default() -> Self {
52        Self::empty()
53    }
54}
55
56impl std::ops::BitOr for LlamaStateSeqFlags {
57    type Output = Self;
58    fn bitor(self, other: Self) -> Self {
59        Self(self.0 | other.0)
60    }
61}
62
63/// Errors from the sequence-state save/restore API.
64///
65/// Module-local mirror of the anchor crate's `crate::StateSeqError`, which
66/// lives in the crate root there; kept local here so this module compiles
67/// independently.
68#[derive(Debug, Eq, PartialEq, thiserror::Error)]
69pub enum StateSeqError {
70    /// llama.cpp read or wrote a different number of bytes than expected.
71    #[error("state seq size mismatch: expected {expected}, actual {actual}")]
72    SizeMismatch {
73        /// Number of bytes the caller expected to transfer.
74        expected: usize,
75        /// Number of bytes llama.cpp actually transferred.
76        actual: usize,
77    },
78}
79
80/// Failed to save a sequence state file
81#[derive(Debug, Eq, PartialEq, thiserror::Error)]
82pub enum SaveSeqStateError {
83    /// llama.cpp failed to save the sequence state file
84    #[error("Failed to save sequence state file")]
85    FailedToSave,
86
87    /// null byte in string
88    #[error("null byte in string {0}")]
89    NullError(#[from] NulError),
90
91    /// failed to convert path to str
92    #[error("failed to convert path {0} to str")]
93    PathToStrError(PathBuf),
94}
95
96/// Failed to load a sequence state file
97#[derive(Debug, Eq, PartialEq, thiserror::Error)]
98pub enum LoadSeqStateError {
99    /// llama.cpp failed to load the sequence state file
100    #[error("Failed to load sequence state file")]
101    FailedToLoad,
102
103    /// null byte in string
104    #[error("null byte in string {0}")]
105    NullError(#[from] NulError),
106
107    /// failed to convert path to str
108    #[error("failed to convert path {0} to str")]
109    PathToStrError(PathBuf),
110
111    /// Insufficient max length
112    #[error("max_length is not large enough to hold {n_out} (was {max_tokens})")]
113    InsufficientMaxLength {
114        /// The length of the loaded sequence
115        n_out: usize,
116        /// The maximum length
117        max_tokens: usize,
118    },
119}
120
121/// Failed to save a Session file
122#[derive(Debug, Eq, PartialEq, thiserror::Error)]
123pub enum SaveSessionError {
124    /// llama.cpp failed to save the session file
125    #[error("Failed to save session file")]
126    FailedToSave,
127
128    /// null byte in string
129    #[error("null byte in string {0}")]
130    NullError(#[from] NulError),
131
132    /// failed to convert path to str
133    #[error("failed to convert path {0} to str")]
134    PathToStrError(PathBuf),
135}
136
137/// Failed to load a Session file
138#[derive(Debug, Eq, PartialEq, thiserror::Error)]
139pub enum LoadSessionError {
140    /// llama.cpp failed to load the session file
141    #[error("Failed to load session file")]
142    FailedToLoad,
143
144    /// null byte in string
145    #[error("null byte in string {0}")]
146    NullError(#[from] NulError),
147
148    /// failed to convert path to str
149    #[error("failed to convert path {0} to str")]
150    PathToStrError(PathBuf),
151
152    /// Insufficient max length
153    #[error("max_length is not large enough to hold {n_out} (was {max_tokens})")]
154    InsufficientMaxLength {
155        /// The length of the session file
156        n_out: usize,
157        /// The maximum length
158        max_tokens: usize,
159    },
160}
161
162impl LlamaContext<'_> {
163    /// Save the current session to a file.
164    ///
165    /// # Parameters
166    ///
167    /// * `path_session` - The file to save to.
168    /// * `tokens` - The tokens to associate the session with. This should be a prefix of a sequence of tokens that the context has processed, so that the relevant KV caches are already filled.
169    ///
170    /// # Errors
171    ///
172    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to save the session file.
173    #[deprecated(since = "0.1.136", note = "Use `state_save_file` instead")]
174    pub fn save_session_file(
175        &self,
176        path_session: impl AsRef<Path>,
177        tokens: &[LlamaToken],
178    ) -> Result<(), SaveSessionError> {
179        let path = path_session.as_ref();
180        let path = path
181            .to_str()
182            .ok_or_else(|| SaveSessionError::PathToStrError(path.to_path_buf()))?;
183
184        let cstr = CString::new(path)?;
185
186        if unsafe {
187            ik_llama_cpp_sys::llama_save_session_file(
188                self.context.as_ptr(),
189                cstr.as_ptr(),
190                tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
191                tokens.len(),
192            )
193        } {
194            Ok(())
195        } else {
196            Err(SaveSessionError::FailedToSave)
197        }
198    }
199    /// Load a session file into the current context.
200    ///
201    /// You still need to pass the returned tokens to the context for inference to work. What this function buys you is that the KV caches are already filled with the relevant data.
202    ///
203    /// # Parameters
204    ///
205    /// * `path_session` - The file to load from. It must be a session file from a compatible context, otherwise the function will error.
206    /// * `max_tokens` - The maximum token length of the loaded session. If the session was saved with a longer length, the function will error.
207    ///
208    /// # Errors
209    ///
210    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to load the session file. (e.g. the file does not exist, is not a session file, etc.)
211    #[deprecated(since = "0.1.136", note = "Use `state_load_file` instead")]
212    pub fn load_session_file(
213        &mut self,
214        path_session: impl AsRef<Path>,
215        max_tokens: usize,
216    ) -> Result<Vec<LlamaToken>, LoadSessionError> {
217        let path = path_session.as_ref();
218        let path = path
219            .to_str()
220            .ok_or(LoadSessionError::PathToStrError(path.to_path_buf()))?;
221
222        let cstr = CString::new(path)?;
223        let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
224        let mut n_out = 0;
225
226        // SAFETY: cast is valid as LlamaToken is repr(transparent)
227        let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
228
229        let load_session_success = unsafe {
230            ik_llama_cpp_sys::llama_load_session_file(
231                self.context.as_ptr(),
232                cstr.as_ptr(),
233                tokens_out,
234                max_tokens,
235                &mut n_out,
236            )
237        };
238        if load_session_success {
239            if n_out > max_tokens {
240                return Err(LoadSessionError::InsufficientMaxLength { n_out, max_tokens });
241            }
242            // SAFETY: we checked that n_out <= max_tokens and llama.cpp promises that n_out tokens will be written
243            unsafe {
244                tokens.set_len(n_out);
245            }
246            Ok(tokens)
247        } else {
248            Err(LoadSessionError::FailedToLoad)
249        }
250    }
251
252    /// Save the full state to a file.
253    ///
254    /// This is the non-deprecated replacement for [`save_session_file`](Self::save_session_file).
255    ///
256    /// # Parameters
257    ///
258    /// * `path_session` - The file to save to.
259    /// * `tokens` - The tokens to associate the state with. This should be a prefix of a sequence
260    ///   of tokens that the context has processed, so that the relevant KV caches are already filled.
261    ///
262    /// # Errors
263    ///
264    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to save
265    /// the state file.
266    pub fn state_save_file(
267        &self,
268        path_session: impl AsRef<Path>,
269        tokens: &[LlamaToken],
270    ) -> Result<(), SaveSessionError> {
271        let path = path_session.as_ref();
272        let path = path
273            .to_str()
274            .ok_or_else(|| SaveSessionError::PathToStrError(path.to_path_buf()))?;
275
276        let cstr = CString::new(path)?;
277
278        if unsafe {
279            ik_llama_cpp_sys::llama_state_save_file(
280                self.context.as_ptr(),
281                cstr.as_ptr(),
282                tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
283                tokens.len(),
284            )
285        } {
286            Ok(())
287        } else {
288            Err(SaveSessionError::FailedToSave)
289        }
290    }
291
292    /// Load a state file into the current context.
293    ///
294    /// This is the non-deprecated replacement for [`load_session_file`](Self::load_session_file).
295    ///
296    /// You still need to pass the returned tokens to the context for inference to work. What this
297    /// function buys you is that the KV caches are already filled with the relevant data.
298    ///
299    /// # Parameters
300    ///
301    /// * `path_session` - The file to load from. It must be a state file from a compatible context,
302    ///   otherwise the function will error.
303    /// * `max_tokens` - The maximum token length of the loaded state. If the state was saved with a
304    ///   longer length, the function will error.
305    ///
306    /// # Errors
307    ///
308    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to load
309    /// the state file.
310    pub fn state_load_file(
311        &mut self,
312        path_session: impl AsRef<Path>,
313        max_tokens: usize,
314    ) -> Result<Vec<LlamaToken>, LoadSessionError> {
315        let path = path_session.as_ref();
316        let path = path
317            .to_str()
318            .ok_or(LoadSessionError::PathToStrError(path.to_path_buf()))?;
319
320        let cstr = CString::new(path)?;
321        let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
322        let mut n_out = 0;
323
324        // SAFETY: cast is valid as LlamaToken is repr(transparent)
325        let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
326
327        let success = unsafe {
328            ik_llama_cpp_sys::llama_state_load_file(
329                self.context.as_ptr(),
330                cstr.as_ptr(),
331                tokens_out,
332                max_tokens,
333                &mut n_out,
334            )
335        };
336        if success {
337            if n_out > max_tokens {
338                return Err(LoadSessionError::InsufficientMaxLength { n_out, max_tokens });
339            }
340            // SAFETY: we checked that n_out <= max_tokens and llama.cpp promises that n_out tokens will be written
341            unsafe {
342                tokens.set_len(n_out);
343            }
344            Ok(tokens)
345        } else {
346            Err(LoadSessionError::FailedToLoad)
347        }
348    }
349
350    /// Save state for a single sequence to a file.
351    ///
352    /// This enables saving state for individual sequences, which is useful for multi-sequence
353    /// inference scenarios.
354    ///
355    /// # Parameters
356    ///
357    /// * `filepath` - The file to save to.
358    /// * `seq_id` - The sequence ID whose state to save.
359    /// * `tokens` - The tokens to associate with the saved state.
360    ///
361    /// # Errors
362    ///
363    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to save
364    /// the sequence state file.
365    ///
366    /// # Returns
367    ///
368    /// The number of bytes written on success.
369    pub fn state_seq_save_file(
370        &self,
371        filepath: impl AsRef<Path>,
372        seq_id: i32,
373        tokens: &[LlamaToken],
374    ) -> Result<usize, SaveSeqStateError> {
375        let path = filepath.as_ref();
376        let path = path
377            .to_str()
378            .ok_or_else(|| SaveSeqStateError::PathToStrError(path.to_path_buf()))?;
379
380        let cstr = CString::new(path)?;
381
382        let bytes_written = unsafe {
383            ik_llama_cpp_sys::llama_state_seq_save_file(
384                self.context.as_ptr(),
385                cstr.as_ptr(),
386                seq_id,
387                tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
388                tokens.len(),
389            )
390        };
391
392        if bytes_written == 0 {
393            Err(SaveSeqStateError::FailedToSave)
394        } else {
395            Ok(bytes_written)
396        }
397    }
398
399    /// Load state for a single sequence from a file.
400    ///
401    /// This enables loading state for individual sequences, which is useful for multi-sequence
402    /// inference scenarios.
403    ///
404    /// # Parameters
405    ///
406    /// * `filepath` - The file to load from.
407    /// * `dest_seq_id` - The destination sequence ID to load the state into.
408    /// * `max_tokens` - The maximum number of tokens to read.
409    ///
410    /// # Errors
411    ///
412    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to load
413    /// the sequence state file.
414    ///
415    /// # Returns
416    ///
417    /// A tuple of `(tokens, bytes_read)` on success.
418    pub fn state_seq_load_file(
419        &mut self,
420        filepath: impl AsRef<Path>,
421        dest_seq_id: i32,
422        max_tokens: usize,
423    ) -> Result<(Vec<LlamaToken>, usize), LoadSeqStateError> {
424        let path = filepath.as_ref();
425        let path = path
426            .to_str()
427            .ok_or(LoadSeqStateError::PathToStrError(path.to_path_buf()))?;
428
429        let cstr = CString::new(path)?;
430        let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
431        let mut n_out = 0;
432
433        // SAFETY: cast is valid as LlamaToken is repr(transparent)
434        let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
435
436        let bytes_read = unsafe {
437            ik_llama_cpp_sys::llama_state_seq_load_file(
438                self.context.as_ptr(),
439                cstr.as_ptr(),
440                dest_seq_id,
441                tokens_out,
442                max_tokens,
443                &mut n_out,
444            )
445        };
446
447        if bytes_read == 0 {
448            return Err(LoadSeqStateError::FailedToLoad);
449        }
450
451        if n_out > max_tokens {
452            return Err(LoadSeqStateError::InsufficientMaxLength { n_out, max_tokens });
453        }
454
455        // SAFETY: we checked that n_out <= max_tokens and llama.cpp promises that n_out tokens will be written
456        unsafe {
457            tokens.set_len(n_out);
458        }
459
460        Ok((tokens, bytes_read))
461    }
462
463    /// Returns the maximum size in bytes of the state (rng, logits, embedding
464    /// and `kv_cache`) - will often be smaller after compacting tokens
465    #[must_use]
466    pub fn get_state_size(&self) -> usize {
467        unsafe { ik_llama_cpp_sys::llama_get_state_size(self.context.as_ptr()) }
468    }
469
470    /// Copies the state to the specified destination address.
471    ///
472    /// Returns the number of bytes copied
473    ///
474    /// # Safety
475    ///
476    /// Destination needs to have allocated enough memory.
477    pub unsafe fn copy_state_data(&self, dest: *mut u8) -> usize {
478        unsafe { ik_llama_cpp_sys::llama_copy_state_data(self.context.as_ptr(), dest) }
479    }
480
481    /// Set the state reading from the specified address
482    /// Returns the number of bytes read
483    ///
484    /// # Safety
485    ///
486    /// help wanted: not entirely sure what the safety requirements are here.
487    pub unsafe fn set_state_data(&mut self, src: &[u8]) -> usize {
488        unsafe { ik_llama_cpp_sys::llama_set_state_data(self.context.as_ptr(), src.as_ptr()) }
489    }
490
491    /// Get the size of the state for a single sequence with optional flags.
492    ///
493    /// This is the extended version that supports flags for partial state operations.
494    ///
495    /// # Parameters
496    ///
497    /// * `seq_id` - The sequence ID to get the state size for.
498    /// * `flags` - Optional flags (e.g., [`LlamaStateSeqFlags::PARTIAL_ONLY`]).
499    ///
500    /// # Returns
501    ///
502    /// The size in bytes needed to store the sequence state.
503    #[must_use]
504    pub fn state_seq_get_size_ext(&self, seq_id: i32, flags: LlamaStateSeqFlags) -> usize {
505        unsafe {
506            ik_llama_cpp_sys::llama_state_seq_get_size(self.context.as_ptr(), seq_id, flags.0)
507        }
508    }
509
510    /// Copy the state of a single sequence into the specified buffer with optional flags.
511    ///
512    /// This is the extended version that supports flags for partial state operations.
513    ///
514    /// # Parameters
515    ///
516    /// * `dest` - Destination buffer to copy state into.
517    /// * `seq_id` - The sequence ID to get the state for.
518    /// * `flags` - Optional flags (e.g., [`LlamaStateSeqFlags::PARTIAL_ONLY`]).
519    ///
520    /// # Safety
521    ///
522    /// Destination needs to have allocated enough memory.
523    ///
524    /// # Returns
525    ///
526    /// The number of bytes copied.
527    pub unsafe fn state_seq_get_data_ext(
528        &self,
529        dest: *mut u8,
530        seq_id: i32,
531        flags: LlamaStateSeqFlags,
532    ) -> usize {
533        unsafe {
534            ik_llama_cpp_sys::llama_state_seq_get_data(
535                self.context.as_ptr(),
536                dest,
537                usize::MAX,
538                seq_id,
539                flags.0,
540            )
541        }
542    }
543
544    /// Set the state for a single sequence from the specified buffer with optional flags.
545    ///
546    /// This is the extended version that supports flags for partial state operations.
547    /// Useful for restoring only the recurrent/partial state without affecting the KV cache.
548    ///
549    /// # Parameters
550    ///
551    /// * `src` - Source buffer containing the state data.
552    /// * `dest_seq_id` - The destination sequence ID to load the state into.
553    /// * `flags` - Optional flags (e.g., [`LlamaStateSeqFlags::PARTIAL_ONLY`]).
554    ///
555    /// # Safety
556    ///
557    /// The source buffer must contain valid state data.
558    ///
559    /// # Returns
560    ///
561    /// `true` on success (the whole `src` buffer was consumed). ik returns
562    /// `SIZE_MAX` when state IO is unsupported and `0` on other failures; both
563    /// are reported as `false` (only `n == src.len()` counts as success).
564    pub unsafe fn state_seq_set_data_ext(
565        &mut self,
566        src: &[u8],
567        dest_seq_id: i32,
568        flags: LlamaStateSeqFlags,
569    ) -> bool {
570        let n = unsafe {
571            ik_llama_cpp_sys::llama_state_seq_set_data(
572                self.context.as_ptr(),
573                src.as_ptr(),
574                src.len(),
575                dest_seq_id,
576                flags.0,
577            )
578        };
579        n == src.len()
580    }
581
582    /// Serialize sequence `seq_id`'s state into an opaque [`SeqState`].
583    ///
584    /// Enables save/restore of context state at arbitrary points in a
585    /// sequence. This is particularly useful on architectures where
586    /// `clear_kv_cache_seq` cannot roll back partial state (Mamba, RWKV,
587    /// Gated Delta Networks, and other recurrent / hybrid-recurrent
588    /// models): pair with [`LlamaStateSeqFlags::PARTIAL_ONLY`] to save
589    /// just the running recurrent and SWA state, then restore it via
590    /// [`Self::state_seq_set`] to effectively "rewind" the sequence.
591    ///
592    /// The returned [`SeqState`] is opaque — its bytes cannot be
593    /// inspected or forged from safe code, so [`Self::state_seq_set`]
594    /// only ever sees data produced by this method.
595    ///
596    /// Wraps `llama_state_seq_get_data`.
597    ///
598    /// # Errors
599    ///
600    /// Returns [`StateSeqError::SizeMismatch`] if llama.cpp writes a
601    /// different number of bytes than the reported state size.
602    pub fn state_seq_get(
603        &self,
604        seq_id: i32,
605        flags: LlamaStateSeqFlags,
606    ) -> Result<SeqState, StateSeqError> {
607        let size = unsafe {
608            ik_llama_cpp_sys::llama_state_seq_get_size(self.context.as_ptr(), seq_id, flags.0)
609        };
610        let mut bytes = vec![0u8; size];
611        let n = unsafe {
612            ik_llama_cpp_sys::llama_state_seq_get_data(
613                self.context.as_ptr(),
614                bytes.as_mut_ptr(),
615                size,
616                seq_id,
617                flags.0,
618            )
619        };
620        if n != size {
621            return Err(StateSeqError::SizeMismatch {
622                expected: size,
623                actual: n,
624            });
625        }
626        Ok(SeqState { bytes, flags })
627    }
628
629    /// Restore sequence state previously captured by [`Self::state_seq_get`]
630    /// into `seq_id`.
631    ///
632    /// Cross-sequence restore (`seq_id` different from the sequence the
633    /// state was captured from) is supported — llama.cpp treats the
634    /// destination sequence id independently of the source.
635    ///
636    /// Wraps `llama_state_seq_set_data`.
637    ///
638    /// # Errors
639    ///
640    /// Returns [`StateSeqError::SizeMismatch`] if llama.cpp reads a
641    /// different number of bytes than the state buffer contains — this
642    /// covers shape mismatches (different `n_ctx`, `n_layer`, quantization,
643    /// etc.) that llama.cpp's own deserializer detects and aborts on.
644    pub fn state_seq_set(&mut self, state: &SeqState, seq_id: i32) -> Result<(), StateSeqError> {
645        let n = unsafe {
646            ik_llama_cpp_sys::llama_state_seq_set_data(
647                self.context.as_ptr(),
648                state.bytes.as_ptr(),
649                state.bytes.len(),
650                seq_id,
651                state.flags.0,
652            )
653        };
654        if n != state.bytes.len() {
655            return Err(StateSeqError::SizeMismatch {
656                expected: state.bytes.len(),
657                actual: n,
658            });
659        }
660        Ok(())
661    }
662}
663
664/// Opaque, immutable snapshot of a single sequence's state.
665///
666/// Produced by [`LlamaContext::state_seq_get`] and consumed by
667/// [`LlamaContext::state_seq_set`]. Bytes cannot be constructed, forged,
668/// or mutated from safe code — the only way to obtain a `SeqState` is
669/// via a get call, which guarantees the payload came from llama.cpp
670/// itself. Combined with llama.cpp's own shape validation on the
671/// deserialize path, this closes the "arbitrary bytes into C parser"
672/// unsoundness that motivates the unsafe raw setters.
673///
674/// The state carries the flag set it was captured with; restore uses
675/// those same flags automatically so the byte layout always matches.
676#[derive(Clone)]
677pub struct SeqState {
678    bytes: Vec<u8>,
679    flags: LlamaStateSeqFlags,
680}
681
682impl SeqState {
683    /// The flag set that was used to capture this state.
684    #[must_use]
685    pub fn flags(&self) -> LlamaStateSeqFlags {
686        self.flags
687    }
688
689    /// Size in bytes of the serialized state.
690    #[must_use]
691    pub fn byte_len(&self) -> usize {
692        self.bytes.len()
693    }
694}
695
696impl Debug for SeqState {
697    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
698        f.debug_struct("SeqState")
699            .field("byte_len", &self.bytes.len())
700            .field("flags", &self.flags)
701            .finish()
702    }
703}