- core: 编码检测(BOM→UTF-8严格→GB18030/Big5统计, Big5与GB重叠按GB18030兜底, §3.5风险注明) + GB18030双字节码表(uint16[126][190]=47880B, tools/gen_gb18030_table.py 生成, 生成物入仓) + 流式排版器(贪心断行+Kinsoku避头尾+分页+重排锚定, rd_meas_fn 测量回调替 FreeType, 宿主可测) + TXT 流式加载器(按段读, 不整文件入内存, \n 恒独立换行可安全分段) - engine: FreeType 封装(FT_New_Face 按需读文件, NO_HINTING/LIGHT 灰阶, LRU 512 key=码点+字号, blend565, rdft_advance 只测宽不栅格化) - app: 书架(扫 Books/ 识别 5 后缀, 最近读置顶, 空态提示) + 阅读页(W_CUSTOM 画布 3 栏翻页 +中央菜单占位(字号±/行距±/返回) +顶栏进度%/章节名) + 书桥(段表+flat 页索引, 渲染, 重排回锚) + 进度持久化(MeowData/os.meow.reader/progress/, path hash→seg/ch/fs/ls/ts) - 取舍: 正文字体 FreeType 直读 TF MeowOS/fonts/ TTF/OTF; 无字体回退 SDK 位图字库(meow_ui_text, 未打包字体(许可/体积), 全 glyph 兜底 R2); 页位图缓存当前+前后各1页(3×691KB), 翻页动画默认无 - 内存实测(宿主 --wrap malloc, 73页书): 书结构 130KB + 当前段刮擦~1.6MB(行/页容量按 n 偏高,R2 收紧) + 字形缓存~650KB + 3页位图 2.07MB ≈ 4.45MB 动态, 在 ≤8MB 阅读态预算内 - 测试: 宿主 core 58/58(enc/gb18030金标准/kinsoku/page/reflow/txt); qemu-mipsel ft_smoke PASS + book_smoke(12页/渲染有墨/翻页/重排回锚) PASS; reader 交叉 -Wall -Wextra 0 warning - 产物: reader(mipsel 静态1.2MB)+build_reader.sh(vendor FreeType 可复现); vendor/二进制 gitignore
94 lines
3.6 KiB
C
94 lines
3.6 KiB
C
/* test/mem_account.c — 书桥动态内存记账(宿主,--wrap=malloc/calloc/realloc/free)。
|
||
* 目的:实测「阅读态」动态堆(书桥 + FreeType 字形缓存 + 段刮擦缓冲),给 R1 内存账。
|
||
* 用一本 ~500KB 中文书:open(索引构建)→ render 首页/中间页 → 报告 current/peak。
|
||
* 页位图(阅读 UI 持有 3×691KB)不在本 harness 内,报告里另加。
|
||
* 用法:./test/mem_account_host <font.otf>
|
||
*/
|
||
#include "book.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
/* --- malloc 包装跟踪 --- */
|
||
static size_t g_cur = 0, g_peak = 0;
|
||
void *__real_malloc(size_t); void *__real_calloc(size_t,size_t);
|
||
void *__real_realloc(void*,size_t); void __real_free(void*);
|
||
static size_t alloc_size(const void *p);
|
||
void *__wrap_malloc(size_t n)
|
||
{
|
||
void *p = __real_malloc(n); size_t sz = alloc_size(p);
|
||
g_cur += sz; if (g_cur > g_peak) g_peak = g_cur; return p;
|
||
}
|
||
void *__wrap_calloc(size_t n, size_t sz)
|
||
{
|
||
void *p = __real_calloc(n, sz); size_t s = alloc_size(p);
|
||
g_cur += s; if (g_cur > g_peak) g_peak = g_cur; return p;
|
||
}
|
||
void *__wrap_realloc(void *old, size_t n)
|
||
{
|
||
size_t os = old ? alloc_size(old) : 0;
|
||
void *p = __real_realloc(old, n); size_t ns = alloc_size(p);
|
||
g_cur = g_cur - os + ns; if (g_cur > g_peak) g_peak = g_cur; return p;
|
||
}
|
||
void __wrap_free(void *p)
|
||
{
|
||
if (p) { size_t s = alloc_size(p); g_cur -= s; }
|
||
__real_free(p);
|
||
}
|
||
/* 用 malloc_usable_size 取实际块(glibc) */
|
||
static size_t alloc_size(const void *p)
|
||
{
|
||
if (!p) return 0;
|
||
/* 免 glibc 头:直接用 __builtin 不可靠;用 malloc_usable_size 需声明。 */
|
||
extern size_t malloc_usable_size(void *);
|
||
return malloc_usable_size((void*)p);
|
||
}
|
||
|
||
static void write_book(const char *path, int chapters)
|
||
{
|
||
FILE *f = fopen(path, "wb");
|
||
for (int c = 0; c < chapters; c++) {
|
||
fprintf(f, "第%d章 测试章节标题用于排版验证\n", c + 1);
|
||
for (int p = 0; p < 12; p++)
|
||
fprintf(f, "第%d章第%d节:这是一段用于内存记账的中文正文,验证流式排版、FreeType 光栅与 "
|
||
"页位图缓存的内存占用是否在预算内。English mixed in too.\n", c + 1, p + 1);
|
||
}
|
||
fclose(f);
|
||
}
|
||
|
||
int main(int argc, char **argv)
|
||
{
|
||
const char *font = (argc > 1) ? argv[1] : "/home/Aska/Project/R3ProII/font/src/MiSans-Regular.otf";
|
||
const char *txt = "/tmp/rdbook_mem.txt";
|
||
write_book(txt, 40); /* ~40 章 ≈ 几百 KB */
|
||
|
||
size_t before_open = g_peak;
|
||
rd_book *b = NULL;
|
||
int rc = rd_book_open(&b, txt, font, 20, 130, 480, 720, 16, 20, 62);
|
||
if (rc != 0) { printf("open failed %d\n", rc); return 1; }
|
||
size_t after_open = g_cur;
|
||
size_t peak_open = g_peak - before_open;
|
||
int np = rd_book_pages(b);
|
||
|
||
int W = 480, H = 720;
|
||
uint16_t *buf = (uint16_t *)malloc((size_t)W * H * 2);
|
||
size_t peak0 = g_peak;
|
||
rd_book_render(b, 0, buf, W * 2, 0xFFFF, 0x0000);
|
||
rd_book_render(b, np / 2, buf, W * 2, 0xFFFF, 0x0000);
|
||
rd_book_render(b, np - 1, buf, W * 2, 0xFFFF, 0x0000);
|
||
size_t peak_render = g_peak - peak0;
|
||
size_t after_render = g_cur;
|
||
|
||
printf("pages=%d\n", np);
|
||
printf("open : cur=%zuB peak=%zuB (%.1f KiB / %.1f KiB)\n",
|
||
after_open, peak_open, after_open / 1024.0, peak_open / 1024.0);
|
||
printf("render: cur=%zuB peak=%zuB (+%zuB) (%.1f KiB / %.1f KiB)\n",
|
||
after_render, peak_render, peak_render, after_render / 1024.0, peak_render / 1024.0);
|
||
printf("pagebuf(3x480x720x2=%zuB=%.2fMiB) in UI layer, not here\n",
|
||
(size_t)W * H * 2 * 3, (double)W * H * 2 * 3 / (1024.0 * 1024.0));
|
||
|
||
free(buf);
|
||
rd_book_close(b);
|
||
return 0;
|
||
}
|