time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

factory.js (8799B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 'use strict';
     20 
     21 // MODULES //
     22 
     23 var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
     24 var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive;
     25 var isCollection = require( '@stdlib/assert/is-collection' );
     26 var isTypedArrayLike = require( '@stdlib/assert/is-typed-array-like' );
     27 var isArrayBuffer = require( '@stdlib/assert/is-arraybuffer' );
     28 var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' );
     29 var setReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' );
     30 var ctors = require( './../../typed-ctors' );
     31 var copy = require( '@stdlib/utils/copy' );
     32 var ArrayBuffer = require( './../../buffer' );
     33 var ceil = require( '@stdlib/math/base/special/ceil' );
     34 var floor = require( '@stdlib/math/base/special/floor' );
     35 var ceil2 = require( '@stdlib/math/base/special/ceil2' );
     36 var log2 = require( '@stdlib/math/base/special/log2' );
     37 var min = require( '@stdlib/math/base/special/min' );
     38 var defaults = require( './defaults.json' );
     39 var validate = require( './validate.js' );
     40 var createPool = require( './pool.js' );
     41 var BYTES_PER_ELEMENT = require( './bytes_per_element.json' );
     42 
     43 
     44 // MAIN //
     45 
     46 /**
     47 * Creates a typed array pool.
     48 *
     49 * @param {Options} [options] - pool options
     50 * @param {NonNegativeInteger} [options.highWaterMark] - maximum total memory which can be allocated
     51 * @throws {TypeError} options argument must be an object
     52 * @throws {TypeError} must provide valid options
     53 * @returns {Function} allocator
     54 *
     55 * @example
     56 * var typedarraypool = factory();
     57 *
     58 * // Allocate an array of doubles:
     59 * var arr = typedarraypool( 5, 'float64' );
     60 * // returns <Float64Array>[ 0.0, 0.0, 0.0, 0.0, 0.0 ]
     61 *
     62 * arr[ 0 ] = 3.14;
     63 * arr[ 1 ] = 3.14;
     64 *
     65 * // ...
     66 *
     67 * // Free the allocated memory to be used in a future allocation:
     68 * typedarraypool.free( arr );
     69 */
     70 function factory( options ) {
     71 	var nbytes;
     72 	var pool;
     73 	var opts;
     74 	var err;
     75 
     76 	opts = copy( defaults );
     77 	if ( arguments.length ) {
     78 		err = validate( opts, options );
     79 		if ( err ) {
     80 			throw err;
     81 		}
     82 	}
     83 	pool = createPool( ceil( log2( opts.highWaterMark ) ) );
     84 	nbytes = 0;
     85 
     86 	setReadOnly( malloc, 'malloc', malloc ); // circular reference
     87 	setReadOnly( malloc, 'calloc', calloc );
     88 	setReadOnly( malloc, 'free', free );
     89 	setReadOnly( malloc, 'clear', clear );
     90 	setReadOnly( malloc, 'highWaterMark', opts.highWaterMark );
     91 	setReadOnlyAccessor( malloc, 'nbytes', getBytes );
     92 
     93 	return malloc;
     94 
     95 	/**
     96 	* Returns the number of allocated bytes.
     97 	*
     98 	* @private
     99 	* @returns {NonNegativeInteger} number of allocated bytes
    100 	*/
    101 	function getBytes() {
    102 		return nbytes;
    103 	}
    104 
    105 	/**
    106 	* Returns an array buffer.
    107 	*
    108 	* @private
    109 	* @param {NonNegativeInteger} n - number of bytes
    110 	* @returns {(ArrayBuffer|null)} array buffer or null
    111 	*/
    112 	function arraybuffer( n ) {
    113 		var buf;
    114 		var i;
    115 
    116 		// Convert the number of bytes to an index in our pool table:
    117 		i = log2( n );
    118 
    119 		// If we already have an available array buffer, use it...
    120 		if ( i < pool.length && pool[ i ].length ) {
    121 			return pool[ i ].pop();
    122 		}
    123 		// Before allocating a new array buffer, ensure that we have not exceeded the maximum number of bytes we are allowed to allocate...
    124 		if ( nbytes+n > opts.highWaterMark ) {
    125 			return null;
    126 		}
    127 		buf = new ArrayBuffer( n );
    128 
    129 		// Update the running counter of allocated bytes:
    130 		nbytes += n;
    131 
    132 		return buf;
    133 	}
    134 
    135 	/**
    136 	* Returns a typed array.
    137 	*
    138 	* @private
    139 	* @param {Function} ctor - typed array constructor
    140 	* @param {NonNegativeInteger} len - view length
    141 	* @param {string} dtype - data type
    142 	* @returns {(TypedArray|null)} typed array or null
    143 	*/
    144 	function typedarray( ctor, len, dtype ) {
    145 		var buf;
    146 		if ( len === 0 ) {
    147 			return new ctor( 0 );
    148 		}
    149 		buf = arraybuffer( ceil2( len )*BYTES_PER_ELEMENT[ dtype ] );
    150 		if ( buf === null ) {
    151 			return buf;
    152 		}
    153 		return new ctor( buf, 0, len );
    154 	}
    155 
    156 	/**
    157 	* Returns an uninitialized typed array.
    158 	*
    159 	* ## Notes
    160 	*
    161 	* -   Memory is **not** initialized.
    162 	* -   Memory is lazily allocated.
    163 	* -   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).
    164 	*
    165 	* @private
    166 	* @param {(NonNegativeInteger|Collection)} [arg] - an array length or an array-like object
    167 	* @param {string} [dtype="float64"] - data type
    168 	* @throws {TypeError} must provide a valid array length or an array-like object
    169 	* @throws {TypeError} must provide a recognized data type
    170 	* @returns {(TypedArray|null)} typed array or null
    171 	*/
    172 	function malloc() {
    173 		var nargs;
    174 		var dtype;
    175 		var ctor;
    176 		var len;
    177 		var arr;
    178 		var out;
    179 		var i;
    180 
    181 		nargs = arguments.length;
    182 		if ( nargs && isString( arguments[ nargs-1 ] ) ) {
    183 			nargs -= 1;
    184 			dtype = arguments[ nargs ];
    185 		} else {
    186 			dtype = 'float64';
    187 		}
    188 		ctor = ctors( dtype );
    189 		if ( ctor === null ) {
    190 			throw new TypeError( 'invalid argument. Must provide a recognized data type. Value: `'+dtype+'`.' );
    191 		}
    192 		if ( nargs <= 0 ) {
    193 			return new ctor( 0 );
    194 		}
    195 		// Check if provided a typed array length...
    196 		if ( isNonNegativeInteger( arguments[ 0 ] ) ) {
    197 			return typedarray( ctor, arguments[ 0 ], dtype );
    198 		}
    199 		// Check if provided an array-like object containing data elements...
    200 		if ( isCollection( arguments[ 0 ] ) ) {
    201 			arr = arguments[ 0 ];
    202 			len = arr.length;
    203 			out = typedarray( ctor, len, dtype );
    204 			if ( out === null ) {
    205 				return out;
    206 			}
    207 			for ( i = 0; i < len; i++ ) {
    208 				out[ i ] = arr[ i ];
    209 			}
    210 			return out;
    211 		}
    212 		throw new TypeError( 'invalid argument. First argument must be either an array length or an array-like object. Value: `'+arguments[ 0 ]+'`.' );
    213 	}
    214 
    215 	/**
    216 	* Returns a zero-initialized typed array.
    217 	*
    218 	* ## Notes
    219 	*
    220 	* -   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).
    221 	*
    222 	* @private
    223 	* @param {NonNegativeInteger} [len=0] - array length
    224 	* @param {string} [dtype="float64"] - data type
    225 	* @throws {TypeError} must provide a valid array length
    226 	* @throws {TypeError} must provide a recognized data type
    227 	* @returns {(TypedArray|null)} typed array or null
    228 	*/
    229 	function calloc() {
    230 		var nargs;
    231 		var out;
    232 		var i;
    233 
    234 		nargs = arguments.length;
    235 		if ( nargs === 0 ) {
    236 			out = malloc();
    237 		} else if ( nargs === 1 ) {
    238 			out = malloc( arguments[ 0 ] );
    239 		} else {
    240 			out = malloc( arguments[ 0 ], arguments[ 1 ] );
    241 		}
    242 		if ( out !== null ) {
    243 			// Initialize the memory...
    244 			for ( i = 0; i < out.length; i++ ) {
    245 				out[ i ] = 0.0;
    246 			}
    247 		}
    248 		return out;
    249 	}
    250 
    251 	/**
    252 	* Frees a typed array or typed array buffer.
    253 	*
    254 	* ## Notes
    255 	*
    256 	* -   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.
    257 	*
    258 	* @private
    259 	* @param {(TypedArray|ArrayBuffer)} buf - typed array or array buffer to free
    260 	* @throws {TypeError} must provide a typed array or typed array buffer
    261 	* @returns {boolean} boolean indicating whether the typed array or array buffer was successfully freed
    262 	*/
    263 	function free( buf ) {
    264 		var n;
    265 		var p;
    266 		var i;
    267 		if ( isTypedArrayLike( buf ) && buf.buffer ) {
    268 			buf = buf.buffer;
    269 		} else if ( !isArrayBuffer( buf ) ) {
    270 			throw new TypeError( 'invalid argument. Must provide a typed array or typed array buffer. Value: `'+buf+'`.' );
    271 		}
    272 		if ( buf.byteLength > 0 ) {
    273 			n = floor( log2( buf.byteLength ) );
    274 
    275 			// Prohibit "freeing" array buffers which would potentially allow users to circumvent high water mark limits:
    276 			n = min( pool.length-1, n );
    277 
    278 			// Ensure that we do not attempt to free the same buffer more than once...
    279 			p = pool[ n ];
    280 			for ( i = 0; i < p.length; i++ ) {
    281 				if ( p[ i ] === buf ) {
    282 					return false;
    283 				}
    284 			}
    285 			// Add the buffer to our pool of free buffers:
    286 			p.push( buf );
    287 		}
    288 		return true;
    289 	}
    290 
    291 	/**
    292 	* Clears the typed array pool allowing garbage collection of previously allocated (and currently free) array buffers.
    293 	*
    294 	* @private
    295 	*/
    296 	function clear() {
    297 		var i;
    298 		for ( i = 0; i < pool.length; i++ ) {
    299 			pool[ i ].length = 0;
    300 		}
    301 		nbytes = 0;
    302 	}
    303 }
    304 
    305 
    306 // EXPORTS //
    307 
    308 module.exports = factory;