function factory( options ) {
	var nbytes;
	var pool;
	var opts;
	var err;

	opts = defaults();
	if ( arguments.length ) {
		err = validate( opts, options );
		if ( err ) {
			throw err;
		}
	}
	pool = createPool( ceil( log2( opts.highWaterMark ) ) );
	nbytes = 0;

	setReadOnly( malloc, 'malloc', malloc ); // circular reference
	setReadOnly( malloc, 'calloc', calloc );
	setReadOnly( malloc, 'free', free );
	setReadOnly( malloc, 'clear', clear );
	setReadOnly( malloc, 'highWaterMark', opts.highWaterMark );
	setReadOnlyAccessor( malloc, 'nbytes', getBytes );

	return malloc;

	/**
	* Returns the number of allocated bytes.
	*
	* @private
	* @returns {NonNegativeInteger} number of allocated bytes
	*/
	function getBytes() {
		return nbytes;
	}

	/**
	* Returns an array buffer.
	*
	* @private
	* @param {NonNegativeInteger} n - number of bytes
	* @returns {(ArrayBuffer|null)} array buffer or null
	*/
	function arraybuffer( n ) {
		var buf;
		var i;

		// Convert the number of bytes to an index in our pool table:
		i = log2( n );

		// If we already have an available array buffer, use it...
		if ( i < pool.length && pool[ i ].length ) {
			return pool[ i ].pop();
		}
		// Before allocating a new array buffer, ensure that we have not exceeded the maximum number of bytes we are allowed to allocate...
		if ( nbytes+n > opts.highWaterMark ) {
			return null;
		}
		buf = new ArrayBuffer( n );

		// Update the running counter of allocated bytes:
		nbytes += n;

		return buf;
	}

	/**
	* Returns a typed array.
	*
	* @private
	* @param {Function} ctor - typed array constructor
	* @param {NonNegativeInteger} len - view length
	* @param {string} dtype - data type
	* @returns {(TypedArray|null)} typed array or null
	*/
	function typedarray( ctor, len, dtype ) {
		var buf;
		if ( len === 0 ) {
			return new ctor( 0 );
		}
		buf = arraybuffer( ceil2( len )*BYTES_PER_ELEMENT[ dtype ] );
		if ( buf === null ) {
			return buf;
		}
		return new ctor( buf, 0, len );
	}

	/**
	* Returns an uninitialized typed array.
	*
	* ## Notes
	*
	* -   Memory is **not** initialized.
	* -   Memory is lazily allocated.
	* -   If the function returns `null`, the function was unable to allocate a new typed array from the typed array pool (most likely due to insufficient memory).
	*
	* @private
	* @param {(NonNegativeInteger|Collection)} [arg] - an array length or an array-like object
	* @param {string} [dtype="float64"] - data type
	* @throws {TypeError} must provide a valid array length or an array-like object
	* @throws {TypeError} must provide a recognized data type
	* @returns {(TypedArray|null)} typed array or null
	*/
	function malloc() {
		var nargs;
		var dtype;
		var ctor;
		var arr;
		var out;
		var set;
		var get;
		var len;
		var i;

		nargs = arguments.length;
		if ( nargs && isString( arguments[ nargs-1 ] ) ) {
			nargs -= 1;
			dtype = arguments[ nargs ];
		} else {
			dtype = DEFAULT_DTYPE;
		}
		ctor = ctors( dtype );
		if ( ctor === null ) {
			throw new TypeError( format( 'invalid argument. Must provide a recognized data type. Value: `%s`.', dtype ) );
		}
		if ( nargs <= 0 ) {
			return new ctor( 0 );
		}
		// Check if provided a typed array length...
		if ( isNonNegativeInteger( arguments[ 0 ] ) ) {
			return typedarray( ctor, arguments[ 0 ], dtype );
		}
		// Check if provided an array-like object containing data elements...
		if ( isCollection( arguments[ 0 ] ) ) {
			arr = arguments[ 0 ];
			len = arr.length;
			if ( isComplex128Array( arr ) ) {
				arr = reinterpret128( arr, 0 );
			} else if ( isComplex64Array( arr ) ) {
				arr = reinterpret64( arr, 0 );
			} else if ( isBooleanArray( arr ) ) {
				arr = reinterpretBoolean( arr, 0 );
			} else if ( isComplexDataType( dtype ) ) {
				// Assume we've been provided an array of interleaved real and imaginary components...
				len /= 2;
			}
			out = typedarray( ctor, len, dtype );
			if ( out === null ) {
				return out;
			}
			if ( isCmplx128Array( out ) || isCmplx64Array( out ) || isBoolArray( out ) ) {
				out.set( arr );
				return out;
			}
			// Resolve element accessors in order to account for the possibility that `arr` is a complex number array. As we don't prohibit other "unsafe" casts (e.g., providing a `Float64Array` and specifying a `dtype` of `uint8`), we don't prohibit providing a complex number array and specifying a real `dtype`. The results will probably be unexpected/gibberish, but I am not sure we should be overly pedantic in ensuring users don't do ill-advised things...
			get = resolveGetter( arr );
			set = resolveSetter( out );
			for ( i = 0; i < len; i++ ) {
				set( out, i, get( arr, i ) );
			}
			return out;
		}
		throw new TypeError( format( 'invalid argument. First argument must be either an array length or an array-like object. Value: `%s`.', arguments[ 0 ] ) );
	}

	/**
	* Returns a zero-initialized typed array.
	*
	* ## Notes
	*
	* -   If the function returns `null`, the function was unable to allocate a new typed array from the typed array pool (most likely due to insufficient memory).
	*
	* @private
	* @param {NonNegativeInteger} [len=0] - array length
	* @param {string} [dtype="float64"] - data type
	* @throws {TypeError} must provide a valid array length
	* @throws {TypeError} must provide a recognized data type
	* @returns {(TypedArray|null)} typed array or null
	*/
	function calloc() {
		var nargs;
		var out;
		var tmp;
		var i;

		nargs = arguments.length;
		if ( nargs === 0 ) {
			out = malloc();
		} else if ( nargs === 1 ) {
			out = malloc( arguments[ 0 ] );
		} else {
			out = malloc( arguments[ 0 ], arguments[ 1 ] );
		}
		if ( out !== null ) {
			// Initialize the memory...
			if ( isCmplx128Array( out ) ) {
				tmp = reinterpret128( out, 0 );
			} else if ( isCmplx64Array( out ) ) {
				tmp = reinterpret64( out, 0 );
			} else if ( isBoolArray( out ) ) {
				tmp = reinterpretBoolean( out, 0 );
			} else {
				tmp = out;
			}
			for ( i = 0; i < tmp.length; i++ ) {
				tmp[ i ] = 0.0;
			}
		}
		return out;
	}

	/**
	* Frees a typed array or typed array buffer.
	*
	* ## Notes
	*
	* -   Implicitly, we support providing non-internally allocated arrays and array buffer (e.g., "freeing" a typed array allocated in userland); however, the freed array buffer is likely to have excess capacity when compared to other members in its pool.
	*
	* @private
	* @param {(TypedArray|ArrayBuffer)} buf - typed array or array buffer to free
	* @throws {TypeError} must provide a typed array or typed array buffer
	* @returns {boolean} boolean indicating whether the typed array or array buffer was successfully freed
	*/
	function free( buf ) {
		var n;
		var p;
		var i;
		if ( isTypedArrayLike( buf ) && buf.buffer ) {
			buf = buf.buffer;
		} else if ( !isArrayBuffer( buf ) ) {
			throw new TypeError( format( 'invalid argument. Must provide a typed array or ArrayBuffer. Value: `%s`.', buf ) );
		}
		if ( buf.byteLength > 0 ) {
			n = floor( log2( buf.byteLength ) );

			// Prohibit "freeing" array buffers which would potentially allow users to circumvent high water mark limits:
			n = min( pool.length-1, n );

			// Ensure that we do not attempt to free the same buffer more than once...
			p = pool[ n ];
			for ( i = 0; i < p.length; i++ ) {
				if ( p[ i ] === buf ) {
					return false;
				}
			}
			// Add the buffer to our pool of free buffers:
			p.push( buf );
		}
		return true;
	}

	/**
	* Clears the typed array pool allowing garbage collection of previously allocated (and currently free) array buffers.
	*
	* @private
	*/
	function clear() {
		var i;
		for ( i = 0; i < pool.length; i++ ) {
			pool[ i ].length = 0;
		}
		nbytes = 0;
	}
}
Creates a typed array pool.

@param {Options} [options] - pool options
@param {NonNegativeInteger} [options.highWaterMark] - maximum total memory which can be allocated
@throws {TypeError} options argument must be an object
@throws {TypeError} must provide valid options
@returns {Function} allocator

@example
var typedarraypool = factory();

// Allocate an array of doubles:
var arr = typedarraypool( 5, 'float64' );
// returns <Float64Array>[ 0.0, 0.0, 0.0, 0.0, 0.0 ]

arr[ 0 ] = 3.14;
arr[ 1 ] = 3.14;

// ...

// Free the allocated memory to be used in a future allocation:
typedarraypool.free( arr );