Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | 1x 1x 1x 17x 17x 68x 68x 68x 68x 68x 68x 2x 68x 68x 32x 32x 32x 32x 32x 32x 1x 1x 22x 22x 22x 22x 22x 3x 3x 3x 28x 28x 3x 2x 2x 3x 3x 1x 27x 27x 27x 248x 27x 27x 27x 221x 221x 248x 27x 27x 8x 8x 8x 8x 8x 8x 8x 8x 8x 22x 21x 22x 15x 22x 14x 6x 8x 2x 2x 2x 8x 8x 8x 8x 21x 21x 21x 21x 21x 176x 176x 155x 176x 21x 21x 21x 21x 21x 1x 699x 683x 699x 683x 683x 699x 1560x 1560x 1560x 683x 683x | import type { ByteRange, VFSAdapter } from './types.js';
/**
* Capability-aware wrappers over the optional streaming methods of
* {@link VFSAdapter}. Every function here works against any adapter: when the
* backend implements the method it is used directly, otherwise the behaviour is
* emulated on top of `read()`/`write()`. Callers never branch — they call
* `readRange(adapter, …)` and get the cheap path where one exists.
*/
/** Chunk size for the emulated paths and for MemoryAdapter. */
export const CHUNK_SIZE = 64 * 1024;
/**
* Blobs at or above this size take the streaming path through the engine
* instead of being held whole. Below it, one-shot reads and the native
* `crypto.subtle` digest are faster, and the memory does not matter.
*/
export const STREAM_THRESHOLD = 4 * 1024 * 1024;
/**
* True when the adapter can both read and write without materialising a whole
* file. Emulated streams still work, they just have no memory advantage — so
* the engine only routes big blobs through streaming when this holds.
*/
export function canStream(adapter: VFSAdapter): boolean {
return typeof adapter.readStream === 'function' && typeof adapter.writeStream === 'function';
}
/** Reads `[start, end)`. Falls back to reading everything and slicing. */
export async function readRange(
adapter: VFSAdapter,
path: string,
range: ByteRange = {},
): Promise<Uint8Array> {
if (adapter.readRange) return adapter.readRange(path, range);
const data = await adapter.read(path);
return data.slice(range.start ?? 0, range.end ?? data.byteLength);
}
/** Streams a file, or a range of it. Falls back to one chunk per CHUNK_SIZE. */
export async function readStream(
adapter: VFSAdapter,
path: string,
range: ByteRange = {},
): Promise<ReadableStream<Uint8Array>> {
if (adapter.readStream) return adapter.readStream(path, range);
return chunked(await readRange(adapter, path, range));
}
/** Writes a file from a stream. Falls back to buffering, then a single write. */
export async function writeStream(
adapter: VFSAdapter,
path: string,
): Promise<WritableStream<Uint8Array>> {
if (adapter.writeStream) return adapter.writeStream(path);
const chunks: Uint8Array[] = [];
return new WritableStream<Uint8Array>({
write(chunk) {
chunks.push(chunk.slice());
},
close: async () => {
await adapter.write(path, concat(chunks));
},
});
}
/** A readable over bytes already in hand. */
export function chunked(data: Uint8Array, size = CHUNK_SIZE): ReadableStream<Uint8Array> {
let offset = 0;
return new ReadableStream<Uint8Array>({
pull(controller) {
if (offset >= data.byteLength) {
controller.close();
return;
}
controller.enqueue(data.subarray(offset, Math.min(offset + size, data.byteLength)));
offset += size;
},
});
}
/**
* Drains `source` into `target`, one chunk at a time so neither side ever holds
* more than a chunk. `onChunk` sees every byte on the way past — that is where
* the running digest is fed from.
*
* `pipeTo()` would do the same, but not on every adapter: a wrapped
* `FileSystemWritableFileStream` is not always a real WritableStream.
*/
export async function pump(
source: ReadableStream<Uint8Array>,
target: WritableStream<Uint8Array>,
onChunk?: (chunk: Uint8Array) => void,
): Promise<void> {
const reader = source.getReader();
const writer = target.getWriter();
try {
for (;;) {
const { done, value } = await reader.read();
if (done) break;
if (!value || value.byteLength === 0) continue;
onChunk?.(value);
await writer.write(value);
}
await writer.close();
} catch (error) {
await writer.abort(error).catch(() => {
// the write side is already broken; the original error is what matters
});
throw error;
} finally {
reader.releaseLock();
}
}
/** Reads a stream to the end. Only for content known to fit in memory. */
export async function collect(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
const chunks: Uint8Array[] = [];
const reader = stream.getReader();
try {
for (;;) {
const { done, value } = await reader.read();
if (done) break;
if (value) chunks.push(value);
}
} finally {
reader.releaseLock();
}
return concat(chunks);
}
export function concat(chunks: Uint8Array[]): Uint8Array {
if (chunks.length === 1) return chunks[0] as Uint8Array;
let size = 0;
for (const chunk of chunks) size += chunk.byteLength;
const out = new Uint8Array(size);
let offset = 0;
for (const chunk of chunks) {
out.set(chunk, offset);
offset += chunk.byteLength;
}
return out;
}
|