import { type Closure, INTERNAL_ERROR_MESSAGE, Option, Some, None, } from "@"; export type Dyn = Alloc | DeAlloc; /** * ***Brief*** * A dynamic resource management pattern, allowing for * allocation and deallocation of resources with proper lifecycle handling. * * ***Warning*** * The `Dyn` wrapper must be properly managed to avoid memory leaks. If the wrapper itself is garbage collected * without its content being explicitly deallocated, the resources it manages will not be recycled * and made available for reuse. Ensure all allocated resources are deallocated before the `Dyn` instance goes out of scope. * * ***Example*** * ```ts * type Car = { * drive(): void; * }; * * const Car: DynConstructor = Dyn( * (_model: string) => { * constructor { * return { drive }; * } * * function drive(): void { * return "Vroom."; * } * }, * car => { * /// Reset car or any tasks before it is made available again. * /// ... * return car; * }, 32n, "ModelT" * ); * * let car: Dyn = Car("ModelF"); * car = car.deAlloc(); * car.map(car => { * /// Will not run because `car` has been deallocated. * /// ... * }); * ``` * * ***Example*** * ```ts * /// Warning. * let car: Dyn = Car("ModelB"); * car.deAlloc(); * car.map(car => { * /// Will run because the car must be updated to the new state. * /// Always assign the `deAlloc` result a new `Dyn` wrapper or * /// itself. * /// ... * }); * ``` */ export function Dyn>(_constructor: Closure, _onDeAlloc: Closure<[T1], T1>, _load: bigint, ..._onLoadPayload: T2): Dyn.Constructor { let _available: Map; let _count: bigint; /** @constructor */ { _available = new Map(); _count = 0n; let i: bigint = 0n; while (i < _load) { let key: bigint = _newKey(); _available.set(key, _constructor(..._onLoadPayload)); i ++; } return function (...payload: T2): Dyn { let _key: Option; let _this: Dyn; /** @constructor */ { _key = None; return _this = alloc(); } function alloc(): Alloc { return _recycledKey() .toResult(undefined) .map(key => { _key = Some(key); return Alloc(_recyled(key), { deAlloc }); }) .recover(() => { _key = Some(_newKey()); return Alloc(_constructor(...payload), { deAlloc }); }) .unwrap(); } function deAlloc(): DeAlloc { _key .toResult(undefined) .map(key => { /// If the key is available then it is allocated. _key = None; _recyle(key, _onDeAlloc(_this.expect("Dyn: Could not deallocate resource but the key was available." + INTERNAL_ERROR_MESSAGE))); return; }); return _this = DeAlloc({ deAlloc }); } }; } function _newKey(): bigint { return _count += 1n; } function _recycledKey(): Option { let key: bigint | undefined = _available .keys() .toArray() .at(0); if (!key) return None; return Some(key); } function _recyled(key: bigint): T1 { let value: T1 = _available.get(key)!; _available.delete(key); return value; } function _recyle(key: bigint, value: T1): void { _available.set(key, value); return; } } export namespace Dyn { export type Constructor> = Closure>; export type Wrapper = { /** * ***Brief*** * Deallocates a resource, making it available for recycling. * * ***Example*** * ```ts * let personD: Dyn<{ name: string }>; * personD * .deAlloc() * .map(person => { * /// Not run because `person` was deallocated. * /// ... * }); * ``` */ deAlloc(): DeAlloc; }; } export type Alloc = & Dyn.Wrapper & Some & { /** * ***Brief*** * Unwraps the wrapped value of type `T1`. * * ***Requirement*** * Does not `panic`. * * ***Example*** * ```ts * let unsafe: Unsafe = Unsafe(500n); * unsafe.unwrap(); * ``` */ unwrap(): T1; } export function Alloc(_value: T1, _dyn: Dyn.Wrapper): Alloc { { return { ... Some(_value), ... _dyn }; } } export type DeAlloc = & None & Dyn.Wrapper; /** * ***Brief*** * The deallocated state of an allocated resource. */ export function DeAlloc(_dyn: Dyn.Wrapper): DeAlloc { { return { ... None, ... _dyn }; } }