time-to-botec

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

main.js (9864B)


      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 /* eslint-disable no-restricted-syntax, no-invalid-this */
     20 
     21 'use strict';
     22 
     23 // MODULES //
     24 
     25 var isCollection = require( '@stdlib/assert/is-collection' );
     26 var isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ).isPrimitive;
     27 var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive;
     28 var setReadOnly = require( './../../define-nonenumerable-read-only-property' );
     29 var setReadOnlyAccessor = require( './../../define-nonenumerable-read-only-accessor' );
     30 var iteratorSymbol = require( '@stdlib/symbol/iterator' );
     31 var MAX_ITERATIONS = require( '@stdlib/constants/float64/max' );
     32 
     33 
     34 // MAIN //
     35 
     36 /**
     37 * Circular buffer constructor.
     38 *
     39 * @constructor
     40 * @param {(PositiveInteger|Collection)} buffer - buffer size or an array-like object to use as the underlying buffer
     41 * @throws {TypeError} must provide either a valid buffer size or an array-like object
     42 * @returns {CircularBuffer} circular buffer instance
     43 *
     44 * @example
     45 * var b = new CircularBuffer( 3 );
     46 *
     47 * // Fill the buffer...
     48 * var v = b.push( 'foo' );
     49 * // returns undefined
     50 *
     51 * v = b.push( 'bar' );
     52 * // returns undefined
     53 *
     54 * v = b.push( 'beep' );
     55 * // returns undefined
     56 *
     57 * // Add another value to the buffer and return the removed element:
     58 * v = b.push( 'boop' );
     59 * // returns 'foo'
     60 */
     61 function CircularBuffer( buffer ) {
     62 	var i;
     63 	if ( !(this instanceof CircularBuffer) ) {
     64 		return new CircularBuffer( buffer );
     65 	}
     66 	if ( isPositiveInteger( buffer ) ) {
     67 		this._buffer = [];
     68 		for ( i = 0; i < buffer; i++ ) {
     69 			this._buffer.push( 0.0 ); // initialize with zeros, but could be any value (we're just ensuring a contiguous block of memory)
     70 		}
     71 	} else if ( isCollection( buffer ) ) {
     72 		this._buffer = buffer;
     73 	} else {
     74 		throw new TypeError( 'invalid argument. Must provide either a valid buffer size (positive integer) or an array-like object which can serve as the underlying buffer. Value: `' + buffer + '`.' );
     75 	}
     76 	this._length = this._buffer.length;
     77 	this._count = 0;
     78 	this._i = -1;
     79 	return this;
     80 }
     81 
     82 /**
     83 * Clears the buffer.
     84 *
     85 * @name clear
     86 * @memberof CircularBuffer.prototype
     87 * @type {Function}
     88 * @returns {CircularBuffer} circular buffer instance
     89 *
     90 * @example
     91 * var b = new CircularBuffer( 2 );
     92 *
     93 * // Add values to the buffer:
     94 * b.push( 'foo' );
     95 * b.push( 'bar' );
     96 * b.push( 'beep' );
     97 * b.push( 'boop' );
     98 *
     99 * // Get the number of elements currently in the buffer:
    100 * var n = b.count;
    101 * // returns 2
    102 *
    103 * // Clear the buffer:
    104 * b.clear();
    105 *
    106 * // Get the number of buffer values:
    107 * n = b.count;
    108 * // returns 0
    109 */
    110 setReadOnly( CircularBuffer.prototype, 'clear', function clear() {
    111 	this._count = 0;
    112 	this._i = -1; // NOTE: this ensures that we always fill the buffer starting at index `0`.
    113 	return this;
    114 });
    115 
    116 /**
    117 * Number of elements currently in the buffer.
    118 *
    119 * @name count
    120 * @memberof CircularBuffer.prototype
    121 * @type {NonNegativeInteger}
    122 *
    123 * @example
    124 * var b = new CircularBuffer( 4 );
    125 *
    126 * // Get the value count:
    127 * var n = b.count;
    128 * // returns 0
    129 *
    130 * // Add values to the buffer:
    131 * b.push( 'foo' );
    132 * b.push( 'bar' );
    133 *
    134 * // Get the value count:
    135 * n = b.count;
    136 * // returns 2
    137 */
    138 setReadOnlyAccessor( CircularBuffer.prototype, 'count', function get() {
    139 	return this._count;
    140 });
    141 
    142 /**
    143 * Boolean indicating whether a circular buffer is full.
    144 *
    145 * @name full
    146 * @memberof CircularBuffer.prototype
    147 * @type {boolean}
    148 *
    149 * @example
    150 * var b = new CircularBuffer( 3 );
    151 *
    152 * // Determine if the buffer is full:
    153 * var bool = b.full;
    154 * // returns false
    155 *
    156 * // Add values to the buffer:
    157 * b.push( 'foo' );
    158 * b.push( 'bar' );
    159 * b.push( 'beep' );
    160 * b.push( 'boop' );
    161 *
    162 * // Determine if the buffer is full:
    163 * bool = b.full;
    164 * // returns true
    165 */
    166 setReadOnlyAccessor( CircularBuffer.prototype, 'full', function get() {
    167 	return this._count === this._length;
    168 });
    169 
    170 /**
    171 * Returns an iterator for iterating over a circular buffer.
    172 *
    173 * ## Notes
    174 *
    175 * -   An iterator does not iterate over partially full buffers.
    176 *
    177 * @name iterator
    178 * @memberof CircularBuffer.prototype
    179 * @type {Function}
    180 * @param {NonNegativeInteger} [niters] - number of iterations
    181 * @throws {TypeError} must provide a nonnegative integer
    182 * @returns {Iterator} iterator
    183 *
    184 * @example
    185 * var b = new CircularBuffer( 3 );
    186 *
    187 * // Add values to the buffer:
    188 * b.push( 'foo' );
    189 * b.push( 'bar' );
    190 * b.push( 'beep' );
    191 * b.push( 'boop' );
    192 *
    193 * // Create an iterator:
    194 * var it = b.iterator( b.length );
    195 *
    196 * // Iterate over the buffer...
    197 * var v = it.next().value;
    198 * // returns 'bar'
    199 *
    200 * v = it.next().value;
    201 * // returns 'beep'
    202 *
    203 * v = it.next().value;
    204 * // returns 'boop'
    205 *
    206 * var bool = it.next().done;
    207 * // returns true
    208 */
    209 setReadOnly( CircularBuffer.prototype, 'iterator', function iterator( niters ) {
    210 	var iter;
    211 	var self;
    212 	var FLG;
    213 	var N;
    214 	var n;
    215 	var i;
    216 
    217 	if ( arguments.length ) {
    218 		if ( !isNonNegativeInteger( niters ) ) {
    219 			throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + niters + '`.' );
    220 		}
    221 		N = niters;
    222 	} else {
    223 		N = MAX_ITERATIONS;
    224 	}
    225 	self = this;
    226 
    227 	// Initialize the iteration index and counter:
    228 	i = this._i;
    229 	n = 0;
    230 
    231 	// Create an iterator protocol-compliant object:
    232 	iter = {};
    233 	setReadOnly( iter, 'next', next );
    234 	setReadOnly( iter, 'return', end );
    235 	if ( iteratorSymbol ) {
    236 		setReadOnly( iter, iteratorSymbol, factory );
    237 	}
    238 	return iter;
    239 
    240 	/**
    241 	* Returns an iterator protocol-compliant object containing the next iterated value.
    242 	*
    243 	* @private
    244 	* @returns {Object} iterator protocol-compliant object
    245 	*/
    246 	function next() {
    247 		/* eslint-disable no-underscore-dangle */
    248 		n += 1;
    249 		if ( FLG || n > N ) {
    250 			return {
    251 				'done': true
    252 			};
    253 		}
    254 		// If the buffer is only partially full, don't allow iteration over "undefined" elements (this ensures similar behavior with `toArray()`)...
    255 		if ( self._count !== self._length ) {
    256 			FLG = true;
    257 			return {
    258 				'done': true
    259 			};
    260 		}
    261 		i = (i+1) % self._length;
    262 		return {
    263 			'value': self._buffer[ i ],
    264 			'done': false
    265 		};
    266 
    267 		/* eslint-enable no-underscore-dangle */
    268 	}
    269 
    270 	/**
    271 	* Finishes an iterator.
    272 	*
    273 	* @private
    274 	* @param {*} [value] - value to return
    275 	* @returns {Object} iterator protocol-compliant object
    276 	*/
    277 	function end( value ) {
    278 		FLG = true;
    279 		if ( arguments.length ) {
    280 			return {
    281 				'value': value,
    282 				'done': true
    283 			};
    284 		}
    285 		return {
    286 			'done': true
    287 		};
    288 	}
    289 
    290 	/**
    291 	* Returns a new iterator.
    292 	*
    293 	* @private
    294 	* @returns {Iterator} iterator
    295 	*/
    296 	function factory() {
    297 		return self.iterator( N );
    298 	}
    299 });
    300 
    301 /**
    302 * Circular buffer length (capacity).
    303 *
    304 * @name length
    305 * @memberof CircularBuffer.prototype
    306 * @type {NonNegativeInteger}
    307 *
    308 * @example
    309 * var b = new CircularBuffer( 4 );
    310 *
    311 * // Get the buffer capacity:
    312 * var len = b.length;
    313 * // returns 4
    314 */
    315 setReadOnlyAccessor( CircularBuffer.prototype, 'length', function get() {
    316 	return this._length;
    317 });
    318 
    319 /**
    320 * Adds a value to the circular buffer.
    321 *
    322 * @name push
    323 * @memberof CircularBuffer.prototype
    324 * @type {Function}
    325 * @returns {(*|void)} removed element or undefined
    326 *
    327 * @example
    328 * var b = new CircularBuffer( 3 );
    329 *
    330 * // Fill the buffer:
    331 * var v = b.push( 'foo' );
    332 * // returns undefined
    333 *
    334 * v = b.push( 'bar' );
    335 * // returns undefined
    336 *
    337 * v = b.push( 'beep' );
    338 * // returns undefined
    339 *
    340 * // Add another value to the buffer and return the removed element:
    341 * v = b.push( 'boop' );
    342 * // returns 'foo'
    343 */
    344 setReadOnly( CircularBuffer.prototype, 'push', function push( value ) {
    345 	var v;
    346 
    347 	// Compute the next buffer index:
    348 	this._i = (this._i+1) % this._length;
    349 
    350 	// Check if we are still filling the buffer...
    351 	if ( this._count < this._length ) {
    352 		this._buffer[ this._i ] = value;
    353 		this._count += 1;
    354 		return;
    355 	}
    356 	// Replace an existing buffer element...
    357 	v = this._buffer[ this._i ];
    358 	this._buffer[ this._i ] = value;
    359 	return v;
    360 });
    361 
    362 /**
    363 * Returns an array of circular buffer values.
    364 *
    365 * @name toArray
    366 * @memberof CircularBuffer.prototype
    367 * @type {Function}
    368 * @returns {Array} circular buffer values
    369 *
    370 * @example
    371 * var b = new CircularBuffer( 3 );
    372 *
    373 * // Add values to the buffer:
    374 * b.push( 'foo' );
    375 * b.push( 'bar' );
    376 * b.push( 'beep' );
    377 * b.push( 'boop' );
    378 *
    379 * // Get an array of buffer values:
    380 * var vals = b.toArray();
    381 * // returns [ 'bar', 'beep', 'boop' ]
    382 */
    383 setReadOnly( CircularBuffer.prototype, 'toArray', function toArray() {
    384 	var out;
    385 	var k;
    386 	var i;
    387 
    388 	out = [];
    389 	for ( i = 1; i <= this._count; i++ ) {
    390 		// Note: in a full buffer, `count == length`; in a partially full buffer, we need to ensure we always start at index `0`
    391 		k = (this._i+i) % this._count;
    392 		out.push( this._buffer[ k ] );
    393 	}
    394 	return out;
    395 });
    396 
    397 /**
    398 * Serializes a circular buffer as JSON.
    399 *
    400 * ## Notes
    401 *
    402 * -   `JSON.stringify()` implicitly calls this method when stringifying a `CircularBuffer` instance.
    403 *
    404 * @name toJSON
    405 * @memberof CircularBuffer.prototype
    406 * @type {Function}
    407 * @returns {Object} serialized circular buffer
    408 *
    409 * @example
    410 * var b = new CircularBuffer( 3 );
    411 *
    412 * // Add values to the buffer:
    413 * b.push( 'foo' );
    414 * b.push( 'bar' );
    415 * b.push( 'beep' );
    416 * b.push( 'boop' );
    417 *
    418 * // Serialize to JSON:
    419 * var o = b.toJSON();
    420 * // returns { 'type': 'circular-buffer', 'length': 3, 'data': [ 'bar', 'beep', 'boop' ] }
    421 */
    422 setReadOnly( CircularBuffer.prototype, 'toJSON', function toJSON() {
    423 	var out = {};
    424 	out.type = 'circular-buffer';
    425 	out.length = this._length;
    426 	out.data = this.toArray();
    427 	return out;
    428 });
    429 
    430 
    431 // EXPORTS //
    432 
    433 module.exports = CircularBuffer;