Yield native import scans by elapsed time and bounded work
This commit is contained in:
@@ -1,18 +1,19 @@
|
||||
import {createImportBudget,type ImportScheduling} from './import-budget.ts';
|
||||
import type {ImportSource,ImportLine} from './imports.ts';
|
||||
import {fault,validJson} from '../canvas-core/values.ts';
|
||||
/** Seekable JSON array reader. Cursors are byte offsets after '[' or a record comma.
|
||||
* Structure errors end the source; previously published records remain explicit. */
|
||||
export function createJsonArrayImportSource(options:{bytes:Blob;fingerprint:string;maxRecordBytes?:number;chunkBytes?:number;yieldTask?:()=>Promise<void>}):ImportSource{
|
||||
export function createJsonArrayImportSource(options:{bytes:Blob;fingerprint:string;maxRecordBytes?:number;chunkBytes?:number}&ImportScheduling):ImportSource{
|
||||
const bytes=options.bytes,max=options.maxRecordBytes??256*1024,chunk=options.chunkBytes??16384;
|
||||
if(!(bytes instanceof Blob)||!options.fingerprint||!Number.isSafeInteger(max)||max<1||!Number.isSafeInteger(chunk)||chunk<1||chunk>max)throw fault('invalid_import_source');
|
||||
let cache={offset:-1,value:new Uint8Array()};const whitespace=(b:number)=>[9,10,13,32].includes(b),yieldTask=options.yieldTask??(()=>new Promise<void>(resolve=>setTimeout(resolve,0)));
|
||||
let cache={offset:-1,value:new Uint8Array()};const whitespace=(b:number)=>[9,10,13,32].includes(b);
|
||||
return Object.freeze({fingerprint:options.fingerprint,parserVersion:3,async read(start:number,signal:AbortSignal):Promise<ImportLine|null>{
|
||||
if(!Number.isSafeInteger(start)||start<0||start>bytes.size)throw fault('invalid_import_cursor');
|
||||
if(start>0&&start===bytes.size){if(signal.aborted)throw fault('import_aborted');return null;}
|
||||
if(bytes.size===0)throw fault('invalid_json_array');
|
||||
let cursor=start,work=0;
|
||||
let cursor=start;const budget=createImportBudget(signal,options);
|
||||
async function peek(){if(signal.aborted)throw fault('import_aborted');if(cursor>=bytes.size)return -1;if(cursor<cache.offset||cursor>=cache.offset+cache.value.length){cache={offset:cursor,value:new Uint8Array(await bytes.slice(cursor,cursor+chunk).arrayBuffer())};if(signal.aborted)throw fault('import_aborted');}return cache.value[cursor-cache.offset];}
|
||||
async function advance(){cursor++;if(++work>=16384){work=0;await yieldTask();if(signal.aborted)throw fault('import_aborted');}}
|
||||
function advance(){cursor++;return budget.step();}
|
||||
async function spaces(){while(whitespace(await peek()))await advance();}
|
||||
const failure=(error:string):ImportLine=>({cursor:start,nextCursor:bytes.size,key:`byte:${start}`,error});
|
||||
await spaces();if(start===0){if(await peek()!==91)return failure('invalid_json_array');await advance();await spaces();}
|
||||
|
||||
Reference in New Issue
Block a user