/** * @packageDocumentation Read/write IOStream backed by a `Blob` or `File` object. * * Reads are performed lazily — each `BlobSegment` is fetched from the blob on * first access and the result is **cached on the segment**, so subsequent reads * of the same range are purely in-memory with no async overhead. When a * `readBlock` call spans multiple uncached segments, all outstanding * `arrayBuffer()` calls are issued in **parallel** via `Promise.all`, * minimising round-trips. * * Writes are captured in a **piece table** — a list of segments that are * either a byte-range reference into the original blob (`BlobSegment`) or a * small in-memory `Uint8Array` buffer (`BufferSegment`). The total logical * length is maintained as a cached field so that `length()` and most seeks are * O(1). * * The modified content can be assembled as a new `Blob` (preserving the * source MIME type) via {@link BlobStream.toBlob}. */ import { ByteVector } from "../byteVector.js"; import { type offset_t, Position } from "./types.js"; import { IOStream } from "./ioStream.js"; /** * A segment that references a byte range inside the original source blob. * * Once the range has been read, the raw bytes are stored in {@link cache} so * that future reads of overlapping ranges are served from memory without * issuing another `arrayBuffer()` call. * @internal */ export interface BlobSegment { /** Discriminant tag. */ kind: "blob"; /** Inclusive start offset within the source blob. */ start: number; /** Exclusive end offset within the source blob. */ end: number; /** * Populated on first fetch. Once set, reads from this segment are * in-memory and require no async I/O. */ cache?: Uint8Array; } /** * A segment that holds a small in-memory buffer representing inserted or * overwritten bytes. * @internal */ export interface BufferSegment { /** Discriminant tag. */ kind: "buffer"; /** The raw byte data. */ data: Uint8Array; } /** * A single entry in the {@link BlobStream} piece table. * @internal */ export type Segment = BlobSegment | BufferSegment; /** * A read/write {@link IOStream} backed by a browser/Node.js `Blob` (or * `File`). * * ### Reading * Each `BlobSegment` is fetched from the blob on first access and its bytes * are cached on the segment object. Subsequent reads of the same range are * served from the cache without any async I/O. When a single `readBlock` * call spans multiple uncached segments, all `arrayBuffer()` requests are * issued in parallel via `Promise.all`. * * ### Writing * A *piece table* tracks the logical content as an ordered list of * {@link Segment}s. Mutations only manipulate this list; they never copy the * original blob. The cached total length is kept up-to-date on every * mutation so that `length()` is O(1). * * ### Exporting * {@link toBlob} assembles a new `Blob` from `blob.slice()` references and * in-memory buffers — no full-file copy. The new blob's MIME type is copied * from the source blob. */ export declare class BlobStream extends IOStream { /** The original, unmodified source blob. */ private readonly _blob; /** MIME type captured from the source blob at construction time. */ private readonly _mimeType; /** Current read/write position in bytes from the logical start. */ private _position; /** The piece table — ordered list of segments forming the logical content. */ private _segments; /** * Cached total logical length in bytes. Maintained by every mutating * operation so that {@link length} and range-checks are O(1). */ private _length; /** * Creates a new `BlobStream` wrapping the given `Blob` or `File`. * * The blob's contents are **not** loaded into memory at construction time. * Each byte range is fetched on demand and cached for subsequent access. * * @param blob - The blob (or `File`) to stream. */ constructor(blob: Blob); /** * Returns the file name when the backing object is a `File`, otherwise `""`. */ name(): string; /** * Reads up to `length` bytes from the current position, spanning segment * boundaries as needed. All uncached `BlobSegment`s in the range are * fetched in parallel via `Promise.all` and their results are cached on the * segment for future reads. * * @param length - Maximum number of bytes to read. * @returns Resolves with a {@link ByteVector} containing the bytes read. * May be shorter than `length` if the logical end of stream is reached. */ readBlock(length: number): Promise; /** * Writes `data` at the current position, overwriting existing content and * extending the stream if necessary. Advances the position by * `data.length`. * * @param data - The bytes to write. */ writeBlock(data: ByteVector): Promise; /** * Inserts `data` at byte offset `start`, optionally replacing `replace` * bytes of existing content. Sets the position to `start + data.length`. * * @param data - The bytes to insert. * @param start - Byte offset at which to begin the insertion. * @param replace - Number of existing bytes to replace. Defaults to `0`. */ insert(data: ByteVector, start: offset_t, replace?: number): Promise; /** * Removes `length` bytes beginning at byte offset `start`. * * @param start - Byte offset of the first byte to remove. * @param length - Number of bytes to remove. */ removeBlock(start: offset_t, length: number): Promise; /** Returns `false` — BlobStream supports write operations. */ readOnly(): boolean; /** Returns `true` — BlobStream is always open. */ isOpen(): boolean; /** * Moves the read/write position within the stream. * * @param offset - Number of bytes to move. * @param position - Reference point for the seek. Defaults to * {@link Position.Beginning}. */ seek(offset: offset_t, position?: Position): Promise; /** Resets the read/write position to the beginning of the stream. */ clear(): Promise; /** Returns the current read/write position in bytes from the logical start. */ tell(): Promise; /** * Returns the total logical byte length of the stream in O(1) time. */ length(): Promise; /** * Truncates or zero-extends the stream to exactly `length` bytes. If the * current position exceeds the new length it is clamped. * * @param length - The desired stream length in bytes. */ truncate(length: offset_t): Promise; /** * Assembles a new `Blob` from the current piece table without loading the * full content into memory. Each {@link BlobSegment} becomes a * `blob.slice()` reference and each {@link BufferSegment} is passed as a raw * `Uint8Array`. The new blob's MIME type is copied from the source blob. * * @returns A new `Blob` reflecting all edits made to this stream. */ toBlob(): Blob; /** * Ensures there is a segment boundary at `offset` and returns the index of * the segment that starts at `offset`. If `offset` falls in the middle of a * segment that segment is split into two. * * When splitting a {@link BlobSegment} that has a populated {@link BlobSegment.cache cache}, * the cache is sub-divided so neither child needs to re-fetch. * * @param offset - Byte offset at which a boundary is required. * @returns The segment index where the boundary now exists. */ private _splitAt; /** * Removes the logical byte range `[start, start + length)` from the piece * table. Segments that overlap either boundary are split first so that only * whole segments need to be spliced out. * * **Note**: the caller is responsible for updating `_length`. * * @param start - Logical start offset of the range to remove. * @param length - Number of bytes to remove. */ private _removeRange; /** * Inserts a new segment into the piece table at logical byte offset * `offset`, splitting any existing segment that spans `offset`. * * **Note**: the caller is responsible for updating `_length`. * * @param offset - Logical byte offset at which the new segment is inserted. * @param seg - The segment to insert. */ private _insertAt; /** * Concatenates an array of `Uint8Array` chunks into a single contiguous * `Uint8Array`. * * @param chunks - Chunks to concatenate. * @returns A new `Uint8Array` containing all bytes in order. */ private _concat; } //# sourceMappingURL=blobStream.d.ts.map