export type EventId = number & { __brand: 'EventId' } export type EventIds = ItemIds export const enum EventIdConstants { FIRST_ID = 1, } export function createEventIds(): EventIds { return createWeakIdMap(EventIdConstants.FIRST_ID as EventId) } export type NodeId = number & { __brand: 'NodeId' } export type NodeIds = ItemIds export const enum NodeIdConstants { FIRST_ID = 0, } export function createNodeIds(): NodeIds { return createWeakIdMap(NodeIdConstants.FIRST_ID as NodeId) } export type StringId = number & { __brand: 'StringId' } export type StringIds = ItemIds export const enum StringIdConstants { FIRST_ID = 0, // An arbitrarily-chosen soft limit on the maximum size of the string id map. SOFT_MAX_SIZE = 1000000, } export function createStringIds(): StringIds { return createIdMap(StringIdConstants.FIRST_ID as StringId) } export type StyleSheetId = number & { __brand: 'StyleSheetId' } export type StyleSheetIds = ItemIds export const enum StyleSheetIdConstants { FIRST_ID = 0, } export function createStyleSheetIds(): StyleSheetIds { return createWeakIdMap(StyleSheetIdConstants.FIRST_ID as StyleSheetId) } export interface ItemIds { clear(this: void): void delete(this: void, item: ItemType): void get(this: void, item: ItemType): ItemId | undefined getOrInsert(this: void, item: ItemType): ItemId get nextId(): ItemId get size(): number } function createIdMap(firstId: ItemId): ItemIds { return createItemIds(() => new Map(), firstId) } function createWeakIdMap(firstId: ItemId): ItemIds { return createItemIds(() => new WeakMap(), firstId) } interface MapLike { delete(key: Key): void get(key: Key): Value | undefined set(key: Key, value: Value): void } function createItemIds( createMap: () => MapLike, firstId: ItemId ): ItemIds { let map = createMap() let nextId = firstId const get = (object: ItemType): ItemId | undefined => map.get(object) return { clear(): void { if (nextId === firstId) { return } map = createMap() nextId = firstId }, delete(object: ItemType): void { map.delete(object) }, get, getOrInsert(object: ItemType): ItemId { // Try to reuse any existing id. let id = get(object) if (id === undefined) { id = nextId++ as ItemId map.set(object, id) } return id }, get nextId(): ItemId { return nextId }, get size(): number { return nextId - firstId }, } }