import type { SchedulerEvent } from './scheduler.types'; /** * Range index over scheduler events. * * ## The access pattern this is built for * * Once per frame, for the ~40–60 resources currently in the grid's render * window, answer: *which events on this resource overlap `[t0, t1)`?* Plus, on * a drag, mutate one event ~60 times a second without re-indexing anything. * * ## Why per-resource sorted arrays, not an interval tree or time buckets * * The query is always partitioned on an **exact** resource key, and the visible * subset is tiny relative to the whole. That changes which structure wins: * * - A **global interval tree** would return every resource's events for the * range and leave the caller to discard ~98% of them. With 500k events over * ~5k resources, a week-wide query returns roughly a thousand where a dozen * are wanted — every frame — and it must rebalance on every drag frame. * - **Time buckets** over-fetch the same way, force a bucket size that cannot * suit both minute and year views, and require long events to be duplicated * into every bucket they span (a six-month booking lands in 180 day-buckets), * turning 500k events into millions of entries. * - **Per-resource sorted arrays** let the `Map` do the 5k → 60 reduction in * hash lookups, leaving a binary search plus a short scan over the ~100 events * on that one resource. Mutation is an array splice, not a rebalance. * * ## The max-end augmentation * * Sorting by start alone is not enough: a year-long booking that began far to * the left of the window still overlaps it, and a naive scan would have to start * at index 0 to find it. `maxEnd[i]` holds `max(ends[0..i])`, which is * non-decreasing *because* the array is sorted — and therefore binary * searchable. One extra search finds the earliest index that can possibly reach * into the window, so the scan stays proportional to the answer rather than to * the resource's history. * * Storage is `Float64Array`/`Int32Array` rather than objects: 500k events cost * ~14 MB contiguous, and the hot loops touch numbers only. The event objects * themselves live once in a pool and are read for the handful that render. */ export declare class EventIndex { /** Dense event pool; an event's numeric handle is its index here. */ private readonly pool; /** Per-resource sorted columns. */ private readonly byResource; /** Event id -> pool handle, for O(1) update/remove by id. */ private readonly handleById; /** * Handles excluded from queries. * * A drag mutates an event up to 60 times a second; re-indexing each time would * be pure waste, since the intermediate positions are never queried. Instead * the dragged event is suppressed here, the ghost is drawn from a plain * variable, and the index is updated exactly once on drop. */ private readonly suppressed; /** * Bumped whenever a resource's events change, so lane layouts can be memoized * against it and a pure scroll reuses them. */ private readonly epochs; /** Replaces the entire contents. O(n log n). */ load(events: readonly SchedulerEvent[]): void; /** Number of indexed events. */ get size(): number; /** Resolves a handle to its event. */ get(handle: number): SchedulerEvent | undefined; /** Resolves an event id to its handle, or `-1`. */ handleOf(id: string): number; /** Mutation counter for a resource, for memoizing derived layout. */ epochOf(resourceId: string): number; /** * Collects handles of events on `resourceId` overlapping `[t0, t1)`. * * Appends into `out` rather than allocating, because this runs once per * visible resource per frame and a fresh array each time would be the single * largest source of garbage in the renderer. * * O(log n + k), where k is the number of results. */ query(resourceId: string, t0: number, t1: number, out: number[]): void; /** Adds one event. O(n) memmove within its resource. */ add(event: SchedulerEvent): number; /** * Replaces an event, re-indexing only if its position actually moved. * * The common case — a drag committing a new start on the same resource, where * the sorted position is unchanged — is two array writes plus a bounded * max-end repair. */ update(next: SchedulerEvent): void; /** Removes an event by id. */ remove(id: string): void; /** * Hides events from queries without touching the index — the drag path. * * @param handles - Pass an empty set to clear. */ suppress(handles: Iterable): void; private buildColumns; private insertHandle; private removeHandle; private bumpEpoch; } //# sourceMappingURL=event-index.d.ts.map