/*global define,module */
/**
* @license fastPool - A lightweight and fast object pool implementation
*
* Version 0.9.0
*
* Copyright (c) 2016 Jens Arps
* http://jensarps.de/
*
* Licensed under the MIT license
*/
(function (name, definition, global) {
'use strict';
if (typeof define === 'function') {
define(definition);
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = definition();
} else {
global[name] = definition();
}
})('fastPool', function () {
'use strict';
/**
* The Pool constructor. When calling `fastPool()`, an instance of `Pool` will be returned.
*
* @exports Pool
*
* @param options
* @param {function} options.createObject A function that returns a new object
* @param {number} options.preAllocate The number of objects to pre-allocate
* @param {function} [options.resetObject] A function that will turn a used, dirty object into a clean state
* @constructor
*/
var Pool = function (options) {
this.createObject = options.createObject;
this.resetObject = options.resetObject;
this.preAllocate(options.preAllocate);
};
Pool.prototype = /** @lends {Pool} */ {
constructor: Pool,
/**
* A function that returns a new object.
*
* @type {function}
*/
createObject: null,
/**
* A function that will turn a used, dirty object into a clean state.
*
* @type {function}
*/
resetObject: null,
/**
* The actual object pool.
*
* @private
*/
_pool: [],
/**
* Pre-allocates the pool to the given size.
*
* @param {number} size The amount of objects to pre-allocate
*/
preAllocate: function (size) {
while (size--) {
this._pool.push(this.createObject());
}
},
/**
* Removes an object from the pool and hands it over to the caller. If the pool is empty, will create a new
* object.
*
* @returns {*} An object
*/
transfer: function () {
var pool = this._pool;
return pool.length ? pool.pop() : this.createObject();
},
/**
* Takes back an object and puts it back into the pool. If a `resetObject` function is given, it will be
* executed with the given object as first and only argument.
*
* @param {*} object The object to put back into the pool.
*/
takeBack: function (object) {
if (this.resetObject) {
this.resetObject(object);
}
this._pool.push(object);
},
/**
* Makes sure that the pool has at least the given size.
*
* @param {number} size
*/
ensureSize: function (size) {
var diff = size - this._pool.length;
if (diff > 0) {
this.preAllocate(diff);
}
}
};
/**
* The map of pools. Singleton.
*
* @type {Object<string, Pool>}
* @private
*/
var _pools = {};
/**
* Returns an object pool identified with the given name. If no pool with the given name exists, it will create a
* new pool.
*
*
* @example
* // create a pool for a given `Vector3` class
*
* var vec3Pool = fastPool({
* name: 'vec3',
* ctor: Vector3,
* preAllocate: 20
* });
*
* // get a Vector3 instance from the pool
* var vec3 = vec3Pool.transfer();
*
* // when it's no longer needed, return it to the pool
* vec3Pool.takeBack(vec3);
*
* @example
* // Extending the example above, but making sure the Vector3 instances are in a clean
* // state whenever one is transferred from the pool. The resetObject function is called
* // during `takeBack`, before the object is put back into the pool.
*
* var vec3Pool = fastPool({
* name: 'vec3',
* ctor: Vector3,
* preAllocate: 20,
* resetObject: function (vec3) {
* vec3.set(0, 0, 0);
* }
* });
*
* // get a Vector3 instance from the pool
* var vec3 = vec3Pool.transfer();
*
* // do work, and then back to the pool
* vec3.x = 5;
* vec3Pool.takeBack(vec3); // before the object is put back into the pool, it gets reset.
*
*
* @example
* // Create pool of Float32Arrays with a given size. Passing only the constructor does not
* // work, as it needs an argument, so using the `createObject` property here instead of `ctor`.
*
* var matrix16Pool = fastPool({
* name: 'matrix4',
* preAllocate: 100,
* createObject: function () {
* return new Float32Array(16);
* }
* });
*
* @example
* // Create a pool of complex objects with unknown quantity for runtime, so keep the
* // pre-allocation low.
*
* var tieFighterPool = fastPool({
* name: 'TieFighter',
* preAllocate: 5,
* createObject: function () {
* var tieFighter = new Fighter({
* type: 'TieFighter',
* AIStrength: 3
* });
* tieFighter.setTarget(player);
* return tieFighter;
* },
* resetObject: function (tieFighter) {
* tieFighter.clearTarget();
* tieFighter.resetPosition();
* }
* });
*
*
* @version 1.0.0
* @type {function}
* @alias fastPool
*
* @param {object} options
* @param {string} options.name The name that identifies this pool
* @param {function} [options.ctor] A constructor, that, when called with the new keyword, returns a new object
* @param {function} [options.createObject] A function that returns a new object
* @param {number} [options.preAllocate=0] The number of objects to pre-allocate
* @param {function} [options.resetObject] A function that will turn a used, 'dirty' object into a clean state
* @returns {Pool} An object pool
*/
function fastPool(options) {
if (!options || !options.name) {
throw new Error('fastPool: No `name` property given.');
}
if (!options.ctor && !options.createObject) {
throw new Error('fastPool: Neither `ctor` nor `createObject` property given.');
}
options.preAllocate = options.preAllocate || 0;
var pool = _pools[options.name];
if (typeof pool !== 'undefined') {
pool.ensureSize(options.preAllocate);
return pool;
}
options.createObject = options.createObject ||
function () {
return new options.ctor();
};
pool = _pools[options.name] = new Pool(options);
return pool;
}
return fastPool;
}, this);