UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

234 lines (199 loc) 6.3 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2006 STZ-IDA, Germany, http://www.stz-ida.de 2009 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Carsten Lergenmueller (carstenl) * Fabian Jakobs (fjakobs) ************************************************************************ */ /** * An memory container which stores arbitrary data up to a maximum number of * entries. When new entries come in an the maximum is reached, the oldest * entries are deleted. * * A mark feature also exists which can be used to remember a point in time. * When retrieving entries, it is possible to get only those entries * after the marked time. This is useful if data from the buffer is extracted * and processed. Whenever this happens, a mark() call can be used so that the * next extraction will only get new data. */ qx.Bootstrap.define("qx.util.RingBuffer", { extend: Object, /** * Constructor. * * @param maxEntries {Integer ? 50} Maximum number of entries in the buffer */ construct(maxEntries) { this.setMaxEntries(maxEntries || 50); }, members: { //Next slot in ringbuffer to use __nextIndexToStoreTo: 0, //Number of elements in ring buffer __entriesStored: 0, //Was a mark set? __isMarkActive: false, //How many elements were stored since setting of mark? __entriesStoredSinceMark: 0, //ring buffer __entries: null, //Maximum number of messages to store. Could be converted to a qx property. __maxEntries: null, /** * Set the maximum number of messages to hold. If null the number of * messages is not limited. * * Warning: Changing this property will clear the events logged so far. * * @param maxEntries {Integer} the maximum number of messages to hold */ setMaxEntries(maxEntries) { this.__maxEntries = maxEntries; this.clear(); }, /** * Get the maximum number of entries to hold * * @return {Integer} */ getMaxEntries() { return this.__maxEntries; }, /** * Adds a single entry * * @param entry {var} The data to store */ addEntry(entry) { this.__entries[this.__nextIndexToStoreTo] = entry; this.__nextIndexToStoreTo = this.__addToIndex( this.__nextIndexToStoreTo, 1 ); //Count # of stored entries var max = this.getMaxEntries(); if (this.__entriesStored < max) { this.__entriesStored++; } //Count # of stored elements since last mark call if (this.__isMarkActive && this.__entriesStoredSinceMark < max) { this.__entriesStoredSinceMark++; } }, /** * Returns the number of entries stored * @return {Integer} */ getNumEntriesStored() { return this.__entriesStored; }, /** * Remembers the current position in the ring buffer * */ mark() { this.__isMarkActive = true; this.__entriesStoredSinceMark = 0; }, /** * Removes the current mark position */ clearMark() { this.__isMarkActive = false; }, /** * Returns all stored entries. Mark is ignored. * * @return {Array} array of stored entries */ getAllEntries() { return this.getEntries(this.getMaxEntries(), false); }, /** * Returns entries which have been added previously. * * @param count {Integer} The number of entries to retrieve. If there are * more entries than the given count, the oldest ones will not be returned. * * @param startingFromMark {Boolean ? false} If true, only entries since * the last call to mark() will be returned * @return {Array} array of stored entries */ getEntries(count, startingFromMark) { //Trim count so it does not exceed ringbuffer size if (count > this.__entriesStored) { count = this.__entriesStored; } // Trim count so it does not exceed last call to mark (if mark was called // and startingFromMark was true) if ( startingFromMark && this.__isMarkActive && count > this.__entriesStoredSinceMark ) { count = this.__entriesStoredSinceMark; } if (count > 0) { var indexOfYoungestElementInHistory = this.__addToIndex( this.__nextIndexToStoreTo, -1 ); var startIndex = this.__addToIndex( indexOfYoungestElementInHistory, -count + 1 ); var result; if (startIndex <= indexOfYoungestElementInHistory) { //Requested segment not wrapping around ringbuffer boundary, get in one run result = this.__entries.slice( startIndex, indexOfYoungestElementInHistory + 1 ); } else { //Requested segment wrapping around ringbuffer boundary, get two parts & concat result = this.__entries .slice(startIndex, this.__entriesStored) .concat( this.__entries.slice(0, indexOfYoungestElementInHistory + 1) ); } } else { result = []; } return result; }, /** * Clears all entries */ clear() { this.__entries = new Array(this.getMaxEntries()); this.__entriesStored = 0; this.__entriesStoredSinceMark = 0; this.__nextIndexToStoreTo = 0; }, /** * Adds a number to an ringbuffer index. Does a modulus calculation, * i. e. if the index leaves the ringbuffer space it will wrap around to * the other end of the ringbuffer. * * @param idx {Number} The current index. * @param addMe {Number} The number to add. * @return {Number} The new index */ __addToIndex(idx, addMe) { var max = this.getMaxEntries(); var result = (idx + addMe) % max; //If negative, wrap up into the ringbuffer space if (result < 0) { result += max; } return result; } } });