DelayNoMore/frontend/assets/scripts/RingBuffer.js
2023-01-21 22:53:41 +08:00

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;