UNPKG

ag-charts-community

Version:

Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue

306 lines (305 loc) 12.1 kB
/** * Represents a splice operation to be applied to an array. * Designed for direct use with Array.splice() for efficient mutations. */ export interface SpliceOperation { /** Index at which to start the splice (in current array state) */ index: number; /** Number of elements to delete from this position */ deleteCount: number; /** Number of new elements to insert at this position */ insertCount: number; /** Optional source indices for preserved elements (for tracking) */ sourceIndices?: number[]; } /** * Tracks transformations from original array indices to final array indices. * Optimized for efficient array mutations using splice operations. */ export interface IndexTransformationMap { /** Original array length before any transactions */ originalLength: number; /** Final array length after all transactions */ finalLength: number; /** * Splice operations to transform the array. * Applied from back to front to avoid index shifting issues. * Each operation represents a single splice call. */ spliceOps: SpliceOperation[]; /** Set of removed original indices */ removedIndices: Set<number>; /** Set of updated final indices (indices in the final array that have updated items) */ updatedIndices: Set<number>; /** Total number of prepended items */ totalPrependCount: number; /** Total number of appended items */ totalAppendCount: number; } /** * Helper functions to compute derived properties from IndexTransformationMap. * These avoid storing redundant computed values and enable optimization checks. */ /** * Check if the change is append-only (no prepends, no removals). * * **Optimization:** Append-only operations don't shift existing indices, * so preserved items don't need to be marked as "moved". * * @param indexMap - The index transformation map to check * @returns True if only appends occurred * * @example * ```typescript * if (isAppendOnly(changeDesc.indexMap)) { * // Skip tracking moved items - nothing moved * } * ``` */ export declare function isAppendOnly(indexMap: IndexTransformationMap): boolean; /** * Check if the change is prepend-only (no appends, no removals). * * **Optimization:** Prepend-only operations shift ALL existing items by a fixed offset, * so we can bulk-mark them as moved without iteration. * * @param indexMap - The index transformation map to check * @returns True if only prepends occurred * * @example * ```typescript * if (isPrependOnly(changeDesc.indexMap)) { * // All items shifted by totalPrependCount * markAllAsMoved(changeDesc.indexMap.totalPrependCount); * } * ``` */ export declare function isPrependOnly(indexMap: IndexTransformationMap): boolean; /** * Check if no items were removed (may have prepends/appends). * * **Optimization:** When no removals occurred, items are either: * - In their original positions (if no prepends) * - Shifted by a fixed offset (if prepends occurred) * * @param indexMap - The index transformation map to check * @returns True if no removals occurred * * @example * ```typescript * if (hasNoRemovals(changeDesc.indexMap) && indexMap.totalPrependCount > 0) { * // All original items shifted by prepend count * } * ``` */ export declare function hasNoRemovals(indexMap: IndexTransformationMap): boolean; /** Check if only in-place updates occurred (no adds/removes). */ export declare function isUpdateOnly(indexMap: IndexTransformationMap): boolean; /** * Check if only removals occurred (no prepends, appends, or other insertions). * * **Optimization:** Removal-only operations preserve sort order and uniqueness: * - Removing values can't create duplicates (only reduce them) * - Removing values can't change ascending to descending order * - KEY_SORT_ORDERS metadata can be preserved */ export declare function hasOnlyRemovals(indexMap: IndexTransformationMap): boolean; /** Check if all removals are contiguous starting at index 0. */ export declare function hasContiguousRemovalsAtStart(indexMap: IndexTransformationMap): boolean; /** Returns the count of contiguous removals from index 0, or 0 if removals are absent/non-contiguous. */ export declare function contiguousRemovalCountAtStart(removedIndices: Set<number>): number; /** Check for rolling window pattern: contiguous removals at start + appends at end. */ export declare function isRollingWindow(indexMap: IndexTransformationMap): boolean; /** * Abstract description of changes to be applied to source data. * Provides precise index mapping for optimized incremental updates. * * **Responsibilities:** * - Describes what changed (splice operations, index mappings, prepend/append counts) * - Provides transformation methods for applying changes to arrays * - Enables iteration over preserved/moved elements * * **Usage Pattern:** * 1. DataSet builds DataChangeDescription from pending transactions * 2. DataController passes it to DataModel for incremental updates * 3. DataModel uses methods below to transform keys, columns, and invalidity arrays * * **Design Note:** * This class intentionally separates "change description" from "transaction management" (DataSet) * and "data processing" (DataModel). This enables: * - Multiple consumers (DataController, DataModel) * - Independent testing * - Clear separation of concerns */ export declare class DataChangeDescription { /** * Map from original to final indices. * * Contains splice operations, removed indices, and counts needed for transformations. * Access directly for low-level operations (e.g., updating banded domains). */ readonly indexMap: IndexTransformationMap; private readonly prependValues; private readonly appendValues; private readonly insertionValues; constructor(indexMap: IndexTransformationMap, insertions: { prependValues: unknown[]; appendValues: unknown[]; insertionValues: unknown[]; }); /** * Get all indices that were removed from the original array, sorted ascending. * * @returns Array of removed indices (e.g., [2, 5, 8]) * * @example * ```typescript * const removed = changeDesc.getRemovedIndices(); * console.log(`Removed ${removed.length} items at indices: ${removed}`); * ``` */ getRemovedIndices(): number[]; /** * Get all indices that were updated in the final array, sorted ascending. * * @returns Array of updated indices (e.g., [1, 3, 7]) * * @example * ```typescript * const updated = changeDesc.getUpdatedIndices(); * console.log(`Updated ${updated.length} items at indices: ${updated}`); * ``` */ getUpdatedIndices(): number[]; /** * Iterate over preserved elements, mapping source index to destination index. * Only calls callback for elements that were NOT removed. * * **Use this for:** * - Tracking which elements moved (when sourceIndex !== destIndex) * - Generating diff metadata (added/removed/moved items) * - Understanding index shifts caused by prepends/removes * * @param callback - Called for each preserved element with (sourceIndex, destIndex) * * @example Detecting moved items * ```typescript * const movedItems = new Set<number>(); * changeDesc.forEachPreservedIndex((srcIdx, destIdx) => { * if (srcIdx !== destIdx) { * movedItems.add(destIdx); * } * }); * ``` */ forEachPreservedIndex(callback: (sourceIndex: number, destIndex: number) => void): void; /** * Get the values that were prepended to the beginning of the array. * * These values are stored during change description construction and can be used * to avoid reprocessing prepended data. * * @returns Array of prepended values in order * * @example Processing prepended data * ```typescript * const prependedData = changeDesc.getPrependedValues<DataRow>(); * for (const row of prependedData) { * processRow(row); * } * ``` */ getPrependedValues<T = unknown>(): T[]; /** * Get the values that were appended to the end of the array. * * These values are stored during change description construction and can be used * to avoid reprocessing appended data. * * @returns Array of appended values in order * * @example Processing appended data * ```typescript * const appendedData = changeDesc.getAppendedValues<DataRow>(); * for (const row of appendedData) { * processRow(row); * } * ``` */ getAppendedValues<T = unknown>(): T[]; /** * Get the values that were inserted at arbitrary indices. * * These values are stored during change description construction and can be used * to avoid reprocessing inserted data. * * @returns Array of insertion values in the order they appear in splice operations * * @example Processing inserted data * ```typescript * const insertedData = changeDesc.getInsertionValues<DataRow>(); * for (const row of insertedData) { * processRow(row); * } * ``` */ getInsertionValues<T = unknown>(): T[]; /** * Applies the transformation to an array in-place using native Array operations. * This is a zero-copy operation that mutates the array directly. * * **Use this for:** * - Transforming processed data arrays (keys, columns, invalidity) * - Applying prepends, removals, and appends in a single pass * - Maintaining synchronization between data and processed arrays * * **How it works:** * 1. Applies splice operations in order (prepends, removals, appends) * 2. Calls processInsertion callback for each inserted element * 3. Mutates the array in-place for zero-copy efficiency * * @param array - The array to transform in-place (will be mutated) * @param processInsertion - Callback to generate values for inserted indices * * @example Transforming a column array * ```typescript * // Transform processed column to match new data layout * const insertionCache = new Map(); // Pre-computed processed values * changeDesc.applyToArray(columnArray, (destIndex) => { * return insertionCache.get(destIndex) ?? defaultValue; * }); * ``` * * @example Transforming an invalidity array * ```typescript * // Transform invalidity flags to match new data * changeDesc.applyToArray(invalidityArray, (destIndex) => { * const cached = insertionCache.get(destIndex); * return cached?.hasInvalidKey ?? false; * }); * ``` */ applyToArray<T>(array: T[], processInsertion: (destIndex: number) => T, onRemove?: (removed: T[], op: SpliceOperation) => void): void; /** * Applies the transformation to a `Uint8Array`, returning a **new** typed array. * TypedArrays are fixed-length so in-place splice is not possible. * * **Fast path** (rolling window, prepend+append, removals-only): copies preserved * blocks via `TypedArray.set()` + `subarray()` — maps to C-level memcpy in V8. * * **Slow path** (mid-array insertions): element-by-element copy via * `forEachPreservedIndex()` with insertion-aware destination adjustment. * * @param arr - Source typed array to transform * @param defaultValue - Value for newly inserted positions (default 0) * @returns A new Uint8Array with transformations applied, or the original * `arr` reference when no structural changes occurred (same length, * no removals, no splice ops) */ applyToTypedArray(arr: Uint8Array, defaultValue?: number): Uint8Array; /** * Collect mid-array insertion positions from splice ops (excluding prepend and append). * Returns sorted ascending by destination index. */ private collectMidArrayInsertions; }