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;
108#define MAX_SPECIAL_TOKENS 512
125 size_t vocab_capacity;
139 int num_special_tokens;
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;
391 bpe->special_tokens[i].token = NULL;
392 bpe->special_tokens[i].id = -1;
393 bpe->special_tokens[i].len = 0;
397 bpe->config.add_bos =
false;
398 bpe->config.add_eos =
false;
399 bpe->config.byte_fallback =
true;
417 if (bpe->id_to_token) {
418 for (
size_t i = 0; i < bpe->vocab_size; i++) {
419 if (bpe->id_to_token[i]) {
420 free(bpe->id_to_token[i]);
423 free(bpe->id_to_token);
426 if (bpe->str_buffer) {
427 free(bpe->str_buffer);
431 for (
int i = 0; i < bpe->num_special_tokens; i++) {
432 if (bpe->special_tokens[i].token) {
433 free(bpe->special_tokens[i].token);
451 if (!bpe || !
token)
return -1;
454 if (
id >= (int32_t)bpe->vocab_capacity) {
455 size_t new_cap = bpe->vocab_capacity * 2;
456 while (new_cap <= (
size_t)
id) new_cap *= 2;
458 char **new_array = (
char **)realloc(bpe->id_to_token, new_cap *
sizeof(
char *));
459 if (!new_array)
return -1;
461 memset(new_array + bpe->vocab_capacity, 0, (new_cap - bpe->vocab_capacity) *
sizeof(
char *));
462 bpe->id_to_token = new_array;
463 bpe->vocab_capacity = new_cap;
470 existing->score =
score;
471 if (bpe->id_to_token[
id]) free(bpe->id_to_token[
id]);
472 bpe->id_to_token[
id] = strdup(
token);
477 BPETokenInfo *info = (BPETokenInfo *)malloc(
sizeof(BPETokenInfo));
478 if (!info)
return -1;
488 if (
id >= (int32_t)bpe->vocab_size) {
489 bpe->vocab_size =
id + 1;
492 if (bpe->id_to_token[
id]) free(bpe->id_to_token[
id]);
493 bpe->id_to_token[
id] = strdup(
token);
522 if (!left_info || !right_info) {
527 size_t left_len = strlen(
left);
528 size_t right_len = strlen(
right);
529 size_t merged_len = left_len + right_len;
531 if (merged_len >= bpe->str_buffer_size) {
535 memcpy(bpe->str_buffer,
left, left_len);
536 memcpy(bpe->str_buffer + left_len,
right, right_len);
537 bpe->str_buffer[merged_len] =
'\0';
562 if (!bpe || !
config)
return;
567 if (!bpe || !
token ||
id < 0)
return -1;
570 int token_len = (int)strlen(
token);
571 if (token_len == 0)
return -1;
574 for (
int i = 0; i < bpe->num_special_tokens; i++) {
575 if (bpe->special_tokens[i].token &&
576 strcmp(bpe->special_tokens[i].token,
token) == 0) {
578 bpe->special_tokens[i].id =
id;
584 int insert_idx = bpe->num_special_tokens;
585 for (
int i = 0; i < bpe->num_special_tokens; i++) {
586 if (token_len > bpe->special_tokens[i].len) {
593 for (
int i = bpe->num_special_tokens; i > insert_idx; i--) {
594 bpe->special_tokens[i] = bpe->special_tokens[i - 1];
598 bpe->special_tokens[insert_idx].token = strdup(
token);
599 if (!bpe->special_tokens[insert_idx].token)
return -1;
600 bpe->special_tokens[insert_idx].id =
id;
601 bpe->special_tokens[insert_idx].len = token_len;
602 bpe->num_special_tokens++;
626 int32_t merged =
merges[i * 3 + 2];
627 if (
left < 0 ||
right < 0 || merged < 0) {
640 if (!bpe || !
token)
return -1;
642 return info ? info->id : -1;
647 if (
id >= 0)
return id;
648 return bpe ? bpe->unk_id : -1;
652 if (!bpe || id < 0 || id >= (int32_t)bpe->vocab_size)
return NULL;
653 return bpe->id_to_token[
id];
664 return bpe->config.space_prefix_style;
671 for (
size_t i = 0; i < bpe->vocab_size; i++) {
672 const char *
token = bpe->id_to_token[i];
673 if (!
token)
continue;
675 unsigned char c0 = (
unsigned char)
token[0];
676 unsigned char c1 = (
unsigned char)
token[1];
678 if (c0 == 0xC4 && c1 == 0xA0) {
680 }
else if (c0 == 0xE2 && c1 == 0x96 && (
unsigned char)
token[2] == 0x81) {
686 if (spm_count > gpt2_count * 2 && spm_count > 0) {
688 }
else if (gpt2_count > 0) {
694 bpe->config.space_prefix_style = detected;
720 return (
byte >= 0x21 &&
byte <= 0x7E) ||
721 (
byte >= 0xA1 &&
byte <= 0xAC) ||
722 (
byte >= 0xAE &&
byte <= 0xFF);
728 unsigned int mapped_index = 0;
729 for (
unsigned int candidate = 0; candidate < byte; candidate++) {
732 return 0x100 + mapped_index;
740 if (codepoint < 0x80) {
741 out[0] = (char)codepoint;
743 }
else if (codepoint < 0x800) {
744 out[0] = (char)(0xC0 | (codepoint >> 6));
745 out[1] = (char)(0x80 | (codepoint & 0x3F));
748 out[0] = (char)(0xE0 | (codepoint >> 12));
749 out[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
750 out[2] = (char)(0x80 | (codepoint & 0x3F));
768 if (
out_len + 3 > out_max)
return -1;
774 for (
int i = 0; i <
text_len; i++) {
775 unsigned char byte = (
unsigned char)
text[i];
780 if (
out_len + 3 > out_max)
return -1;
785 if (
out_len + 1 > out_max)
return -1;
792 if (
out_len + enc_len > out_max)
return -1;
793 for (
int j = 0; j < enc_len; j++) {
804 if ((c & 0x80) == 0)
return 1;
805 if ((c & 0xE0) == 0xC0)
return 2;
806 if ((c & 0xF0) == 0xE0)
return 3;
807 if ((c & 0xF8) == 0xF0)
return 4;
829 return (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z');
833 return c >=
'0' && c <=
'9';
837 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r';
842 return len >= 2 && (
unsigned char)s[0] == 0xC4 && (
unsigned char)s[1] == 0xA0;
848 unsigned char c = (
unsigned char)s[0];
853 if (len >= 2 && (
unsigned char)s[0] == 0xC4) {
854 unsigned char c1 = (
unsigned char)s[1];
855 if (c1 == 0xA0 || c1 == 0x8A || c1 == 0x89 || c1 == 0x8D) {
865 unsigned char c = (
unsigned char)s[0];
888 return len >= 2 && (
unsigned char)s[0] == 0xC4 && (
unsigned char)s[1] == 0x8A;
895 unsigned char c = (
unsigned char)s[0];
907 unsigned char c = (
unsigned char)s[0];
908 return !
is_letter(c) && !
is_digit(c) && c !=
' ' && c !=
'\t' && c !=
'\n' && c !=
'\r';
912 if (len >= 2 && (
unsigned char)s[0] == 0xC4) {
913 unsigned char c1 = (
unsigned char)s[1];
915 if (c1 == 0x8A || c1 == 0x89 || c1 == 0x8D)
return false;
941 while (pos <
text_len && num_chunks < max_chunks) {
942 int chunk_start = pos;
949 bool is_word =
false;
950 int word_start = pos;
960 int after = pos + char_len;
966 prefix_len = char_len;
973 pos = word_start + prefix_len;
982 chunks[num_chunks].start =
text + chunk_start;
983 chunks[num_chunks].len = pos - chunk_start;
992 chunks[num_chunks].start =
text + chunk_start;
993 chunks[num_chunks].len = pos - chunk_start;
1003 int punct_start = has_leading_space ? pos + 2 : pos;
1009 if (has_leading_space) {
1029 chunks[num_chunks].start =
text + chunk_start;
1030 chunks[num_chunks].len = pos - chunk_start;
1040 int space_count = 0;
1041 int space_end = pos;
1053 chunks[num_chunks].start =
text + pos;
1054 chunks[num_chunks].len = space_end - pos;
1066 if (space_count > 1) {
1067 chunks[num_chunks].start =
text + pos;
1068 chunks[num_chunks].len = (space_count - 1) * 2;
1071 pos += (space_count - 1) * 2;
1072 if (num_chunks >= max_chunks)
break;
1085 chunks[num_chunks].start =
text + chunk_start;
1086 chunks[num_chunks].len = pos - chunk_start;
1092 if (space_count > 1) {
1093 chunks[num_chunks].start =
text + pos;
1094 chunks[num_chunks].len = (space_count - 1) * 2;
1097 pos += (space_count - 1) * 2;
1098 if (num_chunks >= max_chunks)
break;
1103 chunks[num_chunks].start =
text + chunk_start;
1104 chunks[num_chunks].len = pos - chunk_start;
1113 chunks[num_chunks].start =
text + pos;
1114 chunks[num_chunks].len = space_count * 2;
1126 chunks[num_chunks].start =
text + chunk_start;
1127 chunks[num_chunks].len = pos - chunk_start;
1135 chunks[num_chunks].start =
text + chunk_start;
1136 chunks[num_chunks].len = pos - chunk_start;
1164 memcpy(char_buf,
text + pos, char_len);
1165 char_buf[char_len] =
'\0';
1181 size_t *best_pos,
const CKBPEMerge **best_merge) {
1184 int32_t best_priority = INT32_MAX;
1186 for (
size_t i = 0; i + 1 < list->count; i++) {
1187 int32_t
left_id = list->tokens[i].id;
1188 int32_t
right_id = list->tokens[i + 1].id;
1193 if (merge && merge->priority < best_priority) {
1194 best_priority = merge->priority;
1196 *best_merge = merge;
1200 return (*best_merge != NULL) ? 0 : -1;
1207 while (list->count > 1) {
1209 const CKBPEMerge *best_merge;
1216 const char *merged_str = bpe->id_to_token[best_merge->merged_id];
1219 size_t left_len = list->tokens[best_pos].len;
1220 size_t right_len = list->tokens[best_pos + 1].len;
1222 if (left_len + right_len >=
sizeof(merged_buf)) {
1226 memcpy(merged_buf, list->tokens[best_pos].str, left_len);
1227 memcpy(merged_buf + left_len, list->tokens[best_pos + 1].str, right_len);
1228 merged_buf[left_len + right_len] =
'\0';
1229 merged_str = merged_buf;
1233 if (
token_list_merge_at(list, best_pos, merged_str, strlen(merged_str), best_merge->merged_id) != 0) {
1245 int32_t *
ids,
int max_ids, CKBPETokenList *list) {
1246 if (chunk_len <= 0)
return 0;
1249 char chunk_buf[256];
1250 if (chunk_len < (
int)
sizeof(chunk_buf)) {
1251 memcpy(chunk_buf, chunk, chunk_len);
1252 chunk_buf[chunk_len] =
'\0';
1254 if (chunk_id >= 0) {
1275 for (
size_t i = 0; i < list->count && out_idx <
max_ids; i++) {
1276 int32_t
id = list->tokens[i].id;
1280 if (bpe->config.byte_fallback) {
1282 for (
size_t j = 0; j < list->tokens[i].len && out_idx <
max_ids; j++) {
1283 unsigned char raw_b = (
unsigned char)list->tokens[i].str[j];
1284 int32_t byte_id = -1;
1287 char raw_tok[2] = { (char)raw_b,
'\0' };
1297 snprintf(byte_token,
sizeof(byte_token),
"<0x%02X>", raw_b);
1304 if (mapped_len > 0 && mapped_len < (
int)
sizeof(mapped)) {
1305 mapped[mapped_len] =
'\0';
1310 ids[out_idx++] = (byte_id >= 0) ? byte_id : bpe->unk_id;
1313 ids[out_idx++] = bpe->unk_id;
1316 ids[out_idx++] =
id;
1331 char preprocessed[16384];
1336 preprocessed[pp_len] =
'\0';
1345 PretokChunk chunks[1024];
1347 preprocessed, pp_len, chunks, 1024, bpe->config.pretokenizer);
1351 if (!list)
return out_idx;
1354 for (
int c = 0; c < num_chunks && out_idx <
max_ids; c++) {
1357 out_idx += chunk_ids;
1364 if (!list)
return out_idx;
1366 int chunk_ids =
encode_chunk(bpe, preprocessed, pp_len,
1368 out_idx += chunk_ids;
1382 const char *cur =
text + pos;
1385 for (
int i = 0; i < bpe->num_special_tokens; i++) {
1386 int tok_len = bpe->special_tokens[i].len;
1387 if (tok_len <= remaining &&
1388 memcmp(cur, bpe->special_tokens[i].token, tok_len) == 0) {
1408 if (bpe->config.add_bos && bpe->bos_id >= 0 && out_idx <
max_ids) {
1409 ids[out_idx++] = bpe->bos_id;
1413 if (bpe->num_special_tokens == 0) {
1418 int segment_start = 0;
1425 if (pos > segment_start) {
1426 int seg_len = pos - segment_start;
1433 ids[out_idx++] = bpe->special_tokens[match].id;
1437 pos += bpe->special_tokens[match].len;
1438 segment_start = pos;
1453 if (bpe->config.add_eos && bpe->eos_id >= 0 && out_idx <
max_ids) {
1454 ids[out_idx++] = bpe->eos_id;
1466 if (!s || len <= 0 || !out_cp || !out_used)
return -1;
1467 unsigned char c0 = s[0];
1468 if ((c0 & 0x80) == 0) {
1473 if ((c0 & 0xE0) == 0xC0) {
1474 if (len < 2)
return -1;
1475 if ((s[1] & 0xC0) != 0x80)
return -1;
1476 *out_cp = ((int)(c0 & 0x1F) << 6) | (
int)(s[1] & 0x3F);
1480 if ((c0 & 0xF0) == 0xE0) {
1481 if (len < 3)
return -1;
1482 if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
return -1;
1483 *out_cp = ((int)(c0 & 0x0F) << 12) |
1484 ((
int)(s[1] & 0x3F) << 6) |
1489 if ((c0 & 0xF8) == 0xF0) {
1490 if (len < 4)
return -1;
1491 if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80 || (s[3] & 0xC0) != 0x80)
return -1;
1492 *out_cp = ((int)(c0 & 0x07) << 18) |
1493 ((
int)(s[1] & 0x3F) << 12) |
1494 ((int)(s[2] & 0x3F) << 6) |
1507 if (cp >= 0x100 && cp <= 0x143) {
1508 int mapped_index = cp - 0x100;
1509 for (
int byte = 0;
byte <= 0xFF;
byte++) {
1511 if (mapped_index-- == 0)
return byte;
1527 int32_t
id =
ids[i];
1528 if (
id < 0)
continue;
1531 if (
id == bpe->bos_id ||
id == bpe->eos_id ||
id == bpe->pad_id) {
1536 if (!
token)
continue;
1539 int token_len_ascii = (int)strlen(
token);
1540 for (
int j = 0; j < token_len_ascii && len <
max_len - 1; ) {
1541 if (j + 2 < token_len_ascii &&
1542 (
unsigned char)
token[j] == 0xE2 &&
1543 (
unsigned char)
token[j + 1] == 0x96 &&
1544 (
unsigned char)
token[j + 2] == 0x81) {
1554 int token_len = (int)strlen(
token);
1557 if (token_len >= 3 &&
1558 (
unsigned char)
token[0] == 0xE2 &&
1559 (
unsigned char)
token[1] == 0x96 &&
1560 (
unsigned char)
token[2] == 0x81) {
1569 while (pos < token_len && len <
max_len - 1) {
1578 text[len++] = (char)decoded;
1581 for (
int j = 0; j < used && pos + j < token_len && len <
max_len - 1; j++) {
1604 return bpe ? bpe->vocab_size : 0;
1608 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)
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)
static CKBPETokenList * token_list_create(size_t initial_capacity)
#define MAX_SPECIAL_TOKENS
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 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_GPT2
int const int32_t * offsets
int32_t int32_t int32_t merged_id
const char int int32_t int max_ids
const char const char * right