package lime.utils; import haxe.ds.ObjectMap; /** A generic object pool for reusing objects. **/ #if !lime_debug @:fileXml('tags="haxe,release"') @:noDebug #end #if !js @:generic #end class ObjectPool { /** The number of active objects in the pool. **/ public var activeObjects(default, null):Int; /** The number of inactive objects in the pool. **/ public var inactiveObjects(default, null):Int; /** The total size of the object pool (both active and inactive objects). **/ public var size(get, set):Null; @:noCompletion private var __inactiveObject0:T; @:noCompletion private var __inactiveObject1:T; @:noCompletion private var __inactiveObjectList:List; @:noCompletion private var __pool:Map; @:noCompletion private var __size:Null; /** Creates a new ObjectPool instance. @param create A function that creates a new instance of type T. @param clean A function that cleans up an instance of type T before it is reused. @param size The maximum size of the object pool. **/ public function new(create:Void->T = null, clean:T->Void = null, size:Null = null) { __pool = cast new ObjectMap(); activeObjects = 0; inactiveObjects = 0; __inactiveObject0 = null; __inactiveObject1 = null; __inactiveObjectList = new List(); if (create != null) { this.create = create; } if (clean != null) { this.clean = clean; } if (size != null) { this.size = size; } } /** Adds an object to the object pool. @param object The object to add to the pool. **/ public function add(object:T):Void { if (object != null && !__pool.exists(object)) { __pool.set(object, false); clean(object); __addInactive(object); } } /** Dynamic function. Cleans up an object before returning it to the pool. @param object The object to clean up. **/ public dynamic function clean(object:T):Void {} /** Clears the object pool, removing all objects. **/ public function clear():Void { __pool = cast new ObjectMap(); activeObjects = 0; inactiveObjects = 0; __inactiveObject0 = null; __inactiveObject1 = null; __inactiveObjectList.clear(); } /** Dynamic function. Creates a new Object. **/ public dynamic function create():T { return null; } /** Creates a new object and adds it to the pool, or returns an existing inactive object from the pool. @return The object retrieved from the pool, or null if the pool is full and no new objects can be created. **/ public function get():T { var object = null; if (inactiveObjects > 0) { object = __getInactive(); } else if (__size == null || activeObjects < __size) { object = create(); if (object != null) { __pool.set(object, true); activeObjects++; } } return object; } /** Releases an active object back into the pool. @param object The object to release. **/ public function release(object:T):Void { #if lime_pool_debug if (object == null || !__pool.exists(object)) { Log.error("Object is not a member of the pool"); } else if (!__pool.get(object)) { Log.error("Object has already been released"); } #end activeObjects--; if (__size == null || activeObjects + inactiveObjects < __size) { clean(object); __addInactive(object); } else { __pool.remove(object); } } /** Removes an object from the pool. @param object The object to remove from the pool. **/ public function remove(object:T):Void { if (object != null && __pool.exists(object)) { __pool.remove(object); if (__inactiveObject0 == object) { __inactiveObject0 = null; inactiveObjects--; } else if (__inactiveObject1 == object) { __inactiveObject1 = null; inactiveObjects--; } else if (__inactiveObjectList.remove(object)) { inactiveObjects--; } else { activeObjects--; } } } @:noCompletion private inline function __addInactive(object:T):Void { #if lime_pool_debug __pool.set(object, false); #end if (__inactiveObject0 == null) { __inactiveObject0 = object; } else if (__inactiveObject1 == null) { __inactiveObject1 = object; } else { __inactiveObjectList.add(object); } inactiveObjects++; } @:noCompletion private inline function __getInactive():T { var object = null; if (__inactiveObject0 != null) { object = __inactiveObject0; __inactiveObject0 = null; } else if (__inactiveObject1 != null) { object = __inactiveObject1; __inactiveObject1 = null; } else { object = __inactiveObjectList.pop(); if (__inactiveObjectList.length > 0) { __inactiveObject0 = __inactiveObjectList.pop(); } if (__inactiveObjectList.length > 0) { __inactiveObject1 = __inactiveObjectList.pop(); } } #if lime_pool_debug __pool.set(object, true); #end inactiveObjects--; activeObjects++; return object; } @:noCompletion private function __removeInactive(count:Int):Void { if (count <= 0 || inactiveObjects == 0) return; if (__inactiveObject0 != null) { __pool.remove(__inactiveObject0); __inactiveObject0 = null; inactiveObjects--; count--; } if (count == 0 || inactiveObjects == 0) return; if (__inactiveObject1 != null) { __pool.remove(__inactiveObject1); __inactiveObject1 = null; inactiveObjects--; count--; } if (count == 0 || inactiveObjects == 0) return; for (object in __inactiveObjectList) { __pool.remove(object); __inactiveObjectList.remove(object); inactiveObjects--; count--; if (count == 0 || inactiveObjects == 0) return; } } // Get & Set Methods @:noCompletion private function get_size():Null { return __size; } @:noCompletion private function set_size(value:Null):Null { if (value == null) { __size = null; } else { var current = inactiveObjects + activeObjects; __size = value; if (current > value) { __removeInactive(current - value); } else if (value > current) { var object; for (i in 0...(value - current)) { object = create(); if (object != null) { __pool.set(object, false); __inactiveObjectList.add(object); inactiveObjects++; } else { break; } } } } return value; } }