Files
2026-10-15 23:13:11 +03:00

774 lines
35 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* tokenizer.c - embedded Tencent Hy3 ByteLevel BPE tokenizer (pure C, no deps).
*
* Counts tokens for arbitrary UTF-8 text using the official Tencent Hy3
* (tencent/Hy3, HYV3ForCausalLM) 120k ByteLevel BPE vocabulary (embedded
* in tokenizer_data.c) and its 818 added special/control tokens.
*
* The pre-tokenizer implements Hy3's 4-stage Sequence pre-tokenizer:
* 1) Split(Isolated): \p{N}{1,3}
* 2) Split(Isolated): [\u4e00-\u9fa5\u3040-\u309f\u30a0-\u30ff]+
* 3) Split(Isolated): [!"#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~][A-Za-z]+
* |[^\r\n\p{L}\p{P}\p{S}]?[\p{L}\p{M}]+
* | ?[\p{P}\p{S}]+[\r\n]*
* |\s*[\r\n]+|\s+(?!\S)|\s+
* 4) ByteLevel(add_prefix_space=false, use_regex=false)
*/
#include "hygate.h"
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
extern const int tok_ntok;
extern const unsigned char tok_blob[];
extern const uint16_t tok_len[];
/* ----------------------------- vocab hash ----------------------------- */
static pthread_once_t tok_once = PTHREAD_ONCE_INIT;
static const char **tok_keys = NULL;
static int *tok_ranks = NULL;
static uint16_t *tok_klen = NULL;
static unsigned int tok_mask = 0;
static uint64_t tok_fnv1a(const unsigned char *s, int len) {
uint64_t h = 1469598103934665603ULL;
for (int i = 0; i < len; i++) {
h ^= (uint64_t)s[i];
h *= 1099511628211ULL;
}
return h;
}
static void tok_init(void) {
unsigned int size = 1;
while (size < (unsigned int)tok_ntok * 2u) size <<= 1;
const char **keys = (const char **)calloc(size, sizeof(char *));
int *ranks = (int *)calloc(size, sizeof(int));
uint16_t *klen = (uint16_t *)calloc(size, sizeof(uint16_t));
if (!keys || !ranks || !klen) {
free(keys);
free(ranks);
free(klen);
return;
}
unsigned int mask = size - 1;
int offset = 0;
for (int i = 0; i < tok_ntok; i++) {
uint16_t kl = tok_len[i];
const char *key = (const char *)(tok_blob + offset);
unsigned int h = (unsigned int)tok_fnv1a((const unsigned char *)key, kl) & mask;
while (keys[h]) {
h = (h + 1) & mask;
}
keys[h] = key;
ranks[h] = i;
klen[h] = kl;
offset += kl;
}
tok_mask = mask;
tok_ranks = ranks;
tok_klen = klen;
tok_keys = keys;
}
static int tok_lookup(const unsigned char *s, int len) {
if (!tok_keys) return -1;
unsigned int h = (unsigned int)tok_fnv1a(s, len) & tok_mask;
while (tok_keys[h]) {
if ((int)tok_klen[h] == len && memcmp(tok_keys[h], s, (size_t)len) == 0)
return tok_ranks[h];
h = (h + 1) & tok_mask;
}
return -1;
}
/* ----------------------------- unicode -------------------------------- */
enum {
UCAT_OTHER = 0,
UCAT_WS = 1, /* \s (Unicode White_Space) */
UCAT_NUM = 2, /* \p{N} */
UCAT_LM = 3, /* [\p{L}\p{M}] */
UCAT_PS = 4 /* [\p{P}\p{S}] */
};
static const unsigned char u_ascii_cat[128] = {
0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,
4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,
4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,0,
};
/* Packed (start_codepoint << 3) | category for U+0080..U+10FFFF (1976 transitions). */
static const uint32_t u_cat_trans[1976] = {
0x400u,0x429u,0x430u,0x501u,0x50cu,0x553u,0x55cu,0x568u,0x574u,0x592u,0x5a4u,0x5abu,
0x5b4u,0x5cau,0x5d3u,0x5dcu,0x5e2u,0x5fcu,0x603u,0x6bcu,0x6c3u,0x7bcu,0x7c3u,0x1614u,
0x1633u,0x1694u,0x1703u,0x172cu,0x1763u,0x176cu,0x1773u,0x177cu,0x1803u,0x1bacu,0x1bb3u,0x1bc0u,
0x1bd3u,0x1bf4u,0x1bfbu,0x1c00u,0x1c24u,0x1c33u,0x1c3cu,0x1c43u,0x1c58u,0x1c63u,0x1c68u,0x1c73u,
0x1d10u,0x1d1bu,0x1fb4u,0x1fbbu,0x2414u,0x241bu,0x2980u,0x298bu,0x2ab8u,0x2acbu,0x2ad4u,0x2b03u,
0x2c4cu,0x2c58u,0x2c6cu,0x2c80u,0x2c8bu,0x2df4u,0x2dfbu,0x2e04u,0x2e0bu,0x2e1cu,0x2e23u,0x2e34u,
0x2e3bu,0x2e40u,0x2e83u,0x2f58u,0x2f7bu,0x2f9cu,0x2fa8u,0x3034u,0x3083u,0x30dcu,0x30e0u,0x30ecu,
0x3103u,0x3302u,0x3354u,0x3373u,0x36a4u,0x36abu,0x36e8u,0x36f4u,0x36fbu,0x374cu,0x3753u,0x3782u,
0x37d3u,0x37ecu,0x37fbu,0x3804u,0x3870u,0x3883u,0x3a58u,0x3a6bu,0x3d90u,0x3e02u,0x3e53u,0x3fb4u,
0x3fd3u,0x3fd8u,0x3febu,0x3ff4u,0x4003u,0x4170u,0x4184u,0x41f8u,0x4203u,0x42e0u,0x42f4u,0x42f8u,
0x4303u,0x4358u,0x4383u,0x4444u,0x444bu,0x4478u,0x44bbu,0x4710u,0x471bu,0x4b24u,0x4b32u,0x4b84u,
0x4b8bu,0x4c20u,0x4c2bu,0x4c68u,0x4c7bu,0x4c88u,0x4c9bu,0x4d48u,0x4d53u,0x4d88u,0x4d93u,0x4d98u,
0x4db3u,0x4dd0u,0x4de3u,0x4e28u,0x4e3bu,0x4e48u,0x4e5bu,0x4e78u,0x4ebbu,0x4ec0u,0x4ee3u,0x4ef0u,
0x4efbu,0x4f20u,0x4f32u,0x4f83u,0x4f94u,0x4fa2u,0x4fd4u,0x4fe3u,0x4fecu,0x4ff3u,0x4ff8u,0x500bu,
0x5020u,0x502bu,0x5058u,0x507bu,0x5088u,0x509bu,0x5148u,0x5153u,0x5188u,0x5193u,0x51a0u,0x51abu,
0x51b8u,0x51c3u,0x51d0u,0x51e3u,0x51e8u,0x51f3u,0x5218u,0x523bu,0x5248u,0x525bu,0x5270u,0x528bu,
0x5290u,0x52cbu,0x52e8u,0x52f3u,0x52f8u,0x5332u,0x5383u,0x53b4u,0x53b8u,0x540bu,0x5420u,0x542bu,
0x5470u,0x547bu,0x5490u,0x549bu,0x5548u,0x5553u,0x5588u,0x5593u,0x55a0u,0x55abu,0x55d0u,0x55e3u,
0x5630u,0x563bu,0x5650u,0x565bu,0x5670u,0x5683u,0x5688u,0x5703u,0x5720u,0x5732u,0x5784u,0x5790u,
0x57cbu,0x5800u,0x580bu,0x5820u,0x582bu,0x5868u,0x587bu,0x5888u,0x589bu,0x5948u,0x5953u,0x5988u,
0x5993u,0x59a0u,0x59abu,0x59d0u,0x59e3u,0x5a28u,0x5a3bu,0x5a48u,0x5a5bu,0x5a70u,0x5aabu,0x5ac0u,
0x5ae3u,0x5af0u,0x5afbu,0x5b20u,0x5b32u,0x5b84u,0x5b8bu,0x5b92u,0x5bc0u,0x5c13u,0x5c20u,0x5c2bu,
0x5c58u,0x5c73u,0x5c88u,0x5c93u,0x5cb0u,0x5ccbu,0x5cd8u,0x5ce3u,0x5ce8u,0x5cf3u,0x5d00u,0x5d1bu,
0x5d28u,0x5d43u,0x5d58u,0x5d73u,0x5dd0u,0x5df3u,0x5e18u,0x5e33u,0x5e48u,0x5e53u,0x5e70u,0x5e83u,
0x5e88u,0x5ebbu,0x5ec0u,0x5f32u,0x5f9cu,0x5fd8u,0x6003u,0x6068u,0x6073u,0x6088u,0x6093u,0x6148u,
0x6153u,0x61d0u,0x61e3u,0x6228u,0x6233u,0x6248u,0x6253u,0x6270u,0x62abu,0x62b8u,0x62c3u,0x62d8u,
0x62ebu,0x62f0u,0x6303u,0x6320u,0x6332u,0x6380u,0x63bcu,0x63c2u,0x63fcu,0x6403u,0x6424u,0x642bu,
0x6468u,0x6473u,0x6488u,0x6493u,0x6548u,0x6553u,0x65a0u,0x65abu,0x65d0u,0x65e3u,0x6628u,0x6633u,
0x6648u,0x6653u,0x6670u,0x66abu,0x66b8u,0x66ebu,0x66f8u,0x6703u,0x6720u,0x6732u,0x6780u,0x678bu,
0x67a0u,0x6803u,0x6868u,0x6873u,0x6888u,0x6893u,0x6a28u,0x6a33u,0x6a48u,0x6a53u,0x6a7cu,0x6a80u,
0x6aa3u,0x6ac2u,0x6afbu,0x6b20u,0x6b32u,0x6bccu,0x6bd3u,0x6c00u,0x6c0bu,0x6c20u,0x6c2bu,0x6cb8u,
0x6cd3u,0x6d90u,0x6d9bu,0x6de0u,0x6debu,0x6df0u,0x6e03u,0x6e38u,0x6e53u,0x6e58u,0x6e7bu,0x6ea8u,
0x6eb3u,0x6eb8u,0x6ec3u,0x6f00u,0x6f32u,0x6f80u,0x6f93u,0x6fa4u,0x6fa8u,0x700bu,0x71d8u,0x71fcu,
0x7203u,0x727cu,0x7282u,0x72d4u,0x72e0u,0x740bu,0x7418u,0x7423u,0x7428u,0x7433u,0x7458u,0x7463u,
0x7520u,0x752bu,0x7530u,0x753bu,0x75f0u,0x7603u,0x7628u,0x7633u,0x7638u,0x7643u,0x7678u,0x7682u,
0x76d0u,0x76e3u,0x7700u,0x7803u,0x780cu,0x78c3u,0x78d4u,0x7902u,0x79a4u,0x79abu,0x79b4u,0x79bbu,
0x79c4u,0x79cbu,0x79d4u,0x79f3u,0x7a40u,0x7a4bu,0x7b68u,0x7b8bu,0x7c2cu,0x7c33u,0x7cc0u,0x7ccbu,
0x7de8u,0x7df4u,0x7e33u,0x7e3cu,0x7e68u,0x7e74u,0x7ed8u,0x8003u,0x8202u,0x8254u,0x8283u,0x8482u,
0x84d3u,0x84f4u,0x8503u,0x8630u,0x863bu,0x8640u,0x866bu,0x8670u,0x8683u,0x87dcu,0x87e3u,0x9248u,
0x9253u,0x9270u,0x9283u,0x92b8u,0x92c3u,0x92c8u,0x92d3u,0x92f0u,0x9303u,0x9448u,0x9453u,0x9470u,
0x9483u,0x9588u,0x9593u,0x95b0u,0x95c3u,0x95f8u,0x9603u,0x9608u,0x9613u,0x9630u,0x9643u,0x96b8u,
0x96c3u,0x9888u,0x9893u,0x98b0u,0x98c3u,0x9ad8u,0x9aebu,0x9b04u,0x9b4au,0x9be8u,0x9c03u,0x9c84u,
0x9cd0u,0x9d03u,0x9fb0u,0x9fc3u,0x9ff0u,0xa004u,0xa00bu,0xb36cu,0xb37bu,0xb401u,0xb40bu,0xb4dcu,
0xb4e8u,0xb503u,0xb75cu,0xb772u,0xb78bu,0xb7c8u,0xb803u,0xb8b0u,0xb8fbu,0xb9acu,0xb9b8u,0xba03u,
0xbaa0u,0xbb03u,0xbb68u,0xbb73u,0xbb88u,0xbb93u,0xbba0u,0xbc03u,0xbea4u,0xbebbu,0xbec4u,0xbee3u,
0xbef0u,0xbf02u,0xbf50u,0xbf82u,0xbfd0u,0xc004u,0xc05bu,0xc070u,0xc07bu,0xc082u,0xc0d0u,0xc103u,
0xc3c8u,0xc403u,0xc558u,0xc583u,0xc7b0u,0xc803u,0xc8f8u,0xc903u,0xc960u,0xc983u,0xc9e0u,0xca04u,
0xca08u,0xca24u,0xca32u,0xca83u,0xcb70u,0xcb83u,0xcba8u,0xcc03u,0xcd60u,0xcd83u,0xce50u,0xce82u,
0xced8u,0xcef4u,0xd003u,0xd0e0u,0xd0f4u,0xd103u,0xd2f8u,0xd303u,0xd3e8u,0xd3fbu,0xd402u,0xd450u,
0xd482u,0xd4d0u,0xd504u,0xd53bu,0xd544u,0xd570u,0xd583u,0xd678u,0xd803u,0xda68u,0xda74u,0xda82u,
0xdad4u,0xdb5bu,0xdba4u,0xdc03u,0xdd82u,0xddd3u,0xdfa0u,0xdfe4u,0xe003u,0xe1c0u,0xe1dcu,0xe202u,
0xe250u,0xe26bu,0xe282u,0xe2d3u,0xe3f4u,0xe403u,0xe458u,0xe483u,0xe5d8u,0xe5ebu,0xe604u,0xe640u,
0xe683u,0xe69cu,0xe6a3u,0xe7d8u,0xe803u,0xf8b0u,0xf8c3u,0xf8f0u,0xf903u,0xfa30u,0xfa43u,0xfa70u,
0xfa83u,0xfac0u,0xfacbu,0xfad0u,0xfadbu,0xfae0u,0xfaebu,0xfaf0u,0xfafbu,0xfbf0u,0xfc03u,0xfda8u,
0xfdb3u,0xfdecu,0xfdf3u,0xfdfcu,0xfe13u,0xfe28u,0xfe33u,0xfe6cu,0xfe83u,0xfea0u,0xfeb3u,0xfee0u,
0xfeecu,0xff03u,0xff6cu,0xff80u,0xff93u,0xffa8u,0xffb3u,0xffecu,0xfff8u,0x10001u,0x10058u,0x10084u,
0x10141u,0x10150u,0x10179u,0x10184u,0x102f9u,0x10300u,0x10382u,0x1038bu,0x10390u,0x103a2u,0x103d4u,0x103fbu,
0x10402u,0x10454u,0x10478u,0x10483u,0x104e8u,0x10504u,0x10608u,0x10683u,0x10788u,0x10804u,0x10813u,0x1081cu,
0x1083bu,0x10844u,0x10853u,0x108a4u,0x108abu,0x108b4u,0x108cbu,0x108f4u,0x10923u,0x1092cu,0x10933u,0x1093cu,
0x10943u,0x1094cu,0x10953u,0x10974u,0x1097bu,0x109d4u,0x109e3u,0x10a04u,0x10a2bu,0x10a54u,0x10a73u,0x10a7cu,
0x10a82u,0x10c1bu,0x10c2au,0x10c54u,0x10c60u,0x10c84u,0x12150u,0x12204u,0x12258u,0x12302u,0x124e4u,0x12752u,
0x12804u,0x13bb2u,0x13ca4u,0x15ba0u,0x15bb4u,0x15cb0u,0x15cbcu,0x16003u,0x1672cu,0x1675bu,0x167a0u,0x167ccu,
0x167eau,0x167f4u,0x16803u,0x16930u,0x1693bu,0x16940u,0x1696bu,0x16970u,0x16983u,0x16b40u,0x16b7bu,0x16b84u,
0x16b88u,0x16bfbu,0x16cb8u,0x16d03u,0x16d38u,0x16d43u,0x16d78u,0x16d83u,0x16db8u,0x16dc3u,0x16df8u,0x16e03u,
0x16e38u,0x16e43u,0x16e78u,0x16e83u,0x16eb8u,0x16ec3u,0x16ef8u,0x16f03u,0x17004u,0x1717bu,0x17184u,0x172f0u,
0x17404u,0x174d0u,0x174dcu,0x177a0u,0x17804u,0x17eb0u,0x17f84u,0x18001u,0x1800cu,0x1802bu,0x1803au,0x18044u,
0x1810au,0x18153u,0x18184u,0x1818bu,0x181b4u,0x181c2u,0x181dbu,0x181ecu,0x18200u,0x1820bu,0x184b8u,0x184cbu,
0x184dcu,0x184ebu,0x18504u,0x1850bu,0x187dcu,0x187e3u,0x18800u,0x1882bu,0x18980u,0x1898bu,0x18c78u,0x18c84u,
0x18c92u,0x18cb4u,0x18d03u,0x18e04u,0x18f30u,0x18f7cu,0x18f83u,0x19004u,0x190f8u,0x19102u,0x19154u,0x19242u,
0x19284u,0x1928au,0x19304u,0x19402u,0x19454u,0x1958au,0x19604u,0x1a003u,0x26e04u,0x27003u,0x52468u,0x52484u,
0x52638u,0x52683u,0x527f4u,0x52803u,0x5306cu,0x53083u,0x53102u,0x53153u,0x53160u,0x53203u,0x5339cu,0x533a3u,
0x533f4u,0x533fbu,0x53732u,0x53783u,0x53794u,0x537c0u,0x53804u,0x538bbu,0x53904u,0x53913u,0x53c4cu,0x53c5bu,
0x53e70u,0x53e83u,0x53e90u,0x53e9bu,0x53ea0u,0x53eabu,0x53ee8u,0x53f93u,0x54144u,0x54163u,0x54168u,0x54182u,
0x541b4u,0x541d0u,0x54203u,0x543a4u,0x543c0u,0x54403u,0x54630u,0x54674u,0x54682u,0x546d0u,0x54703u,0x547c4u,
0x547dbu,0x547e4u,0x547ebu,0x54802u,0x54853u,0x54974u,0x54983u,0x54aa0u,0x54afcu,0x54b03u,0x54be8u,0x54c03u,
0x54e0cu,0x54e70u,0x54e7bu,0x54e82u,0x54ed0u,0x54ef4u,0x54f03u,0x54f82u,0x54fd3u,0x54ff8u,0x55003u,0x551b8u,
0x55203u,0x55270u,0x55282u,0x552d0u,0x552e4u,0x55303u,0x553bcu,0x553d3u,0x55618u,0x556dbu,0x556f4u,0x55703u,
0x55784u,0x55793u,0x557b8u,0x5580bu,0x55838u,0x5584bu,0x55878u,0x5588bu,0x558b8u,0x55903u,0x55938u,0x55943u,
0x55978u,0x55983u,0x55adcu,0x55ae3u,0x55b54u,0x55b60u,0x55b83u,0x55f5cu,0x55f63u,0x55f70u,0x55f82u,0x55fd0u,
0x56003u,0x6bd20u,0x6bd83u,0x6be38u,0x6be5bu,0x6bfe0u,0x7c803u,0x7d370u,0x7d383u,0x7d6d0u,0x7d803u,0x7d838u,
0x7d89bu,0x7d8c0u,0x7d8ebu,0x7d94cu,0x7d953u,0x7d9b8u,0x7d9c3u,0x7d9e8u,0x7d9f3u,0x7d9f8u,0x7da03u,0x7da10u,
0x7da1bu,0x7da28u,0x7da33u,0x7dd94u,0x7de18u,0x7de9bu,0x7e9f4u,0x7ea83u,0x7ec80u,0x7ec93u,0x7ee40u,0x7ee7cu,
0x7ee80u,0x7ef83u,0x7efe4u,0x7f003u,0x7f084u,0x7f0d0u,0x7f103u,0x7f184u,0x7f298u,0x7f2a4u,0x7f338u,0x7f344u,
0x7f360u,0x7f383u,0x7f3a8u,0x7f3b3u,0x7f7e8u,0x7f80cu,0x7f882u,0x7f8d4u,0x7f90bu,0x7f9dcu,0x7fa0bu,0x7fadcu,
0x7fb33u,0x7fdf8u,0x7fe13u,0x7fe40u,0x7fe53u,0x7fe80u,0x7fe93u,0x7fec0u,0x7fed3u,0x7fee8u,0x7ff04u,0x7ff38u,
0x7ff44u,0x7ff78u,0x7ffe4u,0x7fff0u,0x80003u,0x80060u,0x8006bu,0x80138u,0x80143u,0x801d8u,0x801e3u,0x801f0u,
0x801fbu,0x80270u,0x80283u,0x802f0u,0x80403u,0x807d8u,0x80804u,0x80818u,0x8083au,0x809a0u,0x809bcu,0x80a02u,
0x80bccu,0x80c52u,0x80c64u,0x80c78u,0x80c84u,0x80ce8u,0x80d04u,0x80d08u,0x80e84u,0x80febu,0x80ff0u,0x81403u,
0x814e8u,0x81503u,0x81688u,0x81703u,0x8170au,0x817e0u,0x81803u,0x81902u,0x81920u,0x8196bu,0x81a0au,0x81a13u,
0x81a52u,0x81a58u,0x81a83u,0x81bd8u,0x81c03u,0x81cf0u,0x81cfcu,0x81d03u,0x81e20u,0x81e43u,0x81e84u,0x81e8au,
0x81eb0u,0x82003u,0x824f0u,0x82502u,0x82550u,0x82583u,0x826a0u,0x826c3u,0x827e0u,0x82803u,0x82940u,0x82983u,
0x82b20u,0x82b7cu,0x82b83u,0x82bd8u,0x82be3u,0x82c58u,0x82c63u,0x82c98u,0x82ca3u,0x82cb0u,0x82cbbu,0x82d10u,
0x82d1bu,0x82d90u,0x82d9bu,0x82dd0u,0x82ddbu,0x82de8u,0x82e03u,0x82fa0u,0x83003u,0x839b8u,0x83a03u,0x83ab0u,
0x83b03u,0x83b40u,0x83c03u,0x83c30u,0x83c3bu,0x83d88u,0x83d93u,0x83dd8u,0x84003u,0x84030u,0x84043u,0x84048u,
0x84053u,0x841b0u,0x841bbu,0x841c8u,0x841e3u,0x841e8u,0x841fbu,0x842b0u,0x842bcu,0x842c2u,0x84303u,0x843bcu,
0x843cau,0x84403u,0x844f8u,0x8453au,0x84580u,0x84703u,0x84798u,0x847a3u,0x847b0u,0x847dau,0x84803u,0x848b2u,
0x848e0u,0x848fcu,0x84903u,0x849d0u,0x849fcu,0x84a00u,0x84c03u,0x84dc0u,0x84de2u,0x84df3u,0x84e02u,0x84e80u,
0x84e92u,0x85003u,0x85020u,0x8502bu,0x85038u,0x85063u,0x850a0u,0x850abu,0x850c0u,0x850cbu,0x851b0u,0x851c3u,
0x851d8u,0x851fbu,0x85202u,0x85248u,0x85284u,0x852c8u,0x85303u,0x853eau,0x853fcu,0x85403u,0x854eau,0x85500u,
0x85603u,0x85644u,0x8564bu,0x85738u,0x8575au,0x85784u,0x857b8u,0x85803u,0x859b0u,0x859ccu,0x85a03u,0x85ab0u,
0x85ac2u,0x85b03u,0x85b98u,0x85bc2u,0x85c03u,0x85c90u,0x85cccu,0x85ce8u,0x85d4au,0x85d80u,0x86003u,0x86248u,
0x86403u,0x86598u,0x86603u,0x86798u,0x867d2u,0x86803u,0x86940u,0x86982u,0x869d0u,0x86a02u,0x86a53u,0x86b30u,
0x86b4bu,0x86b74u,0x86b7bu,0x86c30u,0x86c74u,0x86c80u,0x87302u,0x873f8u,0x87403u,0x87550u,0x8755bu,0x8756cu,
0x87570u,0x87583u,0x87590u,0x87613u,0x87628u,0x877e3u,0x878eau,0x8793bu,0x87940u,0x87983u,0x87a8au,0x87aacu,
0x87ad0u,0x87b83u,0x87c34u,0x87c50u,0x87d83u,0x87e2au,0x87e60u,0x87f03u,0x87fb8u,0x88003u,0x8823cu,0x88270u,
0x88292u,0x88383u,0x883b0u,0x883fbu,0x885dcu,0x885e8u,0x885f4u,0x88613u,0x88618u,0x88683u,0x88748u,0x88782u,
0x887d0u,0x88803u,0x889a8u,0x889b2u,0x88a04u,0x88a23u,0x88a40u,0x88a83u,0x88ba4u,0x88bb3u,0x88bb8u,0x88c03u,
0x88e2cu,0x88e4bu,0x88e6cu,0x88e73u,0x88e82u,0x88ed3u,0x88edcu,0x88ee3u,0x88eecu,0x88f00u,0x88f0au,0x88fa8u,
0x89003u,0x89090u,0x8909bu,0x891c4u,0x891f3u,0x89210u,0x89403u,0x89438u,0x89443u,0x89448u,0x89453u,0x89470u,
0x8947bu,0x894f0u,0x894fbu,0x8954cu,0x89550u,0x89583u,0x89758u,0x89782u,0x897d0u,0x89803u,0x89820u,0x8982bu,
0x89868u,0x8987bu,0x89888u,0x8989bu,0x89948u,0x89953u,0x89988u,0x89993u,0x899a0u,0x899abu,0x899d0u,0x899dbu,
0x89a28u,0x89a3bu,0x89a48u,0x89a5bu,0x89a70u,0x89a83u,0x89a88u,0x89abbu,0x89ac0u,0x89aebu,0x89b20u,0x89b33u,
0x89b68u,0x89b83u,0x89ba8u,0x89c03u,0x89c50u,0x89c5bu,0x89c60u,0x89c73u,0x89c78u,0x89c83u,0x89db0u,0x89dbbu,
0x89e08u,0x89e13u,0x89e18u,0x89e2bu,0x89e30u,0x89e3bu,0x89e58u,0x89e63u,0x89ea4u,0x89eb0u,0x89ebcu,0x89ec8u,
0x89f0bu,0x89f18u,0x8a003u,0x8a25cu,0x8a282u,0x8a2d4u,0x8a2e0u,0x8a2ecu,0x8a2f3u,0x8a310u,0x8a403u,0x8a634u,
0x8a63bu,0x8a640u,0x8a682u,0x8a6d0u,0x8ac03u,0x8adb0u,0x8adc3u,0x8ae0cu,0x8aec3u,0x8aef0u,0x8b003u,0x8b20cu,
0x8b223u,0x8b228u,0x8b282u,0x8b2d0u,0x8b304u,0x8b368u,0x8b403u,0x8b5ccu,0x8b5d0u,0x8b602u,0x8b650u,0x8b682u,
0x8b720u,0x8b803u,0x8b8d8u,0x8b8ebu,0x8b960u,0x8b982u,0x8b9e4u,0x8ba03u,0x8ba38u,0x8c003u,0x8c1dcu,0x8c1e0u,
0x8c503u,0x8c702u,0x8c798u,0x8c7fbu,0x8c838u,0x8c84bu,0x8c850u,0x8c863u,0x8c8a0u,0x8c8abu,0x8c8b8u,0x8c8c3u,
0x8c9b0u,0x8c9bbu,0x8c9c8u,0x8c9dbu,0x8ca24u,0x8ca38u,0x8ca82u,0x8cad0u,0x8cd03u,0x8cd40u,0x8cd53u,0x8cec0u,
0x8ced3u,0x8cf14u,0x8cf1bu,0x8cf28u,0x8d003u,0x8d1fcu,0x8d23bu,0x8d240u,0x8d283u,0x8d4d4u,0x8d4ebu,0x8d4f4u,
0x8d518u,0x8d583u,0x8d7c8u,0x8d804u,0x8d850u,0x8de03u,0x8df0cu,0x8df10u,0x8df82u,0x8dfd0u,0x8e003u,0x8e048u,
0x8e053u,0x8e1b8u,0x8e1c3u,0x8e20cu,0x8e230u,0x8e282u,0x8e368u,0x8e384u,0x8e393u,0x8e480u,0x8e493u,0x8e540u,
0x8e54bu,0x8e5b8u,0x8e803u,0x8e838u,0x8e843u,0x8e850u,0x8e85bu,0x8e9b8u,0x8e9d3u,0x8e9d8u,0x8e9e3u,0x8e9f0u,
0x8e9fbu,0x8ea40u,0x8ea82u,0x8ead0u,0x8eb03u,0x8eb30u,0x8eb3bu,0x8eb48u,0x8eb53u,0x8ec78u,0x8ec83u,0x8ec90u,
0x8ec9bu,0x8ecc8u,0x8ed02u,0x8ed50u,0x8f703u,0x8f7bcu,0x8f7c8u,0x8f803u,0x8f888u,0x8f893u,0x8f9d8u,0x8f9f3u,
0x8fa1cu,0x8fa82u,0x8fad3u,0x8fad8u,0x8fd83u,0x8fd88u,0x8fe02u,0x8feacu,0x8ff90u,0x8fffcu,0x90003u,0x91cd0u,
0x92002u,0x92378u,0x92384u,0x923a8u,0x92403u,0x92a20u,0x97c83u,0x97f8cu,0x97f98u,0x98003u,0x9a180u,0x9a203u,
0x9a2b0u,0x9a303u,0xa1fd8u,0xa2003u,0xa3238u,0xb0803u,0xb0982u,0xb09d0u,0xb4003u,0xb51c8u,0xb5203u,0xb52f8u,
0xb5302u,0xb5350u,0xb5374u,0xb5383u,0xb55f8u,0xb5602u,0xb5650u,0xb5683u,0xb5770u,0xb5783u,0xb57acu,0xb57b0u,
0xb5803u,0xb59bcu,0xb5a03u,0xb5a24u,0xb5a30u,0xb5a82u,0xb5ad0u,0xb5adau,0xb5b10u,0xb5b1bu,0xb5bc0u,0xb5bebu,
0xb5c80u,0xb6a03u,0xb6b6cu,0xb6b82u,0xb6bd0u,0xb7203u,0xb7402u,0xb74bcu,0xb74d8u,0xb7803u,0xb7a58u,0xb7a7bu,
0xb7c40u,0xb7c7bu,0xb7d00u,0xb7f03u,0xb7f14u,0xb7f1bu,0xb7f28u,0xb7f83u,0xb7f90u,0xb8003u,0xc3fc0u,0xc4003u,
0xc66b0u,0xc67fbu,0xc6848u,0xd7f83u,0xd7fa0u,0xd7fabu,0xd7fe0u,0xd7febu,0xd7ff8u,0xd8003u,0xd8918u,0xd8993u,
0xd8998u,0xd8a83u,0xd8a98u,0xd8aabu,0xd8ab0u,0xd8b23u,0xd8b40u,0xd8b83u,0xd97e0u,0xde003u,0xde358u,0xde383u,
0xde3e8u,0xde403u,0xde448u,0xde483u,0xde4d0u,0xde4e4u,0xde4ebu,0xde4fcu,0xde500u,0xe6004u,0xe6782u,0xe67d0u,
0xe6804u,0xe75a0u,0xe7803u,0xe7970u,0xe7983u,0xe7a38u,0xe7a84u,0xe7e20u,0xe8004u,0xe87b0u,0xe8804u,0xe8938u,
0xe894cu,0xe8b2bu,0xe8b54u,0xe8b6bu,0xe8b98u,0xe8bdbu,0xe8c1cu,0xe8c2bu,0xe8c64u,0xe8d53u,0xe8d74u,0xe8f58u,
0xe9004u,0xe9213u,0xe922cu,0xe9230u,0xe9602u,0xe96a0u,0xe9702u,0xe97a0u,0xe9804u,0xe9ab8u,0xe9b02u,0xe9bc8u,
0xea003u,0xea2a8u,0xea2b3u,0xea4e8u,0xea4f3u,0xea500u,0xea513u,0xea518u,0xea52bu,0xea538u,0xea54bu,0xea568u,
0xea573u,0xea5d0u,0xea5dbu,0xea5e0u,0xea5ebu,0xea620u,0xea62bu,0xea830u,0xea83bu,0xea858u,0xea86bu,0xea8a8u,
0xea8b3u,0xea8e8u,0xea8f3u,0xea9d0u,0xea9dbu,0xea9f8u,0xeaa03u,0xeaa28u,0xeaa33u,0xeaa38u,0xeaa53u,0xeaa88u,
0xeaa93u,0xeb530u,0xeb543u,0xeb60cu,0xeb613u,0xeb6dcu,0xeb6e3u,0xeb7dcu,0xeb7e3u,0xeb8acu,0xeb8b3u,0xeb9acu,
0xeb9b3u,0xeba7cu,0xeba83u,0xebb7cu,0xebb83u,0xebc4cu,0xebc53u,0xebd4cu,0xebd53u,0xebe1cu,0xebe23u,0xebe60u,
0xebe72u,0xec004u,0xed003u,0xed1bcu,0xed1dbu,0xed36cu,0xed3abu,0xed3b4u,0xed423u,0xed42cu,0xed460u,0xed4dbu,
0xed500u,0xed50bu,0xed580u,0xef803u,0xef8f8u,0xef92bu,0xef958u,0xf0003u,0xf0038u,0xf0043u,0xf00c8u,0xf00dbu,
0xf0110u,0xf011bu,0xf0128u,0xf0133u,0xf0158u,0xf0183u,0xf0370u,0xf047bu,0xf0480u,0xf0803u,0xf0968u,0xf0983u,
0xf09f0u,0xf0a02u,0xf0a50u,0xf0a73u,0xf0a7cu,0xf0a80u,0xf1483u,0xf1578u,0xf1603u,0xf1782u,0xf17d0u,0xf17fcu,
0xf1800u,0xf2683u,0xf2782u,0xf27d0u,0xf2e83u,0xf2f8au,0xf2fd8u,0xf2ffcu,0xf3000u,0xf3f03u,0xf3f38u,0xf3f43u,
0xf3f60u,0xf3f6bu,0xf3f78u,0xf3f83u,0xf3ff8u,0xf4003u,0xf4628u,0xf463au,0xf4683u,0xf46b8u,0xf4803u,0xf4a60u,
0xf4a82u,0xf4ad0u,0xf4af4u,0xf4b00u,0xf638au,0xf6564u,0xf656au,0xf6584u,0xf658au,0xf65a8u,0xf680au,0xf6974u,
0xf697au,0xf69f0u,0xf7003u,0xf7020u,0xf702bu,0xf7100u,0xf710bu,0xf7118u,0xf7123u,0xf7128u,0xf713bu,0xf7140u,
0xf714bu,0xf7198u,0xf71a3u,0xf71c0u,0xf71cbu,0xf71d0u,0xf71dbu,0xf71e0u,0xf7213u,0xf7218u,0xf723bu,0xf7240u,
0xf724bu,0xf7250u,0xf725bu,0xf7260u,0xf726bu,0xf7280u,0xf728bu,0xf7298u,0xf72a3u,0xf72a8u,0xf72bbu,0xf72c0u,
0xf72cbu,0xf72d0u,0xf72dbu,0xf72e0u,0xf72ebu,0xf72f0u,0xf72fbu,0xf7300u,0xf730bu,0xf7318u,0xf7323u,0xf7328u,
0xf733bu,0xf7358u,0xf7363u,0xf7398u,0xf73a3u,0xf73c0u,0xf73cbu,0xf73e8u,0xf73f3u,0xf73f8u,0xf7403u,0xf7450u,
0xf745bu,0xf74e0u,0xf750bu,0xf7520u,0xf752bu,0xf7550u,0xf755bu,0xf75e0u,0xf7784u,0xf7790u,0xf8004u,0xf8160u,
0xf8184u,0xf84a0u,0xf8504u,0xf8578u,0xf858cu,0xf8600u,0xf860cu,0xf8680u,0xf868cu,0xf87b0u,0xf8802u,0xf886cu,
0xf8d70u,0xf8f34u,0xf9018u,0xf9084u,0xf91e0u,0xf9204u,0xf9248u,0xf9284u,0xf9290u,0xf9304u,0xf9330u,0xf9804u,
0xfb6c0u,0xfb6e4u,0xfb768u,0xfb784u,0xfb7e8u,0xfb804u,0xfbbb8u,0xfbbdcu,0xfbed0u,0xfbf04u,0xfbf60u,0xfbf84u,
0xfbf88u,0xfc004u,0xfc060u,0xfc084u,0xfc240u,0xfc284u,0xfc2d0u,0xfc304u,0xfc440u,0xfc484u,0xfc570u,0xfc584u,
0xfc5e0u,0xfc604u,0xfc610u,0xfc804u,0xfd2a0u,0xfd304u,0xfd370u,0xfd384u,0xfd3e8u,0xfd404u,0xfd450u,0xfd47cu,
0xfd638u,0xfd674u,0xfd6e8u,0xfd6fcu,0xfd750u,0xfd784u,0xfd7c8u,0xfd804u,0xfdc98u,0xfdca4u,0xfdf82u,0xfdfd0u,
0x100003u,0x153700u,0x153803u,0x15b9d0u,0x15ba03u,0x15c0f0u,0x15c103u,0x167510u,0x167583u,0x175f08u,0x175f83u,0x1772f0u,
0x17c003u,0x17d0f0u,0x180003u,0x189a58u,0x189a83u,0x191d80u,0x700803u,0x700f80u,
};
static int u_cat(int cp) {
if (cp < 0) return UCAT_OTHER;
if (cp < 0x80) return (int)u_ascii_cat[cp];
if (cp > 0x10ffff) return UCAT_OTHER;
uint32_t target = ((uint32_t)cp << 3) | 7u;
int lo = 0, hi = (int)(sizeof(u_cat_trans) / sizeof(u_cat_trans[0])) - 1;
while (lo < hi) {
int mid = (lo + hi + 1) >> 1;
if (u_cat_trans[mid] <= target) lo = mid;
else hi = mid - 1;
}
return (int)(u_cat_trans[lo] & 7u);
}
static int u_is_cjk_kana(int cp) {
return (cp >= 0x4e00 && cp <= 0x9fa5) || (cp >= 0x3040 && cp <= 0x30ff);
}
static int u_is_ascii_punct(int cp) {
return (cp >= 0x21 && cp <= 0x2f) || (cp >= 0x3a && cp <= 0x40) ||
(cp >= 0x5b && cp <= 0x60) || (cp >= 0x7b && cp <= 0x7e);
}
static int u_is_ascii_alpha(int cp) {
return (cp >= 'A' && cp <= 'Z') || (cp >= 'a' && cp <= 'z');
}
/* Decode one UTF-8 codepoint at t[pos]; sets *cp (-1 if invalid byte) and *cplen. */
static void decode_cp(const unsigned char *t, int len, int pos, int *cp, int *cplen) {
unsigned char b = t[pos];
if (b < 0x80) { *cp = b; *cplen = 1; return; }
int c, l, min_cp;
if ((b >> 5) == 0x6) { c = b & 0x1f; l = 2; min_cp = 0x80; }
else if ((b >> 4) == 0xe) { c = b & 0x0f; l = 3; min_cp = 0x800; }
else if ((b >> 3) == 0x1e) { c = b & 0x07; l = 4; min_cp = 0x10000; }
else { *cp = -1; *cplen = 1; return; }
if (pos + l > len) { *cp = -1; *cplen = 1; return; }
for (int j = 1; j < l; j++) {
if ((t[pos + j] & 0xc0) != 0x80) { *cp = -1; *cplen = 1; return; }
c = (c << 6) | (t[pos + j] & 0x3f);
}
if (c < min_cp || c > 0x10ffff || (c >= 0xd800 && c <= 0xdfff)) {
*cp = -1; *cplen = 1; return;
}
*cp = c; *cplen = l;
}
/* ----------------------------- added tokens --------------------------- */
typedef struct {
const char *str;
uint8_t len;
} added_tok_t;
static const added_tok_t tok_added_named[] = {
{ "<\357\275\234tool\342\226\201output\342\226\201begin:opensource\357\275\234>", 40 },
{ "<\357\275\234hy_begin_of_sentence:opensource\357\275\234>", 39 },
{ "<\357\275\234tool\342\226\201calls\342\226\201begin:opensource\357\275\234>", 39 },
{ "<\357\275\234tool\342\226\201call\342\226\201begin:opensource\357\275\234>", 38 },
{ "<\357\275\234tool\342\226\201output\342\226\201end:opensource\357\275\234>", 38 },
{ "<\357\275\234tool\342\226\201calls\342\226\201end:opensource\357\275\234>", 37 },
{ "<\357\275\234tool\342\226\201call\342\226\201end:opensource\357\275\234>", 36 },
{ "<\357\275\234reasoning_mode:opensource\357\275\234>", 33 },
{ "<\357\275\234hy_Assistant:opensource\357\275\234>", 31 },
{ "<\357\275\234hy_end\342\226\201of\342\226\201sentence\357\275\234>", 30 },
{ "<\357\275\234tool\342\226\201sep:opensource\357\275\234>", 29 },
{ "</tool_responses:opensource>", 28 },
{ "</tool_response:opensource>", 27 },
{ "<tool_responses:opensource>", 27 },
{ "<\357\275\234hy_User:opensource\357\275\234>", 26 },
{ "<tool_response:opensource>", 26 },
{ "<\357\275\234hy_pad:opensource\357\275\234>", 25 },
{ "<\357\275\234hy_eos:opensource\357\275\234>", 25 },
{ "</tool_calls:opensource>", 24 },
{ "</tool_call:opensource>", 23 },
{ "<tool_calls:opensource>", 23 },
{ "</arg_value:opensource>", 23 },
{ "<\357\275\234hy_fim\342\226\201begin\357\275\234>", 22 },
{ "<tool_call:opensource>", 22 },
{ "<arg_value:opensource>", 22 },
{ "<\357\275\234hy_fim\342\226\201hole\357\275\234>", 21 },
{ "<tool_sep:opensource>", 21 },
{ "</arg_key:opensource>", 21 },
{ "<\357\275\234hy_fim\342\226\201end\357\275\234>", 20 },
{ "<arg_key:opensource>", 20 },
{ "</think:opensource>", 19 },
{ "<think:opensource>", 18 },
{ "<\357\275\234hy_EOT\357\275\234>", 14 },
{ "</answer>", 9 },
{ "/no_think", 9 },
{ "<answer>", 8 },
{ "/think", 6 },
};
/* Match an added special/control token at t[pos..len). Returns byte length or 0. */
static int tok_match_added(const unsigned char *t, int len, int pos) {
unsigned char b = t[pos];
if (b != '<' && b != '/') return 0;
int rem = len - pos;
const char *p = (const char *)(t + pos);
/* Check <|hy_place▁holder▁no▁N|> (11 <= N <= 799, excl. 20..33 and 48..51) */
static const char ph_prefix[] = "<\357\275\234hy_place\342\226\201holder\342\226\201no\342\226\201";
static const char ph_suffix[] = "\357\275\234>";
const int ph_plen = (int)(sizeof(ph_prefix) - 1);
const int ph_slen = (int)(sizeof(ph_suffix) - 1);
if (b == '<' && rem >= ph_plen + 2 + ph_slen &&
memcmp(p, ph_prefix, (size_t)ph_plen) == 0) {
int k = ph_plen, num = 0, ndig = 0;
while (k < rem && ndig < 3 && p[k] >= '0' && p[k] <= '9') {
num = num * 10 + (p[k] - '0');
k++;
ndig++;
}
if (ndig >= 2 && p[ph_plen] != '0' && num >= 11 && num <= 799 &&
!(num >= 20 && num <= 33) && !(num >= 48 && num <= 51) &&
rem - k >= ph_slen && memcmp(p + k, ph_suffix, (size_t)ph_slen) == 0) {
return k + ph_slen;
}
}
int n_added = (int)(sizeof(tok_added_named) / sizeof(tok_added_named[0]));
for (int i = 0; i < n_added; i++) {
int alen = (int)tok_added_named[i].len;
if (rem >= alen && p[1] == tok_added_named[i].str[1] &&
memcmp(p, tok_added_named[i].str, (size_t)alen) == 0) {
return alen;
}
}
return 0;
}
/* ----------------------------- pre-tokenizer -------------------------- */
static int in_stage3_seg(int cp, int cat) {
return cat != UCAT_NUM && !u_is_cjk_kana(cp);
}
/* Returns the exclusive end byte offset of the pre-tokenized piece starting
* at position i within slice t[0..len), implementing Hy3's 3-stage Split. */
static int tok_piece_end(const unsigned char *t, int len, int i) {
int cp, cplen;
decode_cp(t, len, i, &cp, &cplen);
int cat = u_cat(cp);
int ni = i + cplen;
/* Stage 1: \p{N}{1,3} */
if (cat == UCAT_NUM) {
int e = ni, cnt = 1;
while (e < len && cnt < 3) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
if (u_cat(c) != UCAT_NUM) break;
e += cl;
cnt++;
}
return e;
}
/* Stage 2: [\u4e00-\u9fa5\u3040-\u309f\u30a0-\u30ff]+ sub-slice with Stage 3 applied */
if (u_is_cjk_kana(cp)) {
int ncp = -1, ncl = 0, ncat = UCAT_OTHER;
if (ni < len) {
decode_cp(t, len, ni, &ncp, &ncl);
if (u_is_cjk_kana(ncp)) ncat = u_cat(ncp);
else ncp = -1;
}
if (cat == UCAT_LM) {
int e = ni;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
if (!u_is_cjk_kana(c) || u_cat(c) != UCAT_LM) break;
e += cl;
}
return e;
}
if (cat != UCAT_PS && ncp >= 0 && ncat == UCAT_LM) {
int e = ni + ncl;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
if (!u_is_cjk_kana(c) || u_cat(c) != UCAT_LM) break;
e += cl;
}
return e;
}
if (cat == UCAT_PS) {
int e = ni;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
if (!u_is_cjk_kana(c) || u_cat(c) != UCAT_PS) break;
e += cl;
}
return e;
}
int e = ni;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
if (!u_is_cjk_kana(c)) break;
int cc = u_cat(c);
if (cc == UCAT_LM || cc == UCAT_PS) break;
if (e + cl < len) {
int nc, nl;
decode_cp(t, len, e + cl, &nc, &nl);
if (u_is_cjk_kana(nc) && u_cat(nc) == UCAT_LM) break;
}
e += cl;
}
return e;
}
/* Stage 3 on non-number, non-CJK/Kana segment */
int has_ni = 0, ncp = -1, ncl = 0, ncat = UCAT_OTHER;
if (ni < len) {
decode_cp(t, len, ni, &ncp, &ncl);
ncat = u_cat(ncp);
has_ni = in_stage3_seg(ncp, ncat);
}
/* 1) [!"#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~][A-Za-z]+ */
if (u_is_ascii_punct(cp) && has_ni && u_is_ascii_alpha(ncp)) {
int e = ni + ncl;
while (e < len && u_is_ascii_alpha((int)t[e])) e++;
return e;
}
/* 2) [^\r\n\p{L}\p{P}\p{S}]?[\p{L}\p{M}]+ */
if (cat == UCAT_LM) {
int e = ni;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
int cc = u_cat(c);
if (!in_stage3_seg(c, cc) || cc != UCAT_LM) break;
e += cl;
}
return e;
}
if (cp != 0x0d && cp != 0x0a && cat != UCAT_PS && has_ni && ncat == UCAT_LM) {
int e = ni + ncl;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
int cc = u_cat(c);
if (!in_stage3_seg(c, cc) || cc != UCAT_LM) break;
e += cl;
}
return e;
}
/* 3) ?[\p{P}\p{S}]+[\r\n]* */
if (cp == 0x20 && has_ni && ncat == UCAT_PS) {
int e = ni + ncl;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
int cc = u_cat(c);
if (!in_stage3_seg(c, cc) || cc != UCAT_PS) break;
e += cl;
}
while (e < len && (t[e] == 0x0d || t[e] == 0x0a)) e++;
return e;
}
if (cat == UCAT_PS) {
int e = ni;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
int cc = u_cat(c);
if (!in_stage3_seg(c, cc) || cc != UCAT_PS) break;
e += cl;
}
while (e < len && (t[e] == 0x0d || t[e] == 0x0a)) e++;
return e;
}
/* 4-6) \s*[\r\n]+ | \s+(?!\S) | \s+ */
if (cat == UCAT_WS) {
int e = i;
int last_ws_len = cplen;
int last_nl_end = 0;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
if (u_cat(c) != UCAT_WS) break;
last_ws_len = cl;
if (c == 0x0d || c == 0x0a) last_nl_end = e + cl;
e += cl;
}
if (last_nl_end > i) return last_nl_end;
if (e == len) return e;
int ac, acl;
decode_cp(t, len, e, &ac, &acl);
if (!in_stage3_seg(ac, u_cat(ac))) return e;
if ((e - i) > last_ws_len) return e - last_ws_len;
return e;
}
/* 7) Non-matching gap of UCAT_OTHER characters not followed by in-segment UCAT_LM */
int e = ni;
while (e < len) {
int c, cl;
decode_cp(t, len, e, &c, &cl);
int cc = u_cat(c);
if (!in_stage3_seg(c, cc) || cc != UCAT_OTHER) break;
if (e + cl < len) {
int nc, nl;
decode_cp(t, len, e + cl, &nc, &nl);
int ncc = u_cat(nc);
if (in_stage3_seg(nc, ncc) && ncc == UCAT_LM) break;
}
e += cl;
}
return e;
}
/* ----------------------------- BPE ------------------------------------ */
#define MAX_TOKEN_BYTES 512
typedef struct {
int prev;
int next;
int rank;
} bpe_node_t;
typedef struct {
int rank;
int pos;
} bpe_heap_item_t;
static int heap_less(bpe_heap_item_t a, bpe_heap_item_t b) {
return (a.rank < b.rank) || (a.rank == b.rank && a.pos < b.pos);
}
static void heap_push(bpe_heap_item_t *h, int *hn, bpe_heap_item_t v) {
int i = (*hn)++;
while (i > 0) {
int p = (i - 1) >> 1;
if (!heap_less(v, h[p])) break;
h[i] = h[p];
i = p;
}
h[i] = v;
}
static bpe_heap_item_t heap_pop(bpe_heap_item_t *h, int *hn) {
bpe_heap_item_t top = h[0];
bpe_heap_item_t last = h[--(*hn)];
int n = *hn, i = 0;
while ((i << 1) + 1 < n) {
int l = (i << 1) + 1, r = l + 1;
int c = (r < n && heap_less(h[r], h[l])) ? r : l;
if (!heap_less(h[c], last)) break;
h[i] = h[c];
i = c;
}
if (n > 0) h[i] = last;
return top;
}
/* Hy3's BPE vocab omits 14 single-byte values (0x0d CR and 13 invalid UTF-8
* bytes 0xc0, 0xc1, 0xf5..0xff) with unk_token=null, so HF tokenizers drops
* them when constructing a BPE Word. */
static int is_unmapped_byte(unsigned char b) {
return b == 13 || b == 192 || b == 193 || b >= 245;
}
/* Count tokens in a single raw-byte piece via O(N log N) lowest-rank merges. */
static int tok_bpe_raw(const unsigned char *piece, int plen) {
if (plen <= 0) return 0;
if (plen == 1) return 1;
bpe_node_t *nodes = (bpe_node_t *)malloc(sizeof(bpe_node_t) * (size_t)plen);
bpe_heap_item_t *heap = (bpe_heap_item_t *)malloc(sizeof(bpe_heap_item_t) * (size_t)plen * 2u);
if (!nodes || !heap) {
free(nodes);
free(heap);
return (plen + 3) / 4;
}
int hn = 0;
for (int k = 0; k < plen; k++) {
nodes[k].prev = k - 1;
nodes[k].next = k + 1;
if (k + 1 < plen) {
int r = tok_lookup(piece + k, 2);
nodes[k].rank = r;
if (r >= 0) {
bpe_heap_item_t item = { r, k };
heap_push(heap, &hn, item);
}
} else {
nodes[k].rank = -1;
}
}
int nsym = plen;
while (nsym > 1 && hn > 0) {
bpe_heap_item_t top = heap_pop(heap, &hn);
int best = top.pos;
int right = nodes[best].next;
if (right >= plen || nodes[best].rank != top.rank) continue;
int nr = nodes[right].next;
nodes[best].next = nr;
if (nr < plen) nodes[nr].prev = best;
nodes[right].next = plen + 1;
nodes[right].rank = -1;
nsym--;
if (nr < plen) {
int clen = nodes[nr].next - best;
int r = (clen <= MAX_TOKEN_BYTES) ? tok_lookup(piece + best, clen) : -1;
nodes[best].rank = r;
if (r >= 0) {
bpe_heap_item_t item = { r, best };
heap_push(heap, &hn, item);
}
} else {
nodes[best].rank = -1;
}
int left = nodes[best].prev;
if (left >= 0) {
int clen = nr - left;
int r = (clen <= MAX_TOKEN_BYTES) ? tok_lookup(piece + left, clen) : -1;
nodes[left].rank = r;
if (r >= 0) {
bpe_heap_item_t item = { r, left };
heap_push(heap, &hn, item);
}
}
}
free(nodes);
free(heap);
return nsym;
}
static int tok_bpe(const unsigned char *piece, int plen) {
int has_unmapped = 0;
for (int i = 0; i < plen; i++) {
if (is_unmapped_byte(piece[i])) {
has_unmapped = 1;
break;
}
}
if (!has_unmapped) return tok_bpe_raw(piece, plen);
unsigned char stack_buf[256];
unsigned char *buf = (plen <= (int)sizeof(stack_buf))
? stack_buf
: (unsigned char *)malloc((size_t)plen);
if (!buf) return (plen + 3) / 4;
int flen = 0;
for (int i = 0; i < plen; i++) {
if (!is_unmapped_byte(piece[i])) buf[flen++] = piece[i];
}
int res = tok_bpe_raw(buf, flen);
if (buf != stack_buf) free(buf);
return res;
}
static int tok_count_slice(const unsigned char *t, int len) {
int total = 0, i = 0;
while (i < len) {
int end = tok_piece_end(t, len, i);
total += tok_bpe(t + i, end - i);
i = end;
}
return total;
}
/* ----------------------------- public --------------------------------- */
int tok_count(const char *text, int len) {
if (!text || len <= 0) return 0;
pthread_once(&tok_once, tok_init);
const unsigned char *t = (const unsigned char *)text;
int total = 0, i = 0;
while (i < len) {
int m = i, alen = 0;
while (m < len) {
if (t[m] == '<' || t[m] == '/') {
alen = tok_match_added(t, len, m);
if (alen > 0) break;
}
m++;
}
if (m > i) total += tok_count_slice(t + i, m - i);
if (alen > 0) {
total += 1;
i = m + alen;
} else {
break;
}
}
return total;
}
int tok_count_str(const char *text) {
if (!text) return 0;
return tok_count(text, (int)strlen(text));
}