/** * Source-buffer residency for STEP models. * * A parsed model's descriptors index the raw STEP text by absolute * `[address, address + length)` byte ranges (the SoA `address_` / * `length_` columns). Historically the whole source buffer stayed * resident for the model's lifetime so any range could be read * synchronously. That pins the full file (hundreds of MB on large * models) for property access patterns that only ever touch a tiny * fraction of it after load. * * A {@link StepBufferProvider} abstracts "give me the bytes for this * range" behind two operations: * * - `acquire(address, length)` — SYNCHRONOUS. Returns a byte view * containing the range plus the absolute address of the view's * first byte, so callers can translate between absolute addresses * and view-relative cursors. Throws * {@link StepBufferNotResidentError} when the range is not * resident — synchronous extraction paths never wait. * - `ensureResident(address, length)` — ASYNCHRONOUS. Pages the * range in (from an external store) so a following `acquire` * succeeds. Async API surfaces (the web-ifc shim's property * methods) call this before entering synchronous extraction. * * Two implementations: * * - {@link ResidentStepBufferProvider} wraps the fully-resident * source buffer. `acquire` returns the whole buffer with offset 0 * (so view-relative cursors ARE absolute addresses) and * `ensureResident` resolves immediately. This is the default and * matches the historical behaviour bit-for-bit. * - {@link WindowedStepBufferProvider} pages fixed-size chunks from * a {@link StepExternalByteStore} with an LRU residency cap. * Records contained in one chunk are served as a view over it; * records straddling chunks get a per-record merged copy. Eviction * is advisory — descriptors that captured a chunk keep it alive * via their own reference, so correctness never depends on the * residency set; only memory does. * * The external store itself is environment-provided (OPFS in the * browser, a file or memory in node) — this module only defines the * read contract plus an in-memory implementation used by tests and * small models. */ /** * Get the absolute source address of `buffer[ 0 ]`. * * @param buffer A view handed out by a {@link StepBufferProvider}. * @return {number} The base address (0 for the resident source buffer). */ export declare function stepBufferBase(buffer: Uint8Array): number; /** * Read contract for the spilled source bytes. Implementations must * return exactly the requested range (clamped reads are the caller's * responsibility — the provider never requests past `byteLength`). */ export interface StepExternalByteStore { /** Total size of the stored byte sequence. */ readonly byteLength: number; /** * Read `length` bytes starting at `offset`. * * @param offset Absolute offset of the first byte to read. * @param length Number of bytes to read. * @return {Promise< Uint8Array >} The bytes — a standalone array * (byteOffset 0) of exactly `length` bytes. */ read(offset: number, length: number): Promise; } /** * In-memory {@link StepExternalByteStore}. Used by tests and as a * degenerate store when the caller wants windowed accounting without * real external storage. */ export declare class InMemoryStepByteStore implements StepExternalByteStore { private readonly bytes_; /** * Construct this around a byte array (not copied). * * @param bytes_ The backing bytes. */ constructor(bytes_: Uint8Array); /** * Total size of the stored byte sequence. * * @return {number} The byte length. */ get byteLength(): number; /** * Read a range as a standalone copy. * * @param offset Absolute offset of the first byte to read. * @param length Number of bytes to read. * @return {Promise< Uint8Array >} The bytes. */ read(offset: number, length: number): Promise; } /** * Result of a synchronous range acquisition: a byte view containing * the requested range and the absolute address of `buffer[ 0 ]`. * `address - offset` converts an absolute address into an index into * `buffer`. The buffer is always standalone (byteOffset 0) or the * original resident source buffer — both satisfy the extraction * layer's `new DataView( buffer.buffer )` zero-offset assumption. */ export interface StepBufferAcquisition { buffer: Uint8Array; offset: number; } /** * Thrown by {@link StepBufferProvider.acquire} when the requested * range is not resident. Reaching this means a synchronous extraction * ran without a preceding `ensureResident` — a caller-side sequencing * bug, not a recoverable data condition. */ export declare class StepBufferNotResidentError extends Error { readonly address: number; readonly length: number; /** * Construct this for a byte range. * * @param address Absolute start of the range. * @param length Length of the range. */ constructor(address: number, length: number); } /** * Residency provider for a model's source bytes. */ export interface StepBufferProvider { /** Total logical size of the source bytes. */ readonly byteLength: number; /** Bytes currently held resident by the provider itself. */ readonly residentBytes: number; /** * Synchronously acquire a view containing a byte range. * * @param address Absolute start of the range. * @param length Length of the range. * @throws { StepBufferNotResidentError } When the range isn't resident. * @return {StepBufferAcquisition} The view and its base address. */ acquire(address: number, length: number): StepBufferAcquisition; /** * Make a byte range resident so a following {@link acquire} succeeds. * * @param address Absolute start of the range. * @param length Length of the range. * @return {Promise< void >} Resolves when resident. */ ensureResident(address: number, length: number): Promise; } /** * Provider over a fully-resident source buffer — the default, and * bit-for-bit the historical behaviour: acquisitions are the source * buffer itself at offset 0, so view-relative cursors are absolute * addresses and nothing changes for existing extraction code. */ export declare class ResidentStepBufferProvider implements StepBufferProvider { private readonly buffer_; /** * Construct this over the resident source buffer. * * @param buffer_ The full source bytes. */ constructor(buffer_: Uint8Array); /** * Total logical size of the source bytes. * * @return {number} The byte length. */ get byteLength(): number; /** * Bytes currently held resident — the whole buffer. * * @return {number} The byte length. */ get residentBytes(): number; /** * Acquire the whole buffer at offset 0. * * @return {StepBufferAcquisition} The acquisition. */ acquire(): StepBufferAcquisition; /** * Always resident. * * @return {Promise< void >} Resolved promise. */ ensureResident(): Promise; } /** * Provider that pages fixed-size chunks from an external store with * an LRU residency cap. * * Concurrency note: `ensureResident` de-duplicates in-flight chunk * loads, so overlapping property reads for neighbouring records fetch * each chunk once. Eviction only drops the provider's own reference — * any descriptor that acquired a view over the chunk keeps that chunk * alive independently, so previously-materialised entities keep * working after eviction (they just pin their chunk until the * descriptor cache is invalidated). */ export declare class WindowedStepBufferProvider implements StepBufferProvider { private readonly store_; private readonly chunkBytes_; private readonly maxResidentChunks_; /** Resident chunks by chunk index; Map order doubles as LRU order. */ private readonly chunks_; /** In-flight chunk loads, de-duplicated by chunk index. */ private readonly loading_; /** * Chunks covered by an `ensureResident` call that hasn't returned * yet, refcounted. Eviction skips them: an overlapping ensure for a * different range must not evict chunks another caller has ensured * but not yet synchronously acquired (the acquire happens in the * caller's continuation, which can interleave with this one). */ private readonly ensurePins_; /** * Construct this over an external byte store. * * @param store_ The store holding the source bytes. * @param chunkBytes Chunk size in bytes (default 4MiB). * @param maxResidentChunks LRU residency cap in chunks (default 16). */ constructor(store_: StepExternalByteStore, chunkBytes?: number, maxResidentChunks?: number); /** * Total logical size of the source bytes. * * @return {number} The byte length. */ get byteLength(): number; /** * Bytes currently held resident by the provider. * * @return {number} The resident byte count. */ get residentBytes(): number; /** * Number of resident chunks (telemetry/tests). * * @return {number} The chunk count. */ get residentChunkCount(): number; /** * Touch a chunk for LRU recency. * * @param chunkIndex The chunk to touch. * @param chunk The chunk bytes. */ private touch_; /** * Synchronously acquire a view containing a byte range. * * Single-chunk ranges are served as the chunk itself (zero copy); * straddling ranges get a standalone merged copy spanning exactly * the requested range. * * @param address Absolute start of the range. * @param length Length of the range. * @throws { StepBufferNotResidentError } When any covering chunk isn't resident. * @return {StepBufferAcquisition} The view and its base address. */ acquire(address: number, length: number): StepBufferAcquisition; /** * Make a byte range resident, paging missing chunks from the store * and evicting least-recently-used chunks beyond the cap (never the * chunks needed by this call). * * @param address Absolute start of the range. * @param length Length of the range. * @return {Promise< void >} Resolves when resident. */ ensureResident(address: number, length: number): Promise; } //# sourceMappingURL=step_buffer_provider.d.ts.map