32 lines
3.0 KiB
TypeScript
32 lines
3.0 KiB
TypeScript
/**
|
||
* 解析器的统一精确字节读取边界。默认读不可变 Blob,也可注入带块证明的 reader。
|
||
* 这里检查范围/长度与取消,不负责整源SHA、JSON语法或业务记录;源级读取失败不能降格为可跳过的坏记录。
|
||
*/
|
||
import {fault} from '../canvas-core/values.ts';
|
||
|
||
/** 会破坏整个源读取可信性的错误集合,供上层区分单记录失败与源级暂停/失败。 */
|
||
const failures=new Set(['import_source_changed','invalid_import_byte_range','import_read_capacity','import_source_closed','import_source_read_failed','invalid_import_read_proof','import_read_proof_interpreter_unavailable']);
|
||
/** 只按 code 是否在固定集合分类,不校验错误对象的来源;其它码(含一般 import_aborted)不在集合内。
|
||
* 取消是否应优先由读取调用方看 signal;本分类器不是所有源错误/取消的通用判定器。 */
|
||
export const isImportByteFailure=(error:unknown)=>failures.has((error as {code?:string}|undefined)?.code??'');
|
||
|
||
/** Optional immutable-source port. Implementations return exactly the requested bytes. */
|
||
/** 可选只读端口声明完整长度,必须返回所请求区间的精确字节,不能截断或换源。 */
|
||
export type ImportByteReader={readonly byteLength:number;read(start:number,end:number,signal:AbortSignal):Promise<Uint8Array>};
|
||
/** 校验安全整数范围,允许 end 超出 EOF 并截到 bytes.size;start 超出 EOF 则拒绝。
|
||
* reader 长度须等于 Blob 长度,但本函数不逐字节比较 Blob/reader,也不证明同长度源未被替换。
|
||
* 外来 Uint8Array 结果复制为自有缓冲,默认 Blob 路径返回此次新读缓冲;不缓存结果或做并发合并。
|
||
* 空范围仍会调用 reader/Blob 读取,是否优化零 I/O 由端口决定;bytes 依赖调用方已验证的 Blob 合同。 */
|
||
export async function readImportRange(bytes:Blob,reader:ImportByteReader|undefined,start:number,end:number,signal:AbortSignal):Promise<Uint8Array<ArrayBuffer>>{
|
||
if(signal.aborted)throw fault('import_aborted');
|
||
if(!Number.isSafeInteger(start)||!Number.isSafeInteger(end)||start<0||end<start||start>bytes.size||reader&&reader.byteLength!==bytes.size)throw fault('invalid_import_byte_range');
|
||
const limit=Math.min(end,bytes.size);let value:Uint8Array;
|
||
// 保留已识别源错误,其它I/O异常统一为source_read_failed;取消优先返回import_aborted。
|
||
try{value=reader?await reader.read(start,limit,signal):new Uint8Array(await bytes.slice(start,end).arrayBuffer());}
|
||
catch(error){if(signal.aborted)throw fault('import_aborted');if(isImportByteFailure(error))throw error;throw fault('import_source_read_failed');}
|
||
if(signal.aborted)throw fault('import_aborted');
|
||
// 长度或响应类型不符视为源改变,不能让解析器在缺字节内容上继续产出业务修改。
|
||
if(!(value instanceof Uint8Array)||value.byteLength!==limit-start)throw fault('import_source_changed');
|
||
return reader?new Uint8Array(value):value as Uint8Array<ArrayBuffer>;
|
||
}
|