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