22 lines
2.0 KiB
TypeScript
22 lines
2.0 KiB
TypeScript
/**
|
|
* 完整导入源的增量 SHA256:有界 Blob 分块读取,不把全文件放进 UI ArrayBuffer。
|
|
* 摘要是源完整性/稳定身份依据,不证明该文件有权限、schema 正确或所有记录已加入画布。
|
|
*/
|
|
import { fault } from '../canvas-core/values.ts';
|
|
import {createSha256} from './sha256.ts';
|
|
/** Complete-source SHA-256, with bounded chunk memory and cooperative fallback scheduling. */
|
|
/** 顺序 slice 读取完整 Blob,默认 64KiB 分块(配置上限 4MiB),空文件也返回合法摘要。
|
|
* 每块读前/读后及 yield 后检查取消;原生 arrayBuffer/yield 本身不被信号强制终止。
|
|
* 默认块间 setTimeout(0) 让出,只有仍有下一块才 yield;自定义 yieldTask 决定实际调度公平性。
|
|
* 块内 hash.update 为同步 CPU 工作,不会中途取消;读/让出错误直接传播,不返回部分摘要。 */
|
|
export async function importSourceDigest(bytes:Blob,signal?:AbortSignal,options:{chunkBytes?:number;yieldTask?:()=>Promise<void>}={}):Promise<string>{
|
|
const chunk=options.chunkBytes??64*1024,yieldTask=options.yieldTask??(()=>new Promise<void>(resolve=>setTimeout(resolve,0)));
|
|
if(!(bytes instanceof Blob)||!Number.isSafeInteger(chunk)||chunk<1||chunk>4*1024*1024)throw fault('invalid_digest_input');
|
|
/** 读/计算/让出边界统一检查 signal;异步读取即使无法中止,迟到字节也不再参与有效结果。 */
|
|
const check=()=>{if(signal?.aborted)throw fault('import_aborted');};check();const hash=createSha256();
|
|
// 只顺序持有当前块,hash 自身不累计输入;part 在本次迭代/yield 期间仍可存在,实际释放时机由 GC 决定。
|
|
// 空文件跳过读取/让出,最后 check 后直接 hex;有界分块不是整个应用进程的内存硬上限。
|
|
for(let cursor=0;cursor<bytes.size;cursor+=chunk){check();const part=await bytes.slice(cursor,cursor+chunk).arrayBuffer();check();hash.update(new Uint8Array(part));if(cursor+chunk<bytes.size){await yieldTask();check();}}
|
|
check();return hash.hex();
|
|
}
|