main.js (9113B)
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 setReadOnly = require( './../../define-nonenumerable-read-only-property' ); 26 var setReadOnlyAccessor = require( './../../define-nonenumerable-read-only-accessor' ); 27 var iteratorSymbol = require( '@stdlib/symbol/iterator' ); 28 var Node = require( './node.js' ); // eslint-disable-line stdlib/no-redeclare 29 30 31 // MAIN // 32 33 /** 34 * First-in-first-out queue constructor. 35 * 36 * @constructor 37 * @returns {FIFO} FIFO queue instance 38 * 39 * @example 40 * var queue = new FIFO(); 41 * 42 * // Add values to the queue: 43 * queue.push( 'foo' ).push( 'bar' ); 44 * 45 * // Remove the first value: 46 * var v = queue.pop(); 47 * // returns 'foo' 48 * 49 * // Add a new value to the queue: 50 * queue.push( 'beep' ); 51 * 52 * // Remove the "oldest" queue value: 53 * v = queue.pop(); 54 * // returns 'bar' 55 */ 56 function FIFO() { 57 if ( !(this instanceof FIFO) ) { 58 return new FIFO(); 59 } 60 this._length = 0; 61 this._first = null; 62 this._last = null; 63 return this; 64 } 65 66 /** 67 * Clears the queue. 68 * 69 * @name clear 70 * @memberof FIFO.prototype 71 * @type {Function} 72 * @returns {FIFO} queue instance 73 * 74 * @example 75 * var queue = new FIFO(); 76 * 77 * // Add values to the queue: 78 * queue.push( 'foo' ).push( 'bar' ); 79 * 80 * // Peek at the first value: 81 * var v = queue.first(); 82 * // returns 'foo' 83 * 84 * // Examine the queue length: 85 * var len = queue.length; 86 * // returns 2 87 * 88 * // Clear all queue items: 89 * queue.clear(); 90 * 91 * // Peek at the first value: 92 * v = queue.first(); 93 * // returns undefined 94 * 95 * // Examine the queue length: 96 * len = queue.length; 97 * // returns 0 98 */ 99 setReadOnly( FIFO.prototype, 'clear', function clear() { 100 this._length = 0; 101 this._first = null; 102 this._last = null; 103 return this; 104 }); 105 106 /** 107 * Returns the "oldest" queue value (i.e., the value which is "first-out"). 108 * 109 * @name first 110 * @memberof FIFO.prototype 111 * @type {Function} 112 * @returns {(*|void)} "oldest" queue value 113 * 114 * @example 115 * var queue = new FIFO(); 116 * 117 * // Add values to the queue: 118 * queue.push( 'foo' ).push( 'bar' ); 119 * 120 * // Peek at the first value: 121 * var v = queue.first(); 122 * // returns 'foo' 123 */ 124 setReadOnly( FIFO.prototype, 'first', function first() { 125 if ( this._length ) { 126 return this._first.value; 127 } 128 }); 129 130 /** 131 * Returns an iterator for iterating over a queue. 132 * 133 * ## Notes 134 * 135 * - In order to prevent confusion arising from queue mutation during iteration, a returned iterator **always** iterates over a queue "snapshot", which is defined as the list of queue elements at the time of this method's invocation. 136 * 137 * @name iterator 138 * @memberof FIFO.prototype 139 * @type {Function} 140 * @returns {Iterator} iterator 141 * 142 * @example 143 * var queue = new FIFO(); 144 * 145 * // Add values to the queue: 146 * queue.push( 'foo' ).push( 'bar' ); 147 * 148 * // Create an iterator: 149 * var it = queue.iterator(); 150 * 151 * // Iterate over the queue... 152 * var v = it.next().value; 153 * // returns 'foo' 154 * 155 * v = it.next().value; 156 * // returns 'bar' 157 * 158 * var bool = it.next().done; 159 * // returns true 160 */ 161 setReadOnly( FIFO.prototype, 'iterator', function iterator() { 162 var values; 163 var iter; 164 var self; 165 var FLG; 166 var i; 167 168 self = this; 169 170 // Initialize the iteration index: 171 i = -1; 172 173 // Create a copy of queue values (necessary in order to "snapshot" the queue; otherwise, values could come and go between calls to `next`): 174 values = this.toArray(); 175 176 // Create an iterator protocol-compliant object: 177 iter = {}; 178 setReadOnly( iter, 'next', next ); 179 setReadOnly( iter, 'return', end ); 180 if ( iteratorSymbol ) { 181 setReadOnly( iter, iteratorSymbol, factory ); 182 } 183 return iter; 184 185 /** 186 * Returns an iterator protocol-compliant object containing the next iterated value. 187 * 188 * @private 189 * @returns {Object} iterator protocol-compliant object 190 */ 191 function next() { 192 i += 1; 193 if ( FLG || i >= values.length ) { 194 return { 195 'done': true 196 }; 197 } 198 return { 199 'value': values[ i ], 200 'done': false 201 }; 202 } 203 204 /** 205 * Finishes an iterator. 206 * 207 * @private 208 * @param {*} [value] - value to return 209 * @returns {Object} iterator protocol-compliant object 210 */ 211 function end( value ) { 212 FLG = true; 213 if ( arguments.length ) { 214 return { 215 'value': value, 216 'done': true 217 }; 218 } 219 return { 220 'done': true 221 }; 222 } 223 224 /** 225 * Returns a new iterator. 226 * 227 * @private 228 * @returns {Iterator} iterator 229 */ 230 function factory() { 231 return self.iterator(); 232 } 233 }); 234 235 /** 236 * Returns the "newest" queue value (i.e., the value which is currently "last-out"). 237 * 238 * @name last 239 * @memberof FIFO.prototype 240 * @type {Function} 241 * @returns {(*|void)} "newest" queue value 242 * 243 * @example 244 * var queue = new FIFO(); 245 * 246 * // Add values to the queue: 247 * queue.push( 'foo' ).push( 'bar' ); 248 * 249 * // Peek at the last value: 250 * var v = queue.last(); 251 * // returns 'bar' 252 */ 253 setReadOnly( FIFO.prototype, 'last', function last() { 254 if ( this._length ) { 255 return this._last.value; 256 } 257 }); 258 259 /** 260 * Queue length. 261 * 262 * @name length 263 * @memberof FIFO.prototype 264 * @type {NonNegativeInteger} 265 * 266 * @example 267 * var queue = new FIFO(); 268 * 269 * // Examine the initial queue length: 270 * var len = queue.length; 271 * // returns 0 272 * 273 * // Add values to the queue: 274 * queue.push( 'foo' ).push( 'bar' ); 275 * 276 * // Retrieve the current queue length: 277 * len = queue.length; 278 * // returns 2 279 */ 280 setReadOnlyAccessor( FIFO.prototype, 'length', function get() { 281 return this._length; 282 }); 283 284 /** 285 * Removes a value from the queue. 286 * 287 * @name pop 288 * @memberof FIFO.prototype 289 * @type {Function} 290 * @returns {(*|void)} removed value 291 * 292 * @example 293 * var queue = new FIFO(); 294 * 295 * // Add values to the queue: 296 * queue.push( 'foo' ).push( 'bar' ); 297 * 298 * // Remove the first value: 299 * var v = queue.pop(); 300 * // returns 'foo' 301 * 302 * // Add a new value to the queue: 303 * queue.push( 'beep' ); 304 * 305 * // Remove the "oldest" queue value: 306 * v = queue.pop(); 307 * // returns 'bar' 308 */ 309 setReadOnly( FIFO.prototype, 'pop', function pop() { 310 var value; 311 if ( this._length ) { 312 // Retrieve the "first-out" value: 313 value = this._first.value; 314 315 // Check whether we have a new "first-out" or whether we have drained the queue... 316 if ( this._first.next ) { 317 this._first = this._first.next; 318 this._first.prev = null; 319 } else { 320 // Queue is empty: 321 this._first = null; 322 this._last = null; 323 } 324 // Decrement the queue length: 325 this._length -= 1; 326 } 327 return value; 328 }); 329 330 /** 331 * Adds a value to the queue. 332 * 333 * @name push 334 * @memberof FIFO.prototype 335 * @type {Function} 336 * @returns {FIFO} queue instance 337 * 338 * @example 339 * var queue = new FIFO(); 340 * 341 * // Add values to the queue: 342 * queue.push( 'foo' ).push( 'bar' ); 343 * 344 * // Remove the first value: 345 * var v = queue.pop(); 346 * // returns 'foo' 347 * 348 * // Add a new value to the queue: 349 * queue.push( 'beep' ); 350 * 351 * // Remove the "oldest" queue value: 352 * v = queue.pop(); 353 * // returns 'bar' 354 */ 355 setReadOnly( FIFO.prototype, 'push', function push( value ) { 356 var node; 357 358 // Create a new queue node: 359 node = new Node( value ); 360 361 // Check whether the queue is currently empty... 362 if ( this._length === 0 ) { 363 // This is the only queued node, making it both the first and last node: 364 this._first = node; 365 this._last = node; 366 } else { 367 // Link the node to the previous most "recent" node: 368 node.prev = this._last; 369 370 // Link the previous most "recent" node to the new node: 371 this._last.next = node; 372 373 // Update the queue pointer for the "last" node to the new node: 374 this._last = node; 375 } 376 // Increment the queue length: 377 this._length += 1; 378 379 return this; 380 }); 381 382 /** 383 * Returns an array of queue values. 384 * 385 * @name toArray 386 * @memberof FIFO.prototype 387 * @type {Function} 388 * @returns {Array} queue values 389 * 390 * @example 391 * var queue = new FIFO(); 392 * 393 * // Add values to the queue: 394 * queue.push( 'foo' ).push( 'bar' ); 395 * 396 * // Get an array of queue values: 397 * var vals = queue.toArray(); 398 * // returns [ 'foo', 'bar' ] 399 */ 400 setReadOnly( FIFO.prototype, 'toArray', function toArray() { 401 var node; 402 var out; 403 var i; 404 405 out = []; 406 node = this._first; 407 for ( i = 0; i < this._length; i++ ) { 408 out.push( node.value ); 409 node = node.next; 410 } 411 return out; 412 }); 413 414 /** 415 * Serializes a queue as JSON. 416 * 417 * ## Notes 418 * 419 * - `JSON.stringify()` implicitly calls this method when stringifying a `FIFO` instance. 420 * 421 * @name toJSON 422 * @memberof FIFO.prototype 423 * @type {Function} 424 * @returns {Object} serialized queue 425 * 426 * @example 427 * var queue = new FIFO(); 428 * 429 * // Add values to the queue: 430 * queue.push( 'foo' ).push( 'bar' ); 431 * 432 * // Serialize to JSON: 433 * var o = queue.toJSON(); 434 * // returns { 'type': 'fifo', 'data': [ 'foo', 'bar' ] } 435 */ 436 setReadOnly( FIFO.prototype, 'toJSON', function toJSON() { 437 var out = {}; 438 out.type = 'fifo'; 439 out.data = this.toArray(); 440 return out; 441 }); 442 443 444 // EXPORTS // 445 446 module.exports = FIFO;