Asset(资源)
| 项 | 内容 |
|---|---|
| 契约版本 | asset@1 |
| 当前状态 | 测试中 |
| 源码目录 | src/core/asset/ |
| 页面定位 | 资源管线参考:从 VPath 到句柄的解析、读取、预算、解码、缓存与失效 |
上层只拿资源句柄,不见 URL,也不见协议。平台差异、包形态差异与格式差异都在管线内被吸收。
职责
负责:
- 统一的资源取用入口:虚拟路径(VPath,
assets/<ns>/<类型>/<路径>)到交付值的完整链路。 - 五段管线:解析来源与重定向、读取字节、预算核对、解码或经生成器产出、缓存与交付。
- 来源栈:只由资源包构成,后挂者优先。
- 六种交付形态:
bytes、text、json、texture、stream、font。 - 变体:字符串变体与字体子集;变体生成器接入点。
- 预算、缓存策略、指纹与失效通知。
不负责:
- 不识别外部输入格式,也不做翻译;外部格式只在隔离翻译层处理。
- 不绘制纹理、不播放音频、不排版文本;这些分别属于 Stage、Audio、Text。
- 不持有包清单与签名;包来源读取经
kernel:unit-files完成并逐次复核哈希。 - 不解释 VPath 以外的工作区或本机路径。
对外接口
服务
| 名称 | policy | 说明 |
|---|---|---|
asset:acquire(vpath, { as, variant?, priority?, cache? }) | trusted | 取句柄 { vpath, as, value }(冻结);调用方 effect 结束时自动释放 |
asset:release(handle) | trusted | 释放句柄;重复释放无效果;texture 的副本随即 close() |
asset:exists(vpath) | trusted | 当前是否有来源提供该路径 |
asset:list(prefix) | trusted | 前缀下可取到的路径,去重、按字典序;前缀须以 assets/ 开头 |
asset:fingerprint(vpath) | trusted | 生效来源文件内容的 sha256 |
asset:invalidate(target) | trusted | 让一个路径或以 / 结尾的前缀下已缓存内容失效 |
priority 与 cache 目前接收但暂不生效。
机制插槽
| 名称 | policy | 段 | 说明 |
|---|---|---|---|
asset:resolve-path | trusted | ① | 输入路径查询(含变体与来源栈只读视图),输出候选来源 |
asset:resolve-source-order | trusted | ① | 输入候选来源,输出排好序的候选来源 |
asset:resolve-decoding | trusted | ④ | 输入路径、形态、变体与来源,输出解码器条目名或 { producer };与解码器、生成器实现位同一审计强度 |
通知与实现位
| 名称 | 类别 | policy | 说明 |
|---|---|---|---|
asset:on-invalidate | 通知 | — | 载荷 { vpaths }:资源包卸载、新包遮住已有路径(含带来某个变体文件)、显式失效 |
asset:resolver@1 | 实现位(single) | public | 默认 exact:精确路径匹配,字符串变体的文件排在原文件之前 |
asset:fetcher@1 | 实现位(multi) | public | 按 scheme 分派;内置 pack,经 kernel:unit-files 读取并由 Kernel 逐次复核 sha256 |
asset:budget-policy@1 | 实现位(single) | public | 默认 builtin:单文件 ≤ 256 MiB、解码后单个资源 ≤ 512 MiB |
asset:decoder@1 | 实现位(multi) | trusted | 按格式分派;内置 bytes/text/json/image/font/audio |
asset:cache-policy@1 | 实现位(single) | public | 默认 builtin:引用归零后按最近最少使用保留,单条超过 512 MiB 不保留,空闲总量 ≤ 512 MiB |
asset:variant-producer@1 | 实现位(multi) | trusted | 内置 hca-pcm 与 font-subset |
条目形状:
| 实现位 | 形状 |
|---|---|
asset:resolver@1 | { resolve({ vpath, as, variant, layers }) → 候选来源[], list(prefix, layers) → string[] } |
asset:fetcher@1 | { fetch(来源) → Promise<Uint8Array> } |
asset:decoder@1 | { version, decode(bytes, { vpath, as }) → 值|Promise<值> } |
asset:budget-policy@1 | { check({ vpath, stage: 'file'|'decoded', size }) → 超限原因|undefined } |
asset:cache-policy@1 | { retain({ key, vpath, size }) → boolean, evict(空闲表) → 要丢弃的键[] },空闲表由旧到新 |
asset:variant-producer@1 | { version, produce({ vpath, as, variant, bytes }, { meta, decode, signal }) → Promise<值> } |
关键语义
五段管线
text
acquire(vpath, options)
├─ ① 解析:asset:resolve-path → asset:resolve-source-order
│ → 对每个 pack 候选经 platform:host.resolveAssetUrl 重定向
├─ ② 读取:asset:fetcher@1 按 scheme 取字节(pack 经 kernel:unit-files)
├─ ③ 预算:asset:budget-policy@1 在文件阶段与解码后各核对一次
├─ ④ 解码:asset:resolve-decoding → asset:decoder@1 或 asset:variant-producer@1
└─ ⑤ 缓存与交付:asset:cache-policy@1 决定保留/淘汰,返回冻结句柄来源栈只在 ① 段构造;② 段之后只用 ① 段交出的候选来源序列,不再回看来源栈。
六种交付形态
as | 默认解码 | 句柄里的值 | 副本语义 |
|---|---|---|---|
bytes | bytes | ArrayBuffer | 每个句柄一份 |
text | text | string | — |
json | json | 深冻结的对象 | 共享(已冻结) |
texture | image(浏览器解码) | ImageBitmap | 每个句柄一份 createImageBitmap(主份),释放即 close();主份随缓存条目回收 close() |
stream | audio(decodeAudioData);.hca → hca-pcm | { sampleRate, channels: Float32Array[], loop? },loop 为采样位置 { start, end }(end 不含) | 每个句柄一份 |
font | font;带 { subset } → font-subset | 已 load()、未装入文档的 FontFace,family 为 asset-<字体字节 sha256 前 12 位> | 每个句柄重新解码一份 |
补充:
decodeAudioData会重采样到上下文采样率;内置audio取 48 kHz,播放时由 Web Audio 再按设备采样率换算。- 解码体积按以下口径计算,用于预算与缓存:纹理为宽 × 高 × 4,PCM 为各声道字节之和,字节按长度,其余按文件字节。
- 字体生成器须经
ctx.decode('font', 字节)交付结果,管线据此为每个句柄重新解码。
变体
- 字符串变体:
v取同目录、扩展名前插@v的文件(a.png→a@2x.png);当前来源栈里没有就回退原文件。 - 字体子集:
{ subset: "<字符>" }只用于font;字符去重、按码位排序后取摘要进缓存键。 - 变体名不能为空、不能含
/,插入后须仍是合法 VPath。 - 新装载的包带来某个变体文件时,相关路径即失效并广播通知。
来源栈与缓存键
- 只有资源包进入来源栈;随
kernel:on-unit-loaded/kernel:on-unit-unloaded压入与移除,后挂者优先。 - 内置资源包在根插件之后最先装载,位于最底。
- 缓存键 = 来源包稳定 ID + 版本 + 实际路径 + VPath + 变体 + 形态 + 解码器(或生成器)条目名与版本。
- 产出条目的解码器或生成器被替换,或生成器经
decode用过的解码器被替换,缓存条目即视为过期。
失效
以下情况令相关缓存失效并广播 asset:on-invalidate:
- 资源包装载、卸载;
- 新包遮住已有路径,或带来某个变体文件;
- 显式调用
asset:invalidate; - Platform 的地址重定向钩子变化,或生效宿主后端切换(Asset 保守清空缓存);
- 实现位与机制插槽变化。
读取期间发生相关失效,本次结果以 invalidated 拒绝交付,由调用方重新取用;已交付句柄按通知约定处理。显式 asset:invalidate 即使当前还没有缓存条目也会推进失效代次,因此等待中的字体子集与指纹摘要会按当前来源重算。收到 platform:on-asset-url-change 时,Asset 清空缓存并对已缓存路径发失效通知。
包内逻辑地址重定向
- ① 段先得出候选来源,再对每个
pack候选调用platform:host.resolveAssetUrl。Platform 排在 Asset 之后装载,故调用时再经 Cordis 取服务。 - 目标可以是本次解析快照中任一已装载资源包的文件;包不在快照、版本不符、路径不合法或文件不存在即以
redirect-invalid报错。 - 取用、存在查询与指纹使用同一重定向;枚举仍列实际包内路径。
- 缓存键使用实际目标,来源包或目标包卸载都令相关逻辑路径失效。
当前实现范围
已完成:
- 六种交付形态、字符串变体与
{ subset }、预算与缓存的builtin条目、变体生成器接入点。 meta()读取同包<源文件路径>.meta.json;hca-pcm所需元数据缺失时明确报错,不猜测取值。- 契约用例在 Node 与系统 Edge 中通过;
texture/font/stream的浏览器解码用例只在网页端跑。 hca-pcm:在隔离环境的常驻会话(沙箱 iframe + Worker)里用 acgcore 整段解码 HCA v1.3–v3.0(ciph0/1/56),去掉编码器前置静音与末尾填充,按循环点换算样本位置;先只交文件头读出声道数与样本数,按预算判定解码后大小,超出即报budget、不解码。font-subset:在同一常驻会话里生成子集字体,再交font解码器做成FontFace;只接受 sfnt 封装的 TrueType(glyf)与 OpenType(CFF)单字体,WOFF/WOFF2/TTC/CFF2报不支持。
媒体 Worker 会话中同时在途的请求最多 16 个,主页面另最多排队 64 个;超过队列上限只拒绝该次请求。取消会通知 Worker,移除尚未开始的请求并丢弃迟到结果;来源适配器已经发出的外部读取不能撤回。
未完成/限制:
priority与cache接收但暂不生效。- CJK 的 CID 字体因子程序不裁、其他表原样保留,子集仍然较大。
- 隔离环境不可用时,
hca-pcm报unavailable。 - 完整阶段出口中的「全部资产可被资源包整体替换」尚未逐项核验。