63#define MERGE_HASH_SIZE 65536
64#define INITIAL_TOKEN_CAPACITY 256
65#define MAX_TOKEN_LEN 128
80typedef struct CKMergeEntry {
83 struct CKMergeEntry *next;
88 CKMergeEntry **buckets;
123 size_t vocab_capacity;
136 CKSpecialToken *special_tokens;
137 int num_special_tokens;
138 size_t special_tokens_capacity;
142 bool suppress_dummy_prefix;
146 size_t str_buffer_size;
160 key *= 0xff51afd7ed558ccdULL;
162 key *= 0xc4ceb9fe1a85ec53ULL;
164 return key % num_buckets;
168 CKMergeTable *table = (CKMergeTable *)malloc(
sizeof(CKMergeTable));
169 if (!table)
return NULL;
171 table->buckets = (CKMergeEntry **)calloc(num_buckets,
sizeof(CKMergeEntry *));
172 if (!table->buckets) {
177 table->num_buckets = num_buckets;
178 table->num_entries = 0;
185 for (
size_t i = 0; i < table->num_buckets; i++) {
186 CKMergeEntry *entry = table->buckets[i];
188 CKMergeEntry *next = entry->next;
194 free(table->buckets);
199 uint64_t key =
merge_key(merge->left_id, merge->right_id);
200 size_t bucket =
merge_hash(key, table->num_buckets);
203 CKMergeEntry *entry = table->buckets[bucket];
205 if (entry->key == key) {
207 entry->merge = *merge;
214 entry = (CKMergeEntry *)malloc(
sizeof(CKMergeEntry));
215 if (!entry)
return -1;
218 entry->merge = *merge;
219 entry->next = table->buckets[bucket];
220 table->buckets[bucket] = entry;
221 table->num_entries++;
228 size_t bucket =
merge_hash(key, table->num_buckets);
230 CKMergeEntry *entry = table->buckets[bucket];
232 if (entry->key == key) {
233 return &entry->merge;
246 CKBPETokenList *list = (CKBPETokenList *)malloc(
sizeof(CKBPETokenList));
247 if (!list)
return NULL;
249 list->tokens = (CKBPEToken *)calloc(initial_capacity,
sizeof(CKBPEToken));
256 list->capacity = initial_capacity;
263 for (
size_t i = 0; i < list->count; i++) {
264 if (list->tokens[i].str) {
265 free(list->tokens[i].str);
274 for (
size_t i = 0; i < list->count; i++) {
275 if (list->tokens[i].str) {
276 free(list->tokens[i].str);
277 list->tokens[i].str = NULL;
284 if (list->count >= list->capacity) {
285 size_t new_cap = list->capacity * 2;
286 CKBPEToken *new_tokens = (CKBPEToken *)realloc(list->tokens, new_cap *
sizeof(CKBPEToken));
287 if (!new_tokens)
return -1;
288 list->tokens = new_tokens;
289 list->capacity = new_cap;
291 memset(list->tokens + list->count, 0, (new_cap - list->count) *
sizeof(CKBPEToken));
294 CKBPEToken *tok = &list->tokens[list->count];
295 tok->str = (
char *)malloc(len + 1);
296 if (!tok->str)
return -1;
298 memcpy(tok->str, str, len);
299 tok->str[len] =
'\0';
300 tok->len = (uint16_t)len;
302 tok->is_merged =
false;
310 if (pos + 1 >= list->count)
return -1;
313 free(list->tokens[pos].str);
314 free(list->tokens[pos + 1].str);
317 list->tokens[pos].str = (
char *)malloc(merged_len + 1);
318 if (!list->tokens[pos].str)
return -1;
320 memcpy(list->tokens[pos].str, merged_str, merged_len);
321 list->tokens[pos].str[merged_len] =
'\0';
322 list->tokens[pos].len = (uint16_t)merged_len;
324 list->tokens[pos].is_merged =
true;
327 for (
size_t i = pos + 1; i < list->count - 1; i++) {
328 list->tokens[i] = list->tokens[i + 1];
333 list->tokens[list->count].str = NULL;
343 CKTrueBPE *bpe = (CKTrueBPE *)calloc(1,
sizeof(CKTrueBPE));
344 if (!bpe)
return NULL;
362 bpe->vocab_capacity = 4096;
363 bpe->id_to_token = (
char **)calloc(bpe->vocab_capacity,
sizeof(
char *));
364 if (!bpe->id_to_token) {
372 bpe->str_buffer_size = 4096;
373 bpe->str_buffer = (
char *)malloc(bpe->str_buffer_size);
374 if (!bpe->str_buffer) {
375 free(bpe->id_to_token);
389 bpe->num_special_tokens = 0;
390 bpe->special_tokens = NULL;
391 bpe->special_tokens_capacity = 0;
394 bpe->config.add_bos =
false;
395 bpe->config.add_eos =
false;
396 bpe->config.byte_fallback =
true;
414 if (bpe->id_to_token) {
415 for (
size_t i = 0; i < bpe->vocab_size; i++) {
416 if (bpe->id_to_token[i]) {
417 free(bpe->id_to_token[i]);
420 free(bpe->id_to_token);
423 if (bpe->str_buffer) {
424 free(bpe->str_buffer);
428 for (
int i = 0; i < bpe->num_special_tokens; i++) {
429 if (bpe->special_tokens[i].token) {
430 free(bpe->special_tokens[i].token);
433 free(bpe->special_tokens);
449 if (!bpe || !
token)
return -1;
452 if (
id >= (int32_t)bpe->vocab_capacity) {
453 size_t new_cap = bpe->vocab_capacity * 2;
454 while (new_cap <= (
size_t)
id) new_cap *= 2;
456 char **new_array = (
char **)realloc(bpe->id_to_token, new_cap *
sizeof(
char *));
457 if (!new_array)
return -1;
459 memset(new_array + bpe->vocab_capacity, 0, (new_cap - bpe->vocab_capacity) *
sizeof(
char *));
460 bpe->id_to_token = new_array;
461 bpe->vocab_capacity = new_cap;
468 existing->score =
score;
469 if (bpe->id_to_token[
id]) free(bpe->id_to_token[
id]);
470 bpe->id_to_token[
id] = strdup(
token);
475 BPETokenInfo *info = (BPETokenInfo *)malloc(
sizeof(BPETokenInfo));
476 if (!info)
return -1;
486 if (
id >= (int32_t)bpe->vocab_size) {
487 bpe->vocab_size =
id + 1;
490 if (bpe->id_to_token[
id]) free(bpe->id_to_token[
id]);
491 bpe->id_to_token[
id] = strdup(
token);
520 if (!left_info || !right_info) {
525 size_t left_len = strlen(
left);
526 size_t right_len = strlen(
right);
527 size_t merged_len = left_len + right_len;
529 if (merged_len >= bpe->str_buffer_size) {
533 memcpy(bpe->str_buffer,
left, left_len);
534 memcpy(bpe->str_buffer + left_len,
right, right_len);
535 bpe->str_buffer[merged_len] =
'\0';
560 if (!bpe || !
config)
return;
565 if (bpe) bpe->suppress_dummy_prefix =
suppress;
569 if (!bpe || needed > INT_MAX || needed > SIZE_MAX /
sizeof(CKSpecialToken))
return -1;
570 if (needed <= bpe->special_tokens_capacity)
return 0;
571 size_t capacity = bpe->special_tokens_capacity ? bpe->special_tokens_capacity : 32;
572 while (capacity < needed) {
573 if (capacity > INT_MAX / 2) {
579 CKSpecialToken *items = realloc(bpe->special_tokens, capacity *
sizeof(*items));
580 if (!items)
return -1;
581 bpe->special_tokens = items;
582 bpe->special_tokens_capacity = capacity;
587 if (!bpe || !
token ||
id < 0)
return -1;
589 int token_len = (int)strlen(
token);
590 if (token_len == 0)
return -1;
593 for (
int i = 0; i < bpe->num_special_tokens; i++) {
594 if (bpe->special_tokens[i].token &&
595 strcmp(bpe->special_tokens[i].token,
token) == 0) {
597 bpe->special_tokens[i].id =
id;
603 char *copy = strdup(
token);
604 if (!copy)
return -1;
607 int insert_idx = bpe->num_special_tokens;
608 for (
int i = 0; i < bpe->num_special_tokens; i++) {
609 if (token_len > bpe->special_tokens[i].len) {
616 for (
int i = bpe->num_special_tokens; i > insert_idx; i--) {
617 bpe->special_tokens[i] = bpe->special_tokens[i - 1];
621 bpe->special_tokens[insert_idx].token = copy;
622 bpe->special_tokens[insert_idx].id =
id;
623 bpe->special_tokens[insert_idx].len = token_len;
624 bpe->num_special_tokens++;
648 int32_t merged =
merges[i * 3 + 2];
649 if (
left < 0 ||
right < 0 || merged < 0) {
662 const uint8_t *
types,
664 if (!bpe || !
types ||
count != bpe->vocab_size)
return -1;
666 for (
size_t id = 0;
id <
count;
id++) {
667 if (
types[
id] != 3 &&
types[
id] != 4)
continue;
668 const char *
token = bpe->id_to_token[
id];
672 if (needed > (
size_t)INT_MAX - (
size_t)bpe->num_special_tokens ||
674 for (
size_t id = 0;
id <
count;
id++) {
675 if (
types[
id] != 3 &&
types[
id] != 4)
continue;
682 if (!bpe || !
token)
return -1;
684 return info ? info->id : -1;
689 if (
id >= 0)
return id;
690 return bpe ? bpe->unk_id : -1;
694 if (!bpe || id < 0 || id >= (int32_t)bpe->vocab_size)
return NULL;
695 return bpe->id_to_token[
id];
706 return bpe->config.space_prefix_style;
713 for (
size_t i = 0; i < bpe->vocab_size; i++) {
714 const char *
token = bpe->id_to_token[i];
715 if (!
token)
continue;
717 unsigned char c0 = (
unsigned char)
token[0];
718 unsigned char c1 = (
unsigned char)
token[1];
720 if (c0 == 0xC4 && c1 == 0xA0) {
722 }
else if (c0 == 0xE2 && c1 == 0x96 && (
unsigned char)
token[2] == 0x81) {
728 if (spm_count > gpt2_count * 2 && spm_count > 0) {
730 }
else if (gpt2_count > 0) {
736 bpe->config.space_prefix_style = detected;
762 return (
byte >= 0x21 &&
byte <= 0x7E) ||
763 (
byte >= 0xA1 &&
byte <= 0xAC) ||
764 (
byte >= 0xAE &&
byte <= 0xFF);
770 unsigned int mapped_index = 0;
771 for (
unsigned int candidate = 0; candidate < byte; candidate++) {
774 return 0x100 + mapped_index;
782 if (codepoint < 0x80) {
783 out[0] = (char)codepoint;
785 }
else if (codepoint < 0x800) {
786 out[0] = (char)(0xC0 | (codepoint >> 6));
787 out[1] = (char)(0x80 | (codepoint & 0x3F));
790 out[0] = (char)(0xE0 | (codepoint >> 12));
791 out[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
792 out[2] = (char)(0x80 | (codepoint & 0x3F));
811 if (
out_len + 3 > out_max)
return -1;
817 for (
int i = 0; i <
text_len; i++) {
818 unsigned char byte = (
unsigned char)
text[i];
823 if (
out_len + 3 > out_max)
return -1;
828 if (
out_len + 1 > out_max)
return -1;
835 if (
out_len + enc_len > out_max)
return -1;
836 for (
int j = 0; j < enc_len; j++) {
847 if ((c & 0x80) == 0)
return 1;
848 if ((c & 0xE0) == 0xC0)
return 2;
849 if ((c & 0xF0) == 0xE0)
return 3;
850 if ((c & 0xF8) == 0xF0)
return 4;
872 return (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z');
876 return c >=
'0' && c <=
'9';
880 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r';
885 return len >= 2 && (
unsigned char)s[0] == 0xC4 && (
unsigned char)s[1] == 0xA0;
891 unsigned char c = (
unsigned char)s[0];
896 if (len >= 2 && (
unsigned char)s[0] == 0xC4) {
897 unsigned char c1 = (
unsigned char)s[1];
898 if (c1 == 0xA0 || c1 == 0x8A || c1 == 0x89 || c1 == 0x8D) {
908 unsigned char c = (
unsigned char)s[0];
917 static const char *suffixes[] = {
"s",
"t",
"re",
"ve",
"m",
"ll",
"d"};
918 if (available < 2 || s[0] !=
'\'')
return 0;
919 for (
size_t i = 0; i <
sizeof(suffixes) /
sizeof(suffixes[0]); ++i) {
920 const char *suffix = suffixes[i];
921 size_t length = strlen(suffix);
922 if ((
size_t)available < length + 1)
continue;
924 for (
size_t j = 0; j < length; ++j) {
925 unsigned char c = (
unsigned char)s[j + 1];
926 if (c >=
'A' && c <=
'Z') c = (
unsigned char)(c + (
'a' -
'A'));
927 if (c != (
unsigned char)suffix[j]) {
932 if (matches)
return (
int)length + 1;
954 return len >= 2 && (
unsigned char)s[0] == 0xC4 && (
unsigned char)s[1] == 0x8A;
961 unsigned char c = (
unsigned char)s[0];
973 unsigned char c = (
unsigned char)s[0];
974 return !
is_letter(c) && !
is_digit(c) && c !=
' ' && c !=
'\t' && c !=
'\n' && c !=
'\r';
978 if (len >= 2 && (
unsigned char)s[0] == 0xC4) {
979 unsigned char c1 = (
unsigned char)s[1];
981 if (c1 == 0x8A || c1 == 0x89 || c1 == 0x8D)
return false;
1007 while (pos <
text_len && num_chunks < max_chunks) {
1008 int chunk_start = pos;
1015 bool is_word =
false;
1016 int word_start = pos;
1025 int after = pos + char_len;
1031 prefix_len = char_len;
1038 pos = word_start + prefix_len;
1049 chunks[num_chunks].start =
text + chunk_start;
1050 chunks[num_chunks].len = pos - chunk_start;
1061 for (
int digits = 1; digits < 3 && pos <
text_len; ++digits) {
1068 chunks[num_chunks].start =
text + chunk_start;
1069 chunks[num_chunks].len = pos - chunk_start;
1079 int punct_start = has_leading_space ? pos + 2 : pos;
1085 if (has_leading_space) {
1113 chunks[num_chunks].start =
text + chunk_start;
1114 chunks[num_chunks].len = pos - chunk_start;
1124 int space_count = 0;
1125 int space_end = pos;
1137 chunks[num_chunks].start =
text + pos;
1138 chunks[num_chunks].len = space_end - pos;
1152 chunks[num_chunks].start =
text + pos;
1153 chunks[num_chunks].len = (space_count - 1) * 2;
1156 pos += (space_count - 1) * 2;
1161 if (space_count > 1) {
1162 chunks[num_chunks].start =
text + pos;
1163 chunks[num_chunks].len = (space_count - 1) * 2;
1166 pos += (space_count - 1) * 2;
1167 if (num_chunks >= max_chunks)
break;
1182 chunks[num_chunks].start =
text + chunk_start;
1183 chunks[num_chunks].len = pos - chunk_start;
1190 if (space_count > 1) {
1191 chunks[num_chunks].start =
text + pos;
1192 chunks[num_chunks].len = (space_count - 1) * 2;
1195 pos += (space_count - 1) * 2;
1196 if (num_chunks >= max_chunks)
break;
1201 chunks[num_chunks].start =
text + chunk_start;
1202 chunks[num_chunks].len = pos - chunk_start;
1211 chunks[num_chunks].start =
text + pos;
1212 chunks[num_chunks].len = space_count * 2;
1224 chunks[num_chunks].start =
text + chunk_start;
1225 chunks[num_chunks].len = pos - chunk_start;
1233 chunks[num_chunks].start =
text + chunk_start;
1234 chunks[num_chunks].len = pos - chunk_start;
1262 memcpy(char_buf,
text + pos, char_len);
1263 char_buf[char_len] =
'\0';
1279 size_t *best_pos,
const CKBPEMerge **best_merge) {
1282 int32_t best_priority = INT32_MAX;
1284 for (
size_t i = 0; i + 1 < list->count; i++) {
1285 int32_t
left_id = list->tokens[i].id;
1286 int32_t
right_id = list->tokens[i + 1].id;
1291 if (merge && merge->priority < best_priority) {
1292 best_priority = merge->priority;
1294 *best_merge = merge;
1298 return (*best_merge != NULL) ? 0 : -1;
1305 while (list->count > 1) {
1307 const CKBPEMerge *best_merge;
1314 const char *merged_str = bpe->id_to_token[best_merge->merged_id];
1317 size_t left_len = list->tokens[best_pos].len;
1318 size_t right_len = list->tokens[best_pos + 1].len;
1320 if (left_len + right_len >=
sizeof(merged_buf)) {
1324 memcpy(merged_buf, list->tokens[best_pos].str, left_len);
1325 memcpy(merged_buf + left_len, list->tokens[best_pos + 1].str, right_len);
1326 merged_buf[left_len + right_len] =
'\0';
1327 merged_str = merged_buf;
1331 if (
token_list_merge_at(list, best_pos, merged_str, strlen(merged_str), best_merge->merged_id) != 0) {
1343 int32_t *
ids,
int max_ids, CKBPETokenList *list) {
1344 if (chunk_len <= 0)
return 0;
1347 char chunk_buf[256];
1348 if (chunk_len < (
int)
sizeof(chunk_buf)) {
1349 memcpy(chunk_buf, chunk, chunk_len);
1350 chunk_buf[chunk_len] =
'\0';
1352 if (chunk_id >= 0) {
1373 for (
size_t i = 0; i < list->count; i++) {
1374 int32_t
id = list->tokens[i].id;
1378 if (bpe->config.byte_fallback) {
1380 for (
size_t j = 0; j < list->tokens[i].len; j++) {
1381 if (out_idx >=
max_ids)
return -1;
1382 unsigned char raw_b = (
unsigned char)list->tokens[i].str[j];
1383 int32_t byte_id = -1;
1386 char raw_tok[2] = { (char)raw_b,
'\0' };
1396 snprintf(byte_token,
sizeof(byte_token),
"<0x%02X>", raw_b);
1403 if (mapped_len > 0 && mapped_len < (
int)
sizeof(mapped)) {
1404 mapped[mapped_len] =
'\0';
1409 ids[out_idx++] = (byte_id >= 0) ? byte_id : bpe->unk_id;
1412 if (out_idx >=
max_ids)
return -1;
1413 ids[out_idx++] = bpe->unk_id;
1416 if (out_idx >=
max_ids)
return -1;
1417 ids[out_idx++] =
id;
1433 if (
text_len > (INT_MAX - 4) / 3)
return -1;
1434 size_t pp_capacity = (size_t)
text_len * 3 + 4;
1435 char *preprocessed = malloc(pp_capacity);
1436 if (!preprocessed)
return -1;
1442 preprocessed[pp_len] =
'\0';
1452 if ((
size_t)pp_len > SIZE_MAX /
sizeof(PretokChunk)) {
1456 PretokChunk *chunks = malloc((
size_t)pp_len *
sizeof(*chunks));
1462 preprocessed, pp_len, chunks, pp_len, bpe->config.pretokenizer);
1473 for (
int c = 0; c < num_chunks; c++) {
1476 if (chunk_ids < 0) {
1482 out_idx += chunk_ids;
1495 int chunk_ids =
encode_chunk(bpe, preprocessed, pp_len,
1497 if (chunk_ids < 0) {
1502 out_idx += chunk_ids;
1517 const char *cur =
text + pos;
1520 for (
int i = 0; i < bpe->num_special_tokens; i++) {
1521 int tok_len = bpe->special_tokens[i].len;
1522 if (tok_len <= remaining &&
1523 memcmp(cur, bpe->special_tokens[i].token, tok_len) == 0) {
1533 size_t inferred_len = strlen(
text);
1534 if (inferred_len > INT_MAX)
return -1;
1549 bool explicit_bos = leading_special >= 0 &&
1550 bpe->special_tokens[leading_special].id == bpe->bos_id;
1551 if (bpe->config.add_bos && bpe->bos_id >= 0 && !explicit_bos) {
1552 if (out_idx >=
max_ids)
return -1;
1553 ids[out_idx++] = bpe->bos_id;
1557 if (bpe->num_special_tokens == 0) {
1559 if (segment_count < 0)
return -1;
1560 out_idx += segment_count;
1564 int segment_start = 0;
1571 if (pos > segment_start) {
1572 int seg_len = pos - segment_start;
1575 if (segment_count < 0)
return -1;
1576 out_idx += segment_count;
1580 if (out_idx >=
max_ids)
return -1;
1581 ids[out_idx++] = bpe->special_tokens[match].id;
1584 pos += bpe->special_tokens[match].len;
1585 segment_start = pos;
1596 if (segment_count < 0)
return -1;
1597 out_idx += segment_count;
1602 if (bpe->config.add_eos && bpe->eos_id >= 0) {
1603 if (out_idx >=
max_ids)
return -1;
1604 ids[out_idx++] = bpe->eos_id;
1616 if (!s || len <= 0 || !out_cp || !out_used)
return -1;
1617 unsigned char c0 = s[0];
1618 if ((c0 & 0x80) == 0) {
1623 if ((c0 & 0xE0) == 0xC0) {
1624 if (len < 2)
return -1;
1625 if ((s[1] & 0xC0) != 0x80)
return -1;
1626 *out_cp = ((int)(c0 & 0x1F) << 6) | (
int)(s[1] & 0x3F);
1630 if ((c0 & 0xF0) == 0xE0) {
1631 if (len < 3)
return -1;
1632 if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
return -1;
1633 *out_cp = ((int)(c0 & 0x0F) << 12) |
1634 ((
int)(s[1] & 0x3F) << 6) |
1639 if ((c0 & 0xF8) == 0xF0) {
1640 if (len < 4)
return -1;
1641 if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80 || (s[3] & 0xC0) != 0x80)
return -1;
1642 *out_cp = ((int)(c0 & 0x07) << 18) |
1643 ((
int)(s[1] & 0x3F) << 12) |
1644 ((int)(s[2] & 0x3F) << 6) |
1657 if (cp >= 0x100 && cp <= 0x143) {
1658 int mapped_index = cp - 0x100;
1659 for (
int byte = 0;
byte <= 0xFF;
byte++) {
1661 if (mapped_index-- == 0)
return byte;
1677 int32_t
id =
ids[i];
1678 if (
id < 0)
continue;
1681 if (
id == bpe->bos_id ||
id == bpe->eos_id ||
id == bpe->pad_id) {
1686 if (!
token)
continue;
1689 int token_len_ascii = (int)strlen(
token);
1690 for (
int j = 0; j < token_len_ascii && len <
max_len - 1; ) {
1691 if (j + 2 < token_len_ascii &&
1692 (
unsigned char)
token[j] == 0xE2 &&
1693 (
unsigned char)
token[j + 1] == 0x96 &&
1694 (
unsigned char)
token[j + 2] == 0x81) {
1704 int token_len = (int)strlen(
token);
1707 if (token_len >= 3 &&
1708 (
unsigned char)
token[0] == 0xE2 &&
1709 (
unsigned char)
token[1] == 0x96 &&
1710 (
unsigned char)
token[2] == 0x81) {
1719 while (pos < token_len && len <
max_len - 1) {
1728 text[len++] = (char)decoded;
1731 for (
int j = 0; j < used && pos + j < token_len && len <
max_len - 1; j++) {
1754 return bpe ? bpe->vocab_size : 0;
1758 return bpe ? bpe->num_merges : 0;
#define CK_TOKENIZER_HT_BUCKETS_LARGE
void ck_tokenizer_hash_table_free(CKTokenizerHashTable *table, bool free_values)
int ck_tokenizer_hash_table_insert(CKTokenizerHashTable *table, const char *key, void *value)
CKTokenizerHashTable * ck_tokenizer_hash_table_create(size_t bucket_count)
void * ck_tokenizer_hash_table_lookup(CKTokenizerHashTable *table, const char *key)
const int32_t int num_ids
int32_t int32_t int32_t eos
int32_t int32_t int32_t int32_t pad
const int32_t int int * out_len
static bool is_bpe_punct(const char *s, int len)
static bool gpt2_byte_is_identity(unsigned int byte)
static const CKBPEMerge * merge_table_lookup(const CKMergeTable *table, int32_t left_id, int32_t right_id)
static int gpt2_pretokenize(const char *text, int text_len, PretokChunk *chunks, int max_chunks, CKBPEPretokenizer pretokenizer)
int ck_true_bpe_decode(const CKTrueBPE *bpe, const int32_t *ids, int num_ids, char *text, int max_len)
static bool is_gpt2_space(const char *s, int len)
int ck_true_bpe_encode(CKTrueBPE *bpe, const char *text, int text_len, int32_t *ids, int max_ids)
static bool is_letter(unsigned char c)
int ck_true_bpe_register_token_types(CKTrueBPE *bpe, const uint8_t *types, size_t count)
static int encode_text_segment(CKTrueBPE *bpe, const char *text, int text_len, int32_t *ids, int max_ids)
static CKMergeTable * merge_table_create(size_t num_buckets)
void ck_true_bpe_set_config(CKTrueBPE *bpe, const CKBPEConfig *config)
static void merge_table_free(CKMergeTable *table)
CKSpacePrefixStyle ck_true_bpe_detect_space_style(CKTrueBPE *bpe)
void ck_true_bpe_set_suppress_dummy_prefix(CKTrueBPE *bpe, bool suppress)
static CKBPETokenList * token_list_create(size_t initial_capacity)
void ck_true_bpe_free(CKTrueBPE *bpe)
void ck_true_bpe_set_special_ids(CKTrueBPE *bpe, int32_t unk, int32_t bos, int32_t eos, int32_t pad)
static bool is_bpe_letter(const char *s, int len)
static int init_tokens_from_text(CKTrueBPE *bpe, CKBPETokenList *list, const char *text, int text_len)
static bool is_bpe_newline(const char *s, int len)
static bool is_bpe_digit(const char *s, int len)
int ck_true_bpe_add_merge(CKTrueBPE *bpe, int32_t left_id, int32_t right_id, int32_t merged_id, int32_t priority)
static void token_list_clear(CKBPETokenList *list)
static int encode_chunk(CKTrueBPE *bpe, const char *chunk, int chunk_len, int32_t *ids, int max_ids, CKBPETokenList *list)
CKTrueBPE * ck_true_bpe_create(void)
static int utf8_char_len(unsigned char c)
static void token_list_free(CKBPETokenList *list)
static int preprocess_text(const CKTrueBPE *bpe, const char *text, int text_len, char *out, int out_max)
static int token_list_append(CKBPETokenList *list, const char *str, size_t len, int32_t id)
static bool is_whitespace(unsigned char c)
int32_t ck_true_bpe_num_merges(const CKTrueBPE *bpe)
int ck_true_bpe_add_special_token(CKTrueBPE *bpe, const char *token, int32_t id)
const char * ck_true_bpe_id_to_token(const CKTrueBPE *bpe, int32_t id)
static int decode_utf8_scalar(const unsigned char *s, int len, int *out_cp, int *out_used)
static int match_special_token(const CKTrueBPE *bpe, const char *text, int text_len, int pos)
int ck_true_bpe_load_binary(CKTrueBPE *bpe, int vocab_size, const int32_t *offsets, const char *strings, int num_merges, const int32_t *merges)
static size_t merge_hash(uint64_t key, size_t num_buckets)
static int apply_bpe_merges(CKTrueBPE *bpe, CKBPETokenList *list)
static int reserve_special_tokens(CKTrueBPE *bpe, size_t needed)
static int isolated_contraction_len(const char *s, int available)
static int find_best_merge(const CKTrueBPE *bpe, const CKBPETokenList *list, size_t *best_pos, const CKBPEMerge **best_merge)
static bool is_word_prefix_char(const char *s, int len)
static int merge_table_insert(CKMergeTable *table, const CKBPEMerge *merge)
size_t ck_true_bpe_vocab_size(const CKTrueBPE *bpe)
int ck_true_bpe_add_token(CKTrueBPE *bpe, const char *token, int32_t id, float score)
static uint64_t merge_key(int32_t left_id, int32_t right_id)
static int gpt2_codepoint_to_byte(int cp)
static bool is_digit(unsigned char c)
static int32_t lookup_token_exact(const CKTrueBPE *bpe, const char *token)
static int byte_to_gpt2(unsigned char byte, char *out)
static unsigned int gpt2_byte_to_codepoint(unsigned int byte)
static int token_list_merge_at(CKBPETokenList *list, size_t pos, const char *merged_str, size_t merged_len, int32_t merged_id)
int32_t ck_true_bpe_lookup(const CKTrueBPE *bpe, const char *token)
int ck_true_bpe_add_merge_by_tokens(CKTrueBPE *bpe, const char *left, const char *right, int32_t priority)
#define INITIAL_TOKEN_CAPACITY
const CKBPEConfig * config
int const int32_t const char int num_merges
int const int32_t const char * strings
int const int32_t const char int const int32_t * merges
int32_t int32_t int32_t int32_t priority
const int32_t int char int max_len
@ CK_BPE_PRETOKENIZER_UNICODE_SPLIT_ISOLATED
@ CK_BPE_PRETOKENIZER_GPT2
int const int32_t * offsets
const uint8_t size_t count
int32_t int32_t int32_t merged_id
const char int int32_t int max_ids
const char const char * right