fix(reader): 书索引build_index全量逐字FreeType排版优化 — rdft_advance加(px,cp)->advance缓存(开放寻址4096槽,与glyph缓存分离,load_font/set_px同清), 测量从O(全书字符)降到O(唯一码点~2600); 大书(10MB)打开 rd_book_open 23.9s->140ms(~170x), npages一致布局确定保留; 点条目不再用户态busy循环冻死(非驱动/锁屏, 是UI线程同步全量建索引); 0warning, 宿主test_reader_core 68-0/loaders_smoke/book_smoke/formats_smoke PASS

This commit is contained in:
AskaEth committed 2026-09-04 21:01:56 +08:00
1 parent 5e67100231
commit 68710d7ed8
1 file changed
+51 -3
+51 -3
View File
@@ -15,6 +15,13 @@ typedef struct {
int bw, bh, pitch, left, top;
} glyph_slot;
/* advance 测量缓存:rdft_advance 逐字测量是排版索引用热路径(大书每次开/重排全量逐字测),
* 同一码点在全书反复出现(中文常用字 ~几千)。独立的小型开放寻址表缓存 (px,cp)→advance,
* 避免对重复码点反复 FT_Load_Glyph,把测量成本从 O(全书字符) 摊到 O(唯一码点)。
* 与 glyph_slot 缓存分离:glyph 缓存存「已光栅化位图」,此处只存 advance(不栅格化、不占位图)。 */
#define RD_ADV_SLOTS 4096 /* 幂 2;开放寻址线性探测 */
typedef struct { uint64_t key; int adv; } adv_slot;
struct rdft {
FT_Library lib;
FT_Face face;
@@ -23,6 +30,7 @@ struct rdft {
int nslots; /* 容量 */
int used; /* 在线条目数 */
uint64_t clock; /* 单调戳 */
adv_slot *adv; /* advance 缓存(排版测量) */
int has_font;
};
@@ -36,6 +44,8 @@ int rdft_init(rdft **r)
x->nslots = 512;
x->slots = (glyph_slot *)calloc((size_t)x->nslots, sizeof(glyph_slot));
if (!x->slots) { FT_Done_FreeType(x->lib); free(x); return -6; }
x->adv = (adv_slot *)calloc(RD_ADV_SLOTS, sizeof(adv_slot));
if (!x->adv) { free(x->slots); FT_Done_FreeType(x->lib); free(x); return -6; }
x->used = 0;
x->clock = 1;
*r = x;
@@ -48,6 +58,7 @@ int rdft_load_font(rdft *r, const char *path)
if (r->face) { FT_Done_Face(r->face); r->face = NULL; }
if (r->slots) { for (int i = 0; i < r->nslots; i++) free(r->slots[i].bmp); }
if (r->slots) memset(r->slots, 0, (size_t)r->nslots * sizeof(glyph_slot));
if (r->adv) memset(r->adv, 0, (size_t)RD_ADV_SLOTS * sizeof(adv_slot));
r->used = 0;
if (FT_New_Face(r->lib, path, 0, &r->face) != 0) { r->has_font = 0; return -1; }
r->has_font = 1;
@@ -61,8 +72,9 @@ void rdft_set_px(rdft *r, int px)
if (px <= 0) px = r->px;
r->px = px;
if (r->face) FT_Set_Pixel_Sizes(r->face, 0, (FT_UInt)px);
/* key 含字号,清缓存防旧字号位图残留 */
/* key 含字号,清缓存防旧字号位图残留(glyph + advance 同清,key 同含 px) */
if (r->slots) { for (int i = 0; i < r->nslots; i++) free(r->slots[i].bmp); memset(r->slots, 0, (size_t)r->nslots * sizeof(glyph_slot)); }
if (r->adv) memset(r->adv, 0, (size_t)RD_ADV_SLOTS * sizeof(adv_slot));
r->used = 0;
}
@@ -147,18 +159,53 @@ int rdft_glyph(rdft *r, uint32_t cp, int *adv_x,
return 0;
}
/* (px,cp)→advance 缓存:开放寻址线性探测;key==0 为空槽哨兵(key=(px<<32)|cp,px≥1 恒非 0)。 */
static uint32_t adv_hash(uint64_t key)
{
/* 乘法散列取高位,把 64 位 key(px 在高位、cp 在低位)散布到 12 位表。 */
return (uint32_t)((key * 2654435761ULL) >> 22) & (RD_ADV_SLOTS - 1);
}
static int adv_find(const rdft *r, uint64_t key, int *adv)
{
uint32_t h = adv_hash(key);
for (uint32_t i = 0; i < RD_ADV_SLOTS; i++) {
uint32_t idx = (h + i) & (RD_ADV_SLOTS - 1);
uint64_t k = r->adv[idx].key;
if (k == 0) return 0;
if (k == key) { *adv = r->adv[idx].adv; return 1; }
}
return 0;
}
static void adv_put(rdft *r, uint64_t key, int adv)
{
uint32_t h = adv_hash(key);
for (uint32_t i = 0; i < RD_ADV_SLOTS; i++) {
uint32_t idx = (h + i) & (RD_ADV_SLOTS - 1);
if (r->adv[idx].key == 0 || r->adv[idx].key == key) {
r->adv[idx].key = key; r->adv[idx].adv = adv; return;
}
}
/* 表满:丢弃(下次未命中直接算) */
}
int rdft_advance(rdft *r, uint32_t cp, int *adv_x)
{
if (!r || !r->face || !adv_x) return -1;
uint64_t key = ((uint64_t)(uint32_t)r->px << 32) | cp;
if (adv_find(r, key, adv_x)) return 0;
FT_UInt gi = FT_Get_Char_Index(r->face, cp);
if (r->px <= 0) FT_Set_Pixel_Sizes(r->face, 0, (FT_UInt)r->px);
if (FT_Load_Glyph(r->face, gi, FT_LOAD_NO_HINTING | FT_LOAD_TARGET_LIGHT) != 0) {
*adv_x = r->face->glyph->advance.x >> 6;
int a = r->face->glyph->advance.x >> 6;
*adv_x = a;
adv_put(r, key, a);
return 0;
}
int a = (int)((r->face->glyph->advance.x >= 0 ? r->face->glyph->advance.x + 0x20
: r->face->glyph->advance.x - 0x20) >> 6);
*adv_x = a < 0 ? 0 : a;
a = a < 0 ? 0 : a;
*adv_x = a;
adv_put(r, key, a);
return 0;
}
@@ -209,6 +256,7 @@ void rdft_free(rdft *r)
if (!r) return;
if (r->slots) { for (int i = 0; i < r->nslots; i++) free(r->slots[i].bmp); }
free(r->slots);
free(r->adv);
if (r->face) FT_Done_Face(r->face);
if (r->lib) FT_Done_FreeType(r->lib);
free(r);