首次入库,远端 https://git.gvsds.com/GVSDS/Viper_VET 。 19 个文件全部为源码/配置,无构建产物: App/main.vp、App/lib/*.vp(VCache / VCheck / VCli / VDirs / VFs / VGuard / VGuid / VJson / VLog / VPe / VProc / VScan / __init__) 1.json、LICENSE、README.md、project.vpj、.gitignore 既有 .gitignore 已覆盖本仓的产物目录(**/output/、**/temp/、**/.tpv_build/) 与 *.exe / *.obj / *.o / *.img / *.iso / __pycache__/ / .cache/; 实测 `git add -A --dry-run` 仅 19 个文件、零产物混入,故未改动 .gitignore。
259 lines
14 KiB
Plaintext
259 lines
14 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import stdlib
|
||
import memhub
|
||
import string
|
||
import json
|
||
import lib.VFs as VFs
|
||
import lib.VLog as VLog
|
||
import lib.VCache as VCache
|
||
|
||
|
||
# ============================================================
|
||
# VJson - JSON 构建(单条对象 / 整轮数组,全部走仓库 json 库)
|
||
#
|
||
# 本轮改造(需求 ①/②):
|
||
# * 不再手工拼 `{"k":"v",...}`、不再手写 JsonEscape:条目改为用库的
|
||
# json.object + json.object_set + json.string_val 构造,再由 json.write 序列化;
|
||
# 转义(\ " \n \r \t)与 UTF-8 原样透传由库保证(includes/json/__writer.vp:39-66)。
|
||
# * 输出为**紧凑单行**:库的 write 忽略 pretty 参数、且从不写空白/换行
|
||
# (includes/json/__writer.vp:69-137、:151-155),故整轮数组与其中每个对象在同一行。
|
||
# * 数组框架('[' / ',' / ']' / 结尾换行)仍由本模块拼接:库的 write 只写单个输出缓冲
|
||
# (缺省 cap = 4096,includes/json/__writer.vp:143-148),整轮数组(最多 256 条)
|
||
# 无法一次交给库写,故逐条 write 再拼接。
|
||
#
|
||
# 库接口(静态核验,详见交付报告「能力与用法清单」):
|
||
# json.object(pool) -> rawptr[JsonValue]
|
||
# json.string_val(pool, s: str) -> rawptr[JsonValue] # 内部复制一份字符串
|
||
# json.object_set(pool, obj, key: str, val) -> 无返回 # 追加序 = 输出键序
|
||
# json.write(pool, val, pretty: bool, cap: t.CSizeT = 4096) -> str
|
||
# # pretty 被忽略,恒紧凑单行;超出 cap 返回 None
|
||
# json.parse(pool, src: str) -> rawptr[JsonValue]
|
||
# pool 形参类型 rawptr[memhub.MemManager](vtable 基类)⇒ MemBuddy / MemPool 都可传
|
||
# (先例:includes/requests/https.vp:823,883 传 rawptr[memhub.MemPool])。
|
||
#
|
||
# 用途约定:本模块声明的 Json_Buf 同时供 stdout 与实际写盘的缓存文件使用
|
||
# (调用点见 VScan.RunScanRound),二者逐字节相同。
|
||
#
|
||
# 失败处理(fail-closed):条目在分配前先过 Json_Pool.AllocOk(Need) 预检(BuildEntry),
|
||
# 预检不足即跳过该条目(返回 None);逐条跳过原因(含路径)仅 --verbose 输出,默认由
|
||
# EmitJson 的轮末聚合("本轮有 N 条被跳过")给出计数——库内部的 pool.alloc 不会在本模块
|
||
# 路径上走到 includes/memhub.vp:262-272 的 printf + exit(1);跳过不写 Json_Buf,
|
||
# 故本轮数组仍是合法单行 JSON(末尾 ']' 与换行照常闭合)。
|
||
#
|
||
# 依赖:VCache(CacheCount/CacheExe… 读表)、VFs(AnsiToUtf8/CopyRaw)、VLog。
|
||
#
|
||
# 内存归属(进程生命周期,均不释放):
|
||
# * Json_Buf / Conv_Buf 由 Vet_Mb(main 注入的 MemBuddy)分配;
|
||
# * Json_Arena 由 stdlib.malloc 分配;
|
||
# * Json_Pool(MemPool 管理器对象)由 memhub.NewMemPool 在堆上就地构造
|
||
# ——JsonInit 的栈帧在返回后失效,管理器对象必须在堆上(见 memhub.vp 的
|
||
# NewMemPool 注释与 Debugger/memhub-lifetime.md)。
|
||
# ============================================================
|
||
|
||
JSON_BUF_CAP: t.CDefine = 1048576 # 一轮 JSON 数组缓冲 1 MiB
|
||
JSON_POOL_CAP: t.CDefine = 65536 # 逐条序列化工作池 64 KiB(每条目 reset 复用)
|
||
JSON_ENTRY_MAX: t.CDefine = 4096 # 单条上限 = json 库 write 的缺省 cap(4096)
|
||
JSON_ENTRY_SLACK: t.CDefine = 512 # 单条固定语法开销上界(实测 77:键/值引号 24 + 键名 40 + 冒号 6 + 逗号 5 + 大括号 2)
|
||
|
||
# 单条序列化在 Json_Pool 上的固定开销上界(BuildEntry 的 AllocOk 预检的一部分)。
|
||
# 组成(一次 BuildEntry 的全部池分配;条目级 reset 之后 offset=0):
|
||
# * 7 个 JsonValue 节点(1 个对象 + 6 个字符串值)× 单节点上界 72 字节 = 504
|
||
# (JsonValue 9 字段 × 8 字节上界,includes/json/__init__.vp:23-39,与 VCache
|
||
# 解析树的估算口径一致);
|
||
# * 6 个键名副本(含 NUL)46 字节:id 3 + executable 11 + version 8 + arch 5
|
||
# + source 7 + displayName 12(includes/json/__init__.vp:225-229);
|
||
# * json.write 的输出缓冲 4096 字节(includes/json/__writer.vp:143-148,缺省 cap);
|
||
# * 余量 512 字节:6 个值副本各带一个结尾 NUL,加条内 20 次池分配按
|
||
# MEMHUB_ALIGN=8 向上取整的浪费(每次 ≤7,合计 ≤140)。
|
||
# 合计 504 + 46 + 4096 + 512 = 5158。
|
||
JSON_ENTRY_POOL_FIXED: t.CDefine = 5158
|
||
|
||
# 单条里 executable / displayName 的取值长度上界:二者都由 Conv_Buf(VFs.CONV_CAP 字节)
|
||
# 拷出(WideCharToMultiByte 以 Cap 封顶并写结尾 NUL,失败时 VFs.CopyRaw 写空串),
|
||
# 故每个值 ≤ CONV_CAP-1 字符。写 2047 = VFs.CONV_CAP(2048) - 1 的字面量:模块级
|
||
# t.CDefine 不引用跨模块常量,VFs.CONV_CAP 只在函数体内参与算术。
|
||
JSON_ENTRY_CONV_MAX: t.CDefine = 2047
|
||
|
||
# 由 App/main.vp 注入
|
||
_mbuddy: rawptr[memhub.MemBuddy]
|
||
|
||
Json_Buf: bytes = None # 整轮数组文本(stdout 与缓存文件共用)
|
||
Json_Len: int = 0 # Json_Buf 的有效字节数
|
||
Conv_Buf: bytes = None # ANSI → UTF-8 转换缓冲
|
||
Json_Arena: bytes = None # 序列化工作池 arena(stdlib.malloc,常驻)
|
||
Json_Pool: rawptr[memhub.MemPool] # 序列化工作池(堆上就地构造;见 JsonInit)
|
||
|
||
|
||
# 分配 JSON 静态缓冲与序列化工作池(进程生命周期内复用)。返回 1=成功,0=失败
|
||
def JsonInit() -> int:
|
||
global Json_Buf, Conv_Buf, Json_Arena, Json_Pool
|
||
Json_Buf = _mbuddy.alloc(JSON_BUF_CAP)
|
||
Conv_Buf = _mbuddy.alloc(VFs.CONV_CAP)
|
||
Json_Arena = stdlib.malloc(JSON_POOL_CAP)
|
||
if Json_Buf is None or Conv_Buf is None or Json_Arena is None:
|
||
return 0
|
||
# 池对象走堆上就地构造(memhub.NewMemPool):Json_Pool 要被 EmitJson/BuildEntry
|
||
# 跨帧使用,而本函数的栈帧在返回时即失效——构造表达式在这里会把池对象的存储
|
||
# 落在本帧栈上(IR:VJson 的 alloca %MemPool + __before_init__ + __init__)。
|
||
# arena 由 stdlib.malloc 常驻;池对象同样常驻,进程生命周期内不 free。
|
||
Pool: rawptr[memhub.MemPool] = memhub.NewMemPool(Json_Arena, JSON_POOL_CAP)
|
||
if Pool is None:
|
||
return 0
|
||
Json_Pool = Pool
|
||
return 1
|
||
|
||
|
||
# 供 VScan 写 stdout / VCache 写缓存文件读取结果
|
||
def JsonBuf() -> bytes:
|
||
return Json_Buf
|
||
|
||
|
||
def JsonLen() -> int:
|
||
return Json_Len
|
||
|
||
|
||
def JReset():
|
||
global Json_Len
|
||
Json_Len = 0
|
||
if Json_Buf is not None:
|
||
Json_Buf[0] = 0
|
||
|
||
|
||
# 把字面量/已序列化文本追加到 Json_Buf(容量不足返回 0,不写穿)
|
||
def JWrite(S: str) -> int:
|
||
global Json_Len
|
||
if S is None or Json_Buf is None:
|
||
return 0
|
||
n: t.CSizeT = string.strlen(S)
|
||
if t.CSizeT(Json_Len) + n + 1 > t.CSizeT(JSON_BUF_CAP):
|
||
return 0
|
||
i: t.CSizeT = 0
|
||
while i < n:
|
||
Json_Buf[Json_Len + t.CInt(i)] = S[i]
|
||
i += 1
|
||
Json_Len += t.CInt(n)
|
||
Json_Buf[Json_Len] = 0
|
||
return 1
|
||
|
||
|
||
# 构造第 Idx 条 JSON 对象并交给库序列化为紧凑单行文本。
|
||
# 返回该条文本(分配在 Json_Pool 上,长度 < JSON_ENTRY_MAX);None=已跳过。
|
||
# 每条跳过的具体原因(含被处理路径)仅在 --verbose 时逐条给出;默认安静模式下由
|
||
# EmitJson 的轮末聚合("本轮有 N 条被跳过")给出计数,既不静默异常也不逐路径刷屏。
|
||
def BuildEntry(Idx: int) -> str:
|
||
# 池容量预检(fail-closed):本条在 Json_Pool 上的全部分配必须先确认放得下,
|
||
# 否则库内部的 pool.alloc 会走到 includes/memhub.vp:262-272 的 printf + exit(1)。
|
||
# 预检在 EmitJson 的条目级 reset 之后执行(池 offset=0),故单次 AllocOk 覆盖整条;
|
||
# Need 由固定开销 + 全部值的长度上界构成,是条内池占用(含 20 次分配的 8 字节对齐
|
||
# 浪费)的上界,推导见 JSON_ENTRY_POOL_FIXED 与 JSON_ENTRY_CONV_MAX 的注释。
|
||
ValNeed: t.CSizeT = t.CSizeT(JSON_ENTRY_CONV_MAX) + t.CSizeT(JSON_ENTRY_CONV_MAX)
|
||
ValNeed = ValNeed + string.strlen(VCache.CacheId(Idx))
|
||
ValNeed = ValNeed + string.strlen(VCache.CacheVer(Idx))
|
||
ValNeed = ValNeed + string.strlen(VCache.CacheArch(Idx))
|
||
ValNeed = ValNeed + string.strlen(VCache.CacheSrc(Idx)) # source("workspace"/"check")
|
||
Need: t.CSizeT = t.CSizeT(JSON_ENTRY_POOL_FIXED) + ValNeed
|
||
if Json_Pool.AllocOk(Need) == 0:
|
||
VLog.VerboseFmtIS(" 警告: JSON 工作池不足(本条需 %zu 字节),已跳过: %s\n",
|
||
Need, VCache.CacheExe(Idx))
|
||
return None
|
||
Obj: rawptr[json.JsonValue] = json.object(Json_Pool)
|
||
if Obj == None:
|
||
VLog.VerboseStr(" 警告: JSON 对象分配失败(工作池不足),已跳过该条目\n")
|
||
return None
|
||
# 键插入顺序 = 输出顺序(库按 child→next 链表输出,见 __writer.vp:92-107)。
|
||
# executable / displayName 按既有约定转 UTF-8,且**转换后立即交给库复制**:
|
||
# Conv_Buf 随后要复用于 displayName,晚一步用就会让 executable 串被覆盖。
|
||
json.object_set(Json_Pool, Obj, "id", json.string_val(Json_Pool, VCache.CacheId(Idx)))
|
||
if VFs.AnsiToUtf8(VCache.CacheExe(Idx), Conv_Buf, VFs.CONV_CAP) == 0:
|
||
VFs.CopyRaw(Conv_Buf, VFs.CONV_CAP, VCache.CacheExe(Idx))
|
||
json.object_set(Json_Pool, Obj, "executable", json.string_val(Json_Pool, str(Conv_Buf)))
|
||
json.object_set(Json_Pool, Obj, "version", json.string_val(Json_Pool, VCache.CacheVer(Idx)))
|
||
json.object_set(Json_Pool, Obj, "arch", json.string_val(Json_Pool, VCache.CacheArch(Idx)))
|
||
# source 取自缓存条目(扫盘="workspace";check 通道="check",见 VCache.Cache_Src)
|
||
json.object_set(Json_Pool, Obj, "source", json.string_val(Json_Pool, VCache.CacheSrc(Idx)))
|
||
if VFs.AnsiToUtf8(VCache.CacheDisp(Idx), Conv_Buf, VFs.CONV_CAP) == 0:
|
||
VFs.CopyRaw(Conv_Buf, VFs.CONV_CAP, VCache.CacheDisp(Idx))
|
||
json.object_set(Json_Pool, Obj, "displayName", json.string_val(Json_Pool, str(Conv_Buf)))
|
||
|
||
# 结构自检:库在各分配点"失败即不追加/字段留空",此处逐字段确认 6 个键都带上了
|
||
# 键名与字符串值(池耗尽时若漏检会写出 `{null:"…"}` 这类非法 JSON)。
|
||
# 顺带累计各值长度,供容量守卫使用。
|
||
Ok: int = 1
|
||
N: int = 0
|
||
Sum: t.CSizeT = 0
|
||
cu: rawptr[json.JsonValue] = Obj.child
|
||
while cu != None:
|
||
if cu.key == None or cu.vtype != json.JSON_STRING or cu.str_val == None:
|
||
Ok = 0
|
||
else:
|
||
Sum = Sum + string.strlen(cu.str_val)
|
||
N += 1
|
||
cu = cu.next
|
||
if Ok == 0 or N != 6:
|
||
VLog.VerboseStr(" 警告: JSON 条目构造不完整(工作池不足),已跳过该条目\n")
|
||
return None
|
||
|
||
# 容量守卫:库的 write 缺省 cap=4096(=JSON_ENTRY_MAX),超出已会返回 None 且不写穿
|
||
# (includes/json/__writer.vp:143-154);此处提前守卫只是避免走到"整条被丢弃 + 告警"。
|
||
# 上界推导(Sum = 6 个值长度和):
|
||
# 实际输出 = 各值转义长度和 + 固定语法 77(键/值引号 24 + 键名 40 + 冒号 6 + 逗号 5 + 大括号 2)
|
||
# ≤ 2*Sum + 77 ≤ 2*Sum + JSON_ENTRY_SLACK(512)
|
||
# 故 2*Sum + JSON_ENTRY_SLACK ≤ JSON_ENTRY_MAX ⇒ 实际输出 ≤ 3661 < 4095,含结尾 NUL 也放得下。
|
||
if 2 * Sum + t.CSizeT(JSON_ENTRY_SLACK) > t.CSizeT(JSON_ENTRY_MAX):
|
||
VLog.VerboseFmtIS(" 警告: JSON 条目过长(超过 %zu 字节),已跳过: %s\n",
|
||
t.CSizeT(JSON_ENTRY_MAX), VCache.CacheExe(Idx))
|
||
return None
|
||
|
||
Txt: str = json.write(Json_Pool, Obj, False)
|
||
if Txt is None:
|
||
VLog.VerboseStr(" 警告: JSON 序列化失败(工作池不足),已跳过该条目\n")
|
||
return None
|
||
return Txt
|
||
|
||
|
||
# 把当前缓存整体构建为**单行** JSON 数组文本写入 Json_Buf(stdout 与缓存文件共用)
|
||
def EmitJson() -> int:
|
||
JReset()
|
||
if Json_Pool is None:
|
||
# 不变式:JsonInit(VScan.ScanInit 调用)成功才会走到 EmitJson;此分支仅为不崩。
|
||
VLog.LogStr(" 警告: JSON 工作池未初始化,跳过本轮 JSON 输出\n")
|
||
return 0
|
||
Truncated: int = 0
|
||
Skipped: int = 0
|
||
Count: int = 0
|
||
if JWrite("[") == 0:
|
||
return 0
|
||
i: int = 0
|
||
while i < VCache.CacheCount():
|
||
# 条目级回收:工作池峰值 = 单条(节点 + 字符串副本 + 库的 4096 字节输出缓冲)
|
||
Json_Pool.reset()
|
||
Txt: str = BuildEntry(i)
|
||
if Txt is None:
|
||
Skipped += 1
|
||
else:
|
||
n: t.CSizeT = string.strlen(Txt)
|
||
Need: t.CSizeT = n + 3 # 自身 + 结尾 ']' + '\n' + NUL
|
||
if Count > 0:
|
||
Need = Need + 1 # 分隔逗号
|
||
if t.CSizeT(Json_Len) + Need > t.CSizeT(JSON_BUF_CAP):
|
||
# 缓冲不足:跳过该条(不再像旧实现那样写出半条),末尾仍闭合 ']'
|
||
Truncated = 1
|
||
else:
|
||
if Count > 0:
|
||
JWrite(",")
|
||
JWrite(Txt)
|
||
Count += 1
|
||
i += 1
|
||
if JWrite("]") == 0:
|
||
Truncated = 1
|
||
if JWrite("\n") == 0:
|
||
Truncated = 1
|
||
if Truncated != 0:
|
||
VLog.LogFmt2(" 警告: JSON 输出缓冲不足,本轮已截断(已写 %zu 条 / 缓存 %zu 条)\n",
|
||
t.CSizeT(Count), t.CSizeT(VCache.CacheCount()))
|
||
if Skipped != 0:
|
||
VLog.LogFmt1(" 警告: 本轮有 %zu 条 JSON 条目因过长/工作池不足被跳过\n",
|
||
t.CSizeT(Skipped))
|
||
return 0
|