ropool
Version:
A simple and efficient object pool for JavaScript and TypeScript.
201 lines (199 loc) • 7.52 kB
TypeScript
/**
* @template T The type of objects managed by the pool.
* @class ObjectHandle
* @description
* A handle representing an object acquired from an {@link ObjectPool}.
* This handle is the primary way to interact with a pooled object and release it back to the pool.
* It provides a safe wrapper around the underlying object and prevents issues like double-freeing.
*/
declare class ObjectHandle<T> {
/**
* The unique identifier of the object within the pool.
* This corresponds to the index of the object in the pool's internal `objects` array.
* @private
* @readonly
* @type {number}
*/
private readonly id;
/**
* The actual object instance managed by this handle.
* @private
* @readonly
* @type {T}
*/
private readonly obj;
/**
* A reference back to the pool from which this handle was acquired.
* @private
* @readonly
* @type {ObjectPool<T>}
*/
private readonly pool;
/**
* Internal flag indicating whether the object wrapped by this handle is currently considered released
* and available in the pool's stack.
* @private
* @type {boolean}
*/
private _isReleased;
/**
* Creates an instance of ObjectHandle.
* @param {number} id The unique ID of the object within the pool.
* @param {T} obj The object instance this handle wraps.
* @param {ObjectPool<T>} objPool The pool instance this handle belongs to.
* @internal This constructor is intended for internal use by {@link ObjectPool}. Users should acquire handles via {@link ObjectPool#acquire}.
*/
constructor(id: number, obj: T, objPool: ObjectPool<T>);
/**
* Gets the underlying object instance managed by this handle.
* Use this getter to access and modify the pooled object's properties.
* @returns {T} The pooled object.
*/
get data(): T;
/**
* Releases the object back to the pool, making it available for reuse.
* This method is idempotent; calling it multiple times on the same handle
* instance will only release the object once.
* After calling `free()`, the handle should no longer be used.
*
* @example
* ```typescript
* const handle = pool.acquire();
* // Use handle.data
* handle.free(); // Release the object
* handle.free(); // Calling again does nothing
* ```
*/
free(): void;
/**
* Implements the Symbol.dispose method for the 'using' keyword.
* This allows the handle to be used with the `using` declaration (available in
* environments supporting the Explicit Resource Management proposal, e.g., Node.js 20+).
* When a `using` declaration goes out of scope, this method is automatically called,
* ensuring the object is released back to the pool.
*
* @example
* ```typescript
* using handle = pool.acquire();
* // Use handle.data
* // handle.free() is automatically called when exiting this block
* ```
*/
[Symbol.dispose](): void;
/** @internal For pool internal use to reset state on acquire. */
_onAcquire(): void;
}
/**
* @template T The type of objects managed by the pool.
* @class ObjectPool
* @description
* Manages a pool of reusable objects of type `T`.
* This helps reduce the overhead of creating and garbage collecting objects
* by recycling instances.
*/
declare class ObjectPool<T> {
/**
* The factory function used to create new object instances when the pool needs to grow.
* @private
* @readonly
* @type {() => T}
*/
private readonly createObject;
/**
* A stack of available object IDs (indices into the `objects` array).
* @private
* @type {Uint16Array}
*/
private stack;
/**
* An array holding all object handles created by the pool.
* The index in this array serves as the unique ID for each object/handle.
* @private
* @type {ObjectHandle<T>[]}
*/
private objects;
/**
* The current position in the `stack` array.
* It points to the next available ID to be acquired.
* When `pointer` is 0, the stack is full of available IDs.
* When `pointer` equals `stack.length`, the pool is exhausted.
* @private
* @type {number}
*/
private pointer;
/**
* The initial size of the pool when first created, and also influences
* the minimum growth size during resizing.
* @private
* @readonly
* @type {number}
*/
private readonly initialLength;
/**
* Creates an instance of ObjectPool.
* @param {() => T} createObject A factory function that returns a new instance of the object type `T`.
* This function is called when the pool needs to create new objects.
* @param {number} [initialLength=8] The initial number of objects to create in the pool.
* If 0 is provided, the pool will default to a minimum growth size (currently 8) on the first acquire.
* @example
* ```typescript
* // Create a pool for Vector2 objects with an initial size of 16
* const vectorPool = new ObjectPool(() => ({ x: 0, y: 0 }), 16);
*
* // Create a pool with default initial size (8)
* const defaultPool = new ObjectPool(() => ({ id: 0 }));
* ```
*/
constructor(createObject: () => T, initialLength?: number);
/**
* The maximum number of objects the pool can hold, limited by the capacity of `Uint16Array`.
* This is 2^16 = 65536.
* @static
* @readonly
* @type {number}
*/
private static readonly MAX_POOL_SIZE;
/**
* Resizes the pool by creating new objects and adding their IDs to the stack.
* The pool doubles in size, capped by `MAX_POOL_SIZE`.
* If the pool is already at `MAX_POOL_SIZE`, this method does nothing.
* @private
* @throws {Error} If `createObject` fails during resize, although the resize logic itself prevents exceeding MAX_POOL_SIZE.
*/
private resize;
/**
* Acquires an object handle from the pool.
*
* @returns {ObjectHandle<T>} An ObjectHandle wrapping the acquired object.
* @throws {Error} If the pool is exhausted (at `MAX_POOL_SIZE`) and cannot provide a new object.
* @example
* ```typescript
* const handle = pool.acquire();
* const obj = handle.data; // Access the object
* ```
*/
acquire(): ObjectHandle<T>;
/**
* Releases an object handle back to the pool using its ID.
* This method is typically called internally by {@link ObjectHandle#free}.
* It pushes the object's ID back onto the stack of available IDs.
*
* @param id The ID of the object handle to release.
* @internal Intended for use by {@link ObjectHandle#free}. Direct use is discouraged.
*/
release(id: number): void;
/**
* Releases all acquired objects back to the pool.
* This iterates through all objects managed by the pool, marks their handles
* as released, and resets the internal stack and pointer so that all objects
* are available for re-acquisition in their original order (0, 1, 2, ...).
* Note: This does NOT reset the state of the objects themselves.
*
* @example
* ```typescript
* pool.releaseAll(); // All objects previously acquired are now available
* ```
*/
releaseAll(): void;
}
export { ObjectHandle, ObjectPool };