gigatoken 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +3016 -0
  3. data/Cargo.toml +135 -0
  4. data/LICENSE +21 -0
  5. data/README.md +141 -0
  6. data/exe/gigatoken +9 -0
  7. data/ext/gigatoken/Cargo.toml +24 -0
  8. data/ext/gigatoken/extconf.rb +19 -0
  9. data/ext/gigatoken/src/error.rs +20 -0
  10. data/ext/gigatoken/src/gvl.rs +122 -0
  11. data/ext/gigatoken/src/lib.rs +50 -0
  12. data/ext/gigatoken/src/sentencepiece.rs +205 -0
  13. data/ext/gigatoken/src/sources.rs +207 -0
  14. data/ext/gigatoken/src/tokenizer.rs +571 -0
  15. data/lib/gigatoken/cli/bench.rb +64 -0
  16. data/lib/gigatoken/cli/support.rb +132 -0
  17. data/lib/gigatoken/cli/validate.rb +55 -0
  18. data/lib/gigatoken/cli.rb +18 -0
  19. data/lib/gigatoken/hub.rb +242 -0
  20. data/lib/gigatoken/packed_result.rb +48 -0
  21. data/lib/gigatoken/tokenizer.rb +117 -0
  22. data/lib/gigatoken/version.rb +5 -0
  23. data/lib/gigatoken.rb +29 -0
  24. data/rust-toolchain.toml +8 -0
  25. data/src/batch.rs +1808 -0
  26. data/src/bindings/bridge.rs +396 -0
  27. data/src/bindings/hub.rs +42 -0
  28. data/src/bindings/matcher.rs +114 -0
  29. data/src/bindings/mod.rs +14 -0
  30. data/src/bindings/padding.rs +177 -0
  31. data/src/bindings/pretokenize.rs +53 -0
  32. data/src/bindings/sources.rs +273 -0
  33. data/src/bindings/train.rs +125 -0
  34. data/src/bpe/mod.rs +1217 -0
  35. data/src/bpe/pretoken_cache.rs +495 -0
  36. data/src/bpe/sentencepiece.rs +1485 -0
  37. data/src/bpe/tiktoken.rs +2555 -0
  38. data/src/bpe_train.rs +351 -0
  39. data/src/input/decompress.rs +11 -0
  40. data/src/input/file_source.rs +514 -0
  41. data/src/input/jsonl.rs +94 -0
  42. data/src/input/mod.rs +333 -0
  43. data/src/input/parquet.rs +303 -0
  44. data/src/lib.rs +578 -0
  45. data/src/load_tokenizer/hf.rs +1036 -0
  46. data/src/load_tokenizer/hub.rs +344 -0
  47. data/src/load_tokenizer/mod.rs +3 -0
  48. data/src/load_tokenizer/tiktoken.rs +87 -0
  49. data/src/main.rs +95 -0
  50. data/src/pretokenize/fast/cl100k.rs +426 -0
  51. data/src/pretokenize/fast/cl100k_family.rs +891 -0
  52. data/src/pretokenize/fast/deepseek_v3.rs +605 -0
  53. data/src/pretokenize/fast/kimi.rs +281 -0
  54. data/src/pretokenize/fast/mask.rs +1486 -0
  55. data/src/pretokenize/fast/mod.rs +446 -0
  56. data/src/pretokenize/fast/nemotron.rs +138 -0
  57. data/src/pretokenize/fast/o200k.rs +347 -0
  58. data/src/pretokenize/fast/o200k_family.rs +1734 -0
  59. data/src/pretokenize/fast/olmo3.rs +505 -0
  60. data/src/pretokenize/fast/qwen2.rs +429 -0
  61. data/src/pretokenize/fast/qwen3_5.rs +541 -0
  62. data/src/pretokenize/fast/r50k.rs +1250 -0
  63. data/src/pretokenize/mod.rs +1079 -0
  64. data/src/pretokenize/options.rs +188 -0
  65. data/src/pretokenize/pretoken.rs +20 -0
  66. data/src/pretokenize/pretokenize_traits.rs +49 -0
  67. data/src/pretokenize/reference/avx512.rs +522 -0
  68. data/src/pretokenize/reference/combinator.rs +572 -0
  69. data/src/pretokenize/reference/mod.rs +28 -0
  70. data/src/pretokenize/reference/simd.rs +852 -0
  71. data/src/pretokenize/reference/state_machine.rs +365 -0
  72. data/src/pretokenize/unicode.rs +546 -0
  73. data/src/test_hub.rs +28 -0
  74. data/src/token.rs +42 -0
  75. metadata +161 -0
@@ -0,0 +1,365 @@
1
+ //! Hand-rolled state-machine pretokenizer. Kept as a reference implementation
2
+ //! and benchmark baseline; the production pretokenizer is
3
+ //! `fast::r50k::FastR50kPretokenizer` (see `pretokenize_as_iter`).
4
+ use crate::input::DocRef;
5
+ use crate::pretokenize::pretoken::Pretoken;
6
+ use crate::pretokenize::unicode;
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // State-machine implementation
10
+ // ---------------------------------------------------------------------------
11
+
12
+ #[derive(Clone, Debug)]
13
+ pub enum PretokenizerState {
14
+ Start,
15
+ Nonchar,
16
+ Apostrophe,
17
+ AsciiSpace,
18
+ Whitespace(u8),
19
+ Letter,
20
+ Number,
21
+ Save,
22
+ Finish,
23
+ }
24
+
25
+ pub struct UTF8Iterator<'a> {
26
+ bytes: DocRef<'a>,
27
+ pos: usize,
28
+ }
29
+
30
+ enum StartResult {
31
+ Apostrophe,
32
+ Letter,
33
+ Number,
34
+ AsciiSpace,
35
+ Whitespace(u8),
36
+ Nonchar,
37
+ }
38
+
39
+ enum WhitespaceResult {
40
+ AsciiSpace,
41
+ Whitespace(u8),
42
+ Neither,
43
+ }
44
+
45
+ enum ApostropheResult {
46
+ Matched,
47
+ NotMatched,
48
+ }
49
+
50
+ pub(crate) struct OutOfBytesError {}
51
+
52
+ impl<'a> UTF8Iterator<'a> {
53
+ fn next_codepoint_and_length(&mut self) -> Option<(char, usize)> {
54
+ let cp = unsafe { str::from_utf8_unchecked(&self.bytes[self.pos..]) }
55
+ .chars()
56
+ .next()?;
57
+ let len = cp.len_utf8();
58
+ self.pos += len;
59
+ Some((cp, len))
60
+ }
61
+
62
+ fn start_check(&mut self) -> Result<StartResult, OutOfBytesError> {
63
+ if self.pos >= self.bytes.0.len() {
64
+ return Err(OutOfBytesError {});
65
+ }
66
+ let byte = self.bytes[self.pos];
67
+ if byte.is_ascii() {
68
+ self.pos += 1;
69
+ Ok(match byte {
70
+ b'A'..=b'Z' | b'a'..=b'z' => StartResult::Letter,
71
+ b' ' => StartResult::AsciiSpace,
72
+ 9..=13 => StartResult::Whitespace(1),
73
+ b'0'..=b'9' => StartResult::Number,
74
+ b'\'' => StartResult::Apostrophe,
75
+ _ => StartResult::Nonchar,
76
+ })
77
+ } else {
78
+ let (next_codepoint, len) =
79
+ self.next_codepoint_and_length().ok_or(OutOfBytesError {})?;
80
+ let gc = unicode::get_general_category(next_codepoint);
81
+ Ok(if unicode::is_gc_letter(gc) {
82
+ StartResult::Letter
83
+ } else if unicode::is_gc_number(gc) {
84
+ StartResult::Number
85
+ } else if unicode::is_whitespace(next_codepoint) {
86
+ StartResult::Whitespace(len as u8)
87
+ } else {
88
+ StartResult::Nonchar
89
+ })
90
+ }
91
+ }
92
+
93
+ fn whitespace_check(&mut self) -> Result<WhitespaceResult, OutOfBytesError> {
94
+ if self.pos >= self.bytes.len() {
95
+ return Err(OutOfBytesError {});
96
+ }
97
+ let byte = self.bytes[self.pos];
98
+ if byte.is_ascii() {
99
+ Ok(match byte {
100
+ b' ' => {
101
+ self.pos += 1;
102
+ WhitespaceResult::AsciiSpace
103
+ }
104
+ 9..=13 => {
105
+ self.pos += 1;
106
+ WhitespaceResult::Whitespace(1)
107
+ }
108
+ _ => WhitespaceResult::Neither,
109
+ })
110
+ } else {
111
+ let (next_codepoint, len) =
112
+ self.next_codepoint_and_length().ok_or(OutOfBytesError {})?;
113
+ Ok(if unicode::is_whitespace(next_codepoint) {
114
+ WhitespaceResult::Whitespace(len as u8)
115
+ } else {
116
+ self.pos -= len;
117
+ WhitespaceResult::Neither
118
+ })
119
+ }
120
+ }
121
+
122
+ fn letter_check(&mut self) -> Result<(), OutOfBytesError> {
123
+ loop {
124
+ if self.pos >= self.bytes.len() {
125
+ return Err(OutOfBytesError {});
126
+ }
127
+ let byte = self.bytes[self.pos];
128
+ if byte.is_ascii() {
129
+ match byte {
130
+ b'A'..=b'Z' | b'a'..=b'z' => {
131
+ self.pos += 1;
132
+ }
133
+ _ => {
134
+ return Ok(());
135
+ }
136
+ }
137
+ } else {
138
+ let (next_codepoint, len) =
139
+ self.next_codepoint_and_length().ok_or(OutOfBytesError {})?;
140
+ if !unicode::is_letter(next_codepoint) {
141
+ self.pos -= len;
142
+ return Ok(());
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ fn number_check(&mut self) -> Result<(), OutOfBytesError> {
149
+ loop {
150
+ if self.pos >= self.bytes.len() {
151
+ return Err(OutOfBytesError {});
152
+ }
153
+ let byte = self.bytes[self.pos];
154
+ if byte.is_ascii() {
155
+ match byte {
156
+ b'0'..=b'9' => {
157
+ self.pos += 1;
158
+ }
159
+ _ => {
160
+ return Ok(());
161
+ }
162
+ }
163
+ } else {
164
+ let (next_codepoint, len) =
165
+ self.next_codepoint_and_length().ok_or(OutOfBytesError {})?;
166
+ if !unicode::is_number(next_codepoint) {
167
+ self.pos -= len;
168
+ return Ok(());
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ fn other_check(&mut self) -> Result<(), OutOfBytesError> {
175
+ loop {
176
+ if self.pos >= self.bytes.len() {
177
+ return Err(OutOfBytesError {});
178
+ }
179
+ let byte = self.bytes[self.pos];
180
+ if byte.is_ascii() {
181
+ match byte {
182
+ b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b' ' | 9..=13 => {
183
+ return Ok(());
184
+ }
185
+ _ => {
186
+ self.pos += 1;
187
+ }
188
+ }
189
+ } else {
190
+ let (next_codepoint, len) =
191
+ self.next_codepoint_and_length().ok_or(OutOfBytesError {})?;
192
+ let gc = unicode::get_general_category(next_codepoint);
193
+ if unicode::is_gc_letter(gc)
194
+ || unicode::is_gc_number(gc)
195
+ || unicode::is_whitespace(next_codepoint)
196
+ {
197
+ self.pos -= len;
198
+ return Ok(());
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ fn apostrophe_check(&mut self) -> Result<ApostropheResult, OutOfBytesError> {
205
+ if self.pos >= self.bytes.len() {
206
+ return Err(OutOfBytesError {});
207
+ }
208
+ let byte = self.bytes[self.pos];
209
+ match byte {
210
+ b's' | b'd' | b'm' | b't' => {
211
+ self.pos += 1;
212
+ Ok(ApostropheResult::Matched)
213
+ }
214
+ b'l' | b'v' | b'r' => {
215
+ if self.pos + 1 >= self.bytes.len() {
216
+ return Ok(ApostropheResult::NotMatched);
217
+ }
218
+ let next_byte = self.bytes[self.pos + 1];
219
+ match (byte, next_byte) {
220
+ (b'l', b'l') | (b'v', b'e') | (b'r', b'e') => {
221
+ self.pos += 2;
222
+ Ok(ApostropheResult::Matched)
223
+ }
224
+ _ => Ok(ApostropheResult::NotMatched),
225
+ }
226
+ }
227
+ _ => Ok(ApostropheResult::NotMatched),
228
+ }
229
+ }
230
+ }
231
+
232
+ // ---------------------------------------------------------------------------
233
+ // PretokenizerIter — state-machine pretokenizer
234
+ // ---------------------------------------------------------------------------
235
+
236
+ pub struct PretokenizerIter<'a> {
237
+ bytes: &'a [u8],
238
+ pos: usize,
239
+ state: PretokenizerState,
240
+ }
241
+
242
+ impl<'a> PretokenizerIter<'a> {
243
+ pub fn new(input: &'a [u8]) -> PretokenizerIter<'a> {
244
+ PretokenizerIter {
245
+ bytes: input,
246
+ pos: 0,
247
+ state: PretokenizerState::Start,
248
+ }
249
+ }
250
+ }
251
+
252
+ impl<'a> Iterator for PretokenizerIter<'a> {
253
+ type Item = Pretoken<'a>;
254
+
255
+ fn next(&mut self) -> Option<Self::Item> {
256
+ self.next_state_machine()
257
+ }
258
+ }
259
+
260
+ impl<'a> PretokenizerIter<'a> {
261
+ #[inline]
262
+ fn next_state_machine(&mut self) -> Option<Pretoken<'a>> {
263
+ let mut iter = UTF8Iterator {
264
+ bytes: self.bytes.into(),
265
+ pos: self.pos,
266
+ };
267
+ let starting = self.pos;
268
+ let mut cur_starting = starting;
269
+
270
+ let (state_after, new_pretoken) = loop {
271
+ self.state = match self.state {
272
+ PretokenizerState::Start => match iter.start_check() {
273
+ Ok(StartResult::Apostrophe) => {
274
+ if cur_starting == iter.pos - 1 {
275
+ PretokenizerState::Apostrophe
276
+ } else {
277
+ PretokenizerState::Nonchar
278
+ }
279
+ }
280
+ Ok(StartResult::Letter) => PretokenizerState::Letter,
281
+ Ok(StartResult::Number) => PretokenizerState::Number,
282
+ Ok(StartResult::AsciiSpace) => PretokenizerState::AsciiSpace,
283
+ Ok(StartResult::Whitespace(wslen)) => PretokenizerState::Whitespace(wslen),
284
+ Ok(StartResult::Nonchar) => PretokenizerState::Nonchar,
285
+ Err(OutOfBytesError {}) => PretokenizerState::Finish,
286
+ },
287
+ PretokenizerState::Save => {
288
+ let saved_tokens = &self.bytes[cur_starting..iter.pos];
289
+ cur_starting = iter.pos;
290
+ break (PretokenizerState::Start, saved_tokens);
291
+ }
292
+ PretokenizerState::Apostrophe => match iter.apostrophe_check() {
293
+ Ok(ApostropheResult::Matched) => PretokenizerState::Save,
294
+ Ok(ApostropheResult::NotMatched) => PretokenizerState::Nonchar,
295
+ Err(OutOfBytesError {}) => PretokenizerState::Finish,
296
+ },
297
+ PretokenizerState::Nonchar => match iter.other_check() {
298
+ Ok(_) => PretokenizerState::Save,
299
+ Err(OutOfBytesError {}) => PretokenizerState::Finish,
300
+ },
301
+ PretokenizerState::Letter => match iter.letter_check() {
302
+ Ok(_) => PretokenizerState::Save,
303
+ Err(OutOfBytesError {}) => PretokenizerState::Finish,
304
+ },
305
+ PretokenizerState::Number => match iter.number_check() {
306
+ Ok(_) => PretokenizerState::Save,
307
+ Err(OutOfBytesError {}) => PretokenizerState::Finish,
308
+ },
309
+ PretokenizerState::Whitespace(prev_wslen) => match iter.whitespace_check() {
310
+ Ok(WhitespaceResult::AsciiSpace) => PretokenizerState::AsciiSpace,
311
+ Ok(WhitespaceResult::Whitespace(wslen)) => {
312
+ PretokenizerState::Whitespace(wslen)
313
+ }
314
+ Ok(WhitespaceResult::Neither) => {
315
+ let saved_token =
316
+ &self.bytes[cur_starting..iter.pos - (prev_wslen as usize)];
317
+ cur_starting = iter.pos - (prev_wslen as usize);
318
+ if saved_token.is_empty() {
319
+ PretokenizerState::Save
320
+ } else {
321
+ // The next token starts fresh at the reserved last
322
+ // whitespace char; resuming in `Save` would emit an
323
+ // empty span and end the stream.
324
+ break (PretokenizerState::Start, saved_token);
325
+ }
326
+ }
327
+ Err(OutOfBytesError {}) => PretokenizerState::Finish,
328
+ },
329
+ PretokenizerState::AsciiSpace => match iter.whitespace_check() {
330
+ Ok(WhitespaceResult::AsciiSpace) => PretokenizerState::AsciiSpace,
331
+ Ok(WhitespaceResult::Whitespace(wslen)) => {
332
+ PretokenizerState::Whitespace(wslen)
333
+ }
334
+ Ok(WhitespaceResult::Neither) => {
335
+ let saved_token = &self.bytes[cur_starting..iter.pos - 1];
336
+ if saved_token.is_empty() {
337
+ cur_starting = iter.pos - 1;
338
+ PretokenizerState::Start
339
+ } else {
340
+ cur_starting = iter.pos - 1;
341
+ break (PretokenizerState::Start, saved_token);
342
+ }
343
+ }
344
+ Err(OutOfBytesError {}) => {
345
+ let saved_token = &self.bytes[cur_starting..iter.pos];
346
+ cur_starting = iter.pos;
347
+ break (PretokenizerState::Finish, saved_token);
348
+ }
349
+ },
350
+ PretokenizerState::Finish => {
351
+ let saved_token = &self.bytes[cur_starting..iter.pos];
352
+ cur_starting = iter.pos;
353
+ break (PretokenizerState::Finish, saved_token);
354
+ }
355
+ }
356
+ };
357
+ self.state = state_after;
358
+ self.pos = cur_starting;
359
+ if new_pretoken.is_empty() {
360
+ return None;
361
+ }
362
+ Some(Pretoken(new_pretoken))
363
+ }
364
+ }
365
+