- reader分段(只txt): core/seg.{h,c} 512KB码位对齐切段(尾字完整)+manifest+按需读单段; book.c rd_book_open只布局每页所在段(治CPU100%/返回无效) load_seg只读当前段(非txt原路径); reader.c 进度实时保存(翻页/刮擦/字号/编码dirty+on_tick节流落盘+退出即时写,锚点存段号,崩溃不丢进度); rdtxt.c fread替代fgetc(去开书扫描卡顿); Phase2(双段拼接+4%释放)留待
- 面板: /api/ams/bg加fg/fg_count(后台保持向后兼容)+POST /api/ams/:pkg/killfg(前台救命:优先am kill,launcher拒则pid直信号SIGTERM->SIGKILL绕过门控)+前端/#/bg前台组'终止'绿徽标+确认弹窗(一键清仍只后台不误杀前台); POST /api/input/gesture(返回=左缘右滑/回桌面=底部上滑,注入滑动轨迹复用SDK手势)+前端屏幕控制页加返回/回桌面MD3按钮
0warning;宿主test_reader_core 72-0/seg_smoke 19-0/loaders_smoke/book_smoke/formats_smoke PASS;面板selftest/node check PASS
145 lines
6.0 KiB
C
145 lines
6.0 KiB
C
/* test/seg_smoke.c — os.meow.reader 分段流式缓存宿主单测(纯 C,无 SDK/Freetype)。
|
|
*
|
|
* 覆盖:
|
|
* S1 UTF-8 大文件按 ~512KB 切段:段数>1、每段为合法 UTF-8(码位对齐,不切在字符中间)、
|
|
* 拼接后 == 原文件字节。
|
|
* S2 GBK(GB18030) 大文件切段:每段 raw 在完整双字节边界、逐段 rd_to_utf8 拼接 == 整文件
|
|
* 一次转换(边界不把双字节对拆开)。
|
|
* S3 幂等:同 (size,mtime,enc) 再次 build 段数不变、不重写。
|
|
* S4 每段原始字节长与 rdseg_load 读回一致。
|
|
*/
|
|
#include "seg.h"
|
|
#include "rdenc.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static int g_pass = 0, g_fail = 0;
|
|
#define CHECK(cond, msg) do { if (cond) { g_pass++; } else { g_fail++; printf("FAIL %s:%d %s\n", __FILE__, __LINE__, msg); } } while (0)
|
|
|
|
/* 生成一个 utf8 大文件:CJK 3 字节 + ASCII + 换行,填充到 > 512KB */
|
|
static void write_utf8_big(const char *path)
|
|
{
|
|
FILE *f = fopen(path, "wb");
|
|
const uint8_t cjk[] = { 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD }; /* 你好 */
|
|
size_t total = 0;
|
|
while (total < RDSEG_SIZE + 100000) {
|
|
for (int i = 0; i < 16; i++) { fwrite(cjk, 1, sizeof cjk, f); total += sizeof cjk; }
|
|
const char *line = "这是一段用于验证分段对齐的中文正文,English words 123.\n";
|
|
size_t ll = strlen(line); fwrite(line, 1, ll, f); total += ll;
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
/* 生成一个 GBK 大文件:双字节对(你好世界)重复,填到 > 512KB */
|
|
static void write_gbk_big(const char *path)
|
|
{
|
|
FILE *f = fopen(path, "wb");
|
|
const uint8_t gbk[] = { 0xC4, 0xE3, 0xBA, 0xC3, 0xCA, 0xC0, 0xBD, 0xE7 }; /* 你好世界 */
|
|
size_t total = 0;
|
|
while (total < RDSEG_SIZE + 100000) {
|
|
for (int i = 0; i < 16; i++) { fwrite(gbk, 1, sizeof gbk, f); total += sizeof gbk; }
|
|
fputc('\n', f); total += 1;
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
static void test_align_utf8(const char *seg_dir, const char *path)
|
|
{
|
|
/* 每段合法 UTF-8 */
|
|
int n = rdseg_n(seg_dir);
|
|
CHECK(n >= 2, "utf8 segs >= 2");
|
|
for (int i = 0; i < n; i++) {
|
|
size_t rl = 0;
|
|
CHECK(rdseg_rawlen(seg_dir, i, &rl) == 0 && rl > 0, "utf8 rawlen>0");
|
|
uint8_t *b = (uint8_t *)malloc(rl ? rl : 1);
|
|
size_t got = 0;
|
|
CHECK(rdseg_load(seg_dir, i, b, rl, &got) == 0 && got == rl, "utf8 load==rawlen");
|
|
CHECK(rd_utf8_validate(b, got) == 1, "utf8 seg valid (no mid-char split)");
|
|
free(b);
|
|
}
|
|
/* 拼接后 == 原文件字节 */
|
|
size_t fz = 0;
|
|
FILE *src = fopen(path, "rb"); fseek(src, 0, SEEK_END); fz = (size_t)ftell(src); fseek(src, 0, SEEK_SET);
|
|
uint8_t *full = (uint8_t *)malloc(fz ? fz : 1);
|
|
(void)!fread(full, 1, fz, src); fclose(src);
|
|
size_t o = 0, ok = 1;
|
|
for (int i = 0; i < n; i++) {
|
|
size_t rl = 0; rdseg_rawlen(seg_dir, i, &rl);
|
|
uint8_t *b = (uint8_t *)malloc(rl ? rl : 1);
|
|
size_t got = 0; rdseg_load(seg_dir, i, b, rl, &got);
|
|
if (o + got > fz || memcmp(full + o, b, got) != 0) ok = 0;
|
|
o += got; free(b);
|
|
}
|
|
CHECK(ok && o == fz, "utf8 concat == original bytes");
|
|
free(full);
|
|
}
|
|
|
|
static void test_gbk_roundtrip(const char *seg_dir, const char *path)
|
|
{
|
|
int n = rdseg_n(seg_dir);
|
|
CHECK(n >= 2, "gbk segs >= 2");
|
|
/* 整文件一次转换(金标准) */
|
|
FILE *src = fopen(path, "rb"); fseek(src, 0, SEEK_END); long fz = ftell(src); fseek(src, 0, SEEK_SET);
|
|
uint8_t *raw = (uint8_t *)malloc((size_t)fz ? (size_t)fz : 1);
|
|
(void)!fread(raw, 1, (size_t)fz, src); fclose(src);
|
|
uint8_t *full = (uint8_t *)malloc((size_t)fz * 2 + 64);
|
|
size_t fl = 0;
|
|
int rc = rd_gb18030_to_utf8(raw, (size_t)fz, full, (size_t)fz * 2 + 64, &fl);
|
|
CHECK(rc == RD_ENC_OK, "gbk full convert ok");
|
|
free(raw);
|
|
/* 逐段转换拼接 */
|
|
uint8_t *concat = (uint8_t *)malloc((size_t)fz * 2 + 64);
|
|
size_t o = 0, ok = 1;
|
|
for (int i = 0; i < n; i++) {
|
|
size_t rl = 0; rdseg_rawlen(seg_dir, i, &rl);
|
|
uint8_t *b = (uint8_t *)malloc(rl ? rl : 1);
|
|
size_t got = 0; rdseg_load(seg_dir, i, b, rl, &got);
|
|
uint8_t *u8 = (uint8_t *)malloc(got * 2 + 64);
|
|
size_t ul = 0;
|
|
if (rd_gb18030_to_utf8(b, got, u8, got * 2 + 64, &ul) != RD_ENC_OK) ok = 0;
|
|
if (o + ul > (size_t)fz * 2 + 64) ok = 0;
|
|
if (ok) { memcpy(concat + o, u8, ul); o += ul; }
|
|
free(u8); free(b);
|
|
}
|
|
CHECK(ok && o == fl && memcmp(concat, full, fl) == 0, "gbk per-seg convert concat == full convert");
|
|
free(concat); free(full);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
rdseg_set_base("/tmp/meow_seg_base");
|
|
char dir[600];
|
|
const char *u8 = "/tmp/seg_u8.txt", *gbk = "/tmp/seg_gbk.txt";
|
|
write_utf8_big(u8); write_gbk_big(gbk);
|
|
|
|
/* UTF-8 */
|
|
CHECK(rdseg_dir_for(u8, dir, sizeof dir) == 0, "utf8 dir_for");
|
|
int n = rdseg_build(u8, RD_ENC_UTF8, dir);
|
|
CHECK(n >= 2, "utf8 build nsegs>=2");
|
|
test_align_utf8(dir, u8);
|
|
/* 幂等:同键再建段数不变 */
|
|
CHECK(rdseg_build(u8, RD_ENC_UTF8, dir) == n, "utf8 build idempotent");
|
|
|
|
/* GBK */
|
|
CHECK(rdseg_dir_for(gbk, dir, sizeof dir) == 0, "gbk dir_for");
|
|
n = rdseg_build(gbk, RD_ENC_GB18030, dir);
|
|
CHECK(n >= 2, "gbk build nsegs>=2");
|
|
test_gbk_roundtrip(dir, gbk);
|
|
|
|
/* 大文件不切(清空再建,确保得到多个段也覆盖单段路径) */
|
|
{
|
|
char small[600]; rdseg_set_base("/tmp/meow_seg_base2");
|
|
const char *tiny = "/tmp/seg_tiny.txt";
|
|
FILE *f = fopen(tiny, "wb"); fwrite("hello 你好 world\n", 1, strlen("hello 你好 world\n"), f); fclose(f);
|
|
CHECK(rdseg_dir_for(tiny, small, sizeof small) == 0, "tiny dir");
|
|
int tn = rdseg_build(tiny, RD_ENC_UTF8, small);
|
|
CHECK(tn == 1, "tiny build nsegs==1");
|
|
size_t rl = 0; CHECK(rdseg_rawlen(small, 0, &rl) == 0 && rl == strlen("hello 你好 world\n"), "tiny rawlen");
|
|
}
|
|
|
|
remove(u8); remove(gbk); remove("/tmp/seg_tiny.txt");
|
|
printf("seg_smoke: %d pass, %d fail\n", g_pass, g_fail);
|
|
return g_fail ? 1 : 0;
|
|
}
|