mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 03:39:00 +00:00
110 lines
3.0 KiB
JavaScript
110 lines
3.0 KiB
JavaScript
window.RING_BUFF_CONSECUTIVE_SET = 0;
|
|
window.RING_BUFF_NON_CONSECUTIVE_SET = 1;
|
|
window.RING_BUFF_FAILED_TO_SET = 2;
|
|
|
|
var RingBuffer = function(capacity) {
|
|
this.ed = 0; // write index, open index
|
|
this.st = 0; // read index, closed index
|
|
this.edFrameId = 0;
|
|
this.stFrameId = 0;
|
|
this.n = capacity;
|
|
this.cnt = 0; // the count of valid elements in the buffer, used mainly to distinguish what "st == ed" means for "Pop" and "Get" methods
|
|
this.eles = new Array(capacity).fill(null);
|
|
};
|
|
|
|
RingBuffer.prototype.put = function(item) {
|
|
let firstPopped = null;
|
|
while (0 < this.cnt && this.cnt >= this.n) {
|
|
// Make room for the new element
|
|
const popped = this.pop();
|
|
if (null == firstPopped)
|
|
firstPopped = popped;
|
|
}
|
|
this.eles[this.ed] = item
|
|
this.edFrameId++;
|
|
this.cnt++;
|
|
this.ed++;
|
|
if (this.ed >= this.n) {
|
|
this.ed -= this.n; // Deliberately not using "%" operator for performance concern
|
|
}
|
|
return firstPopped;
|
|
};
|
|
|
|
RingBuffer.prototype.pop = function() {
|
|
if (0 == this.cnt) {
|
|
return null;
|
|
}
|
|
const item = this.eles[this.st];
|
|
this.stFrameId++;
|
|
this.cnt--;
|
|
this.st++;
|
|
if (this.st >= this.n) {
|
|
this.st -= this.n;
|
|
}
|
|
return item;
|
|
};
|
|
|
|
RingBuffer.prototype.getArrIdxByOffset = function(offsetFromSt) {
|
|
if (0 > offsetFromSt || 0 == this.cnt) {
|
|
return null;
|
|
}
|
|
let arrIdx = this.st + offsetFromSt;
|
|
if (this.st < this.ed) {
|
|
// case#1: 0...st...ed...n-1
|
|
if (this.st <= arrIdx && arrIdx < this.ed) {
|
|
return arrIdx;
|
|
}
|
|
} else {
|
|
// if this.st >= this.sd
|
|
// case#2: 0...ed...st...n-1
|
|
if (arrIdx >= this.n) {
|
|
arrIdx -= this.n
|
|
}
|
|
if (arrIdx >= this.st || arrIdx < this.ed) {
|
|
return arrIdx;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
RingBuffer.prototype.getByFrameId = function(frameId) {
|
|
if (frameId >= this.edFrameId || frameId < this.stFrameId) return null;
|
|
const arrIdx = this.getArrIdxByOffset(frameId - this.stFrameId);
|
|
return (null == arrIdx ? null : this.eles[arrIdx]);
|
|
};
|
|
|
|
// [WARNING] During a battle, frontend could receive non-consecutive frames (either renderFrame or inputFrame) due to resync, the buffer should handle these frames properly.
|
|
RingBuffer.prototype.setByFrameId = function(item, frameId) {
|
|
const oldStFrameId = this.stFrameId,
|
|
oldEdFrameId = this.edFrameId;
|
|
if (frameId < oldStFrameId) {
|
|
return [window.RING_BUFF_FAILED_TO_SET, oldStFrameId, oldEdFrameId];
|
|
}
|
|
// By now "this.stFrameId <= frameId"
|
|
|
|
if (oldEdFrameId > frameId) {
|
|
const arrIdx = this.getArrIdxByOffset(frameId - this.stFrameId);
|
|
if (null != arrIdx) {
|
|
this.eles[arrIdx] = item;
|
|
return [window.RING_BUFF_CONSECUTIVE_SET, oldStFrameId, oldEdFrameId];
|
|
}
|
|
}
|
|
|
|
// By now "this.edFrameId <= frameId"
|
|
let ret = window.RING_BUFF_CONSECUTIVE_SET;
|
|
if (oldEdFrameId < frameId) {
|
|
this.st = this.ed = 0;
|
|
this.stFrameId = this.edFrameId = frameId;
|
|
this.cnt = 0;
|
|
ret = window.RING_BUFF_NON_CONSECUTIVE_SET;
|
|
}
|
|
|
|
// By now "this.edFrameId == frameId"
|
|
this.put(item);
|
|
|
|
return [ret, oldStFrameId, oldEdFrameId];
|
|
};
|
|
|
|
module.exports = RingBuffer;
|