Skip to main content

llama_cpp_4/
token.rs

1//! Safe wrappers around `llama_token_data` and `llama_token_data_array`.
2
3use std::fmt::Debug;
4use std::fmt::Display;
5use std::mem::ManuallyDrop;
6
7pub mod data;
8pub mod data_array;
9pub mod detokenizer;
10
11/// A safe wrapper for `llama_token`.
12///
13/// This struct wraps around a `llama_token` and implements various traits for safe usage, including
14/// `Clone`, `Copy`, `Debug`, `Eq`, `PartialEq`, `Ord`, `PartialOrd`, and `Hash`. The `Display` trait
15/// is also implemented to provide a simple way to format `LlamaToken` for printing.
16#[repr(transparent)]
17#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
18#[allow(clippy::module_name_repetitions)]
19pub struct LlamaToken(pub llama_cpp_sys_4::llama_token);
20
21impl Display for LlamaToken {
22    /// Formats the `LlamaToken` for display by printing its inner value.
23    ///
24    /// This implementation allows you to easily print a `LlamaToken` by using `{}` in formatting macros.
25    ///
26    /// # Example
27    ///
28    /// ```
29    /// # use llama_cpp_4::token::LlamaToken;
30    /// let token = LlamaToken::new(42);
31    /// println!("{}", token); // Prints: 42
32    /// ```
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.0)
35    }
36}
37
38impl LlamaToken {
39    /// Creates a new `LlamaToken` from an `i32`.
40    ///
41    /// This constructor allows you to easily create a `LlamaToken` from a raw integer value representing
42    /// the token's ID. This is useful when interacting with external systems that provide token IDs as integers.
43    ///
44    /// # Example
45    ///
46    /// ```
47    /// # use llama_cpp_4::token::LlamaToken;
48    /// let token = LlamaToken::new(0);
49    /// assert_eq!(token, LlamaToken(0));
50    /// ```
51    ///
52    /// # Parameters
53    ///
54    /// - `token_id`: The integer ID for the token.
55    ///
56    /// # Returns
57    ///
58    /// Returns a new instance of `LlamaToken` wrapping the provided `token_id`.
59    #[must_use]
60    pub fn new(token_id: i32) -> Self {
61        Self(token_id)
62    }
63}
64
65/// Converts a vector of `llama_token` to a vector of `LlamaToken` without memory allocation,
66/// and consumes the original vector. This conversion is safe because `LlamaToken` is repr(transparent),
67/// meaning it is just a wrapper around the raw `llama_token` type.
68///
69/// # Safety
70///
71/// This operation is safe because `LlamaToken` has a `repr(transparent)` attribute, ensuring that
72/// the memory layout of `LlamaToken` is the same as that of the underlying `llama_token` type.
73#[must_use]
74pub fn from_vec_token_sys(vec_sys: Vec<llama_cpp_sys_4::llama_token>) -> Vec<LlamaToken> {
75    let mut vec_sys = ManuallyDrop::new(vec_sys);
76    let ptr = vec_sys.as_mut_ptr().cast::<LlamaToken>();
77    // SAFETY: the pointer is non-null and the length is the count the library
78    // reported for that same buffer, so the slice stays inside it.
79    unsafe { Vec::from_raw_parts(ptr, vec_sys.len(), vec_sys.capacity()) }
80}
81
82/// Converts a vector of `LlamaToken` to a vector of `llama_token` without memory allocation,
83/// and consumes the original vector. This conversion is safe because `LlamaToken` is repr(transparent),
84/// meaning it is just a wrapper around the raw `llama_token` type.
85///
86/// # Safety
87///
88/// This operation is safe because `LlamaToken` has a `repr(transparent)` attribute, ensuring that
89/// the memory layout of `LlamaToken` is the same as that of the underlying `llama_token` type.
90#[must_use]
91pub fn to_vec_token_sys(vec_llama: Vec<LlamaToken>) -> Vec<llama_cpp_sys_4::llama_token> {
92    let mut vec_llama = ManuallyDrop::new(vec_llama);
93    let ptr = vec_llama
94        .as_mut_ptr()
95        .cast::<llama_cpp_sys_4::llama_token>();
96    // SAFETY: the pointer is non-null and the length is the count the library
97    // reported for that same buffer, so the slice stays inside it.
98    unsafe { Vec::from_raw_parts(ptr, vec_llama.len(), vec_llama.capacity()) }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use std::time::Instant;
105
106    #[test]
107    fn test_new_llama_token() {
108        let token = LlamaToken::new(42);
109        assert_eq!(token, LlamaToken(42)); // Verify that the created token has the expected value
110    }
111
112    #[test]
113    fn test_llama_token_display() {
114        let token = LlamaToken::new(99);
115        assert_eq!(format!("{token}"), "99"); // Verify that the token formats correctly
116    }
117
118    #[test]
119    fn test_from_vec_token_sys() {
120        // Test converting a vector of raw `llama_token` to a vector of `LlamaToken`
121        let vec_sys: Vec<llama_cpp_sys_4::llama_token> = vec![1, 2, 3];
122        let vec_llama = from_vec_token_sys(vec_sys);
123
124        // Ensure that the conversion works correctly
125        assert_eq!(vec_llama.len(), 3);
126        assert_eq!(vec_llama[0], LlamaToken(1));
127        assert_eq!(vec_llama[1], LlamaToken(2));
128        assert_eq!(vec_llama[2], LlamaToken(3));
129    }
130
131    #[test]
132    fn test_to_vec_token_sys() {
133        // Test converting a vector of `LlamaToken` to a vector of raw `llama_token`
134        let vec_llama = vec![LlamaToken(10), LlamaToken(20), LlamaToken(30)];
135        let vec_sys = to_vec_token_sys(vec_llama);
136
137        // Ensure that the conversion works correctly
138        assert_eq!(vec_sys.len(), 3);
139        assert_eq!(vec_sys[0], 10);
140        assert_eq!(vec_sys[1], 20);
141        assert_eq!(vec_sys[2], 30);
142    }
143
144    #[test]
145    fn benchmark_to_vec_token_sys() {
146        // Benchmark the speed of to_vec_token_sys by timing it
147        let vec_llama: Vec<LlamaToken> = (0..100_000).map(LlamaToken::new).collect();
148
149        let start = Instant::now();
150        let _vec_sys = to_vec_token_sys(vec_llama);
151        let duration = start.elapsed();
152
153        println!("Time taken to convert Vec<LlamaToken> to Vec<llama_token>: {duration:?}");
154
155        // Here we can assert that the conversion took a reasonable amount of time.
156        // This threshold is arbitrary and can be adjusted according to expected performance.
157        assert!(duration.as_micros() < 1_000); // Ensure it takes less than 1ms
158    }
159}