2022-10-02 16:22:05 +00:00
window . RING _BUFF _CONSECUTIVE _SET = 0 ;
window . RING _BUFF _NON _CONSECUTIVE _SET = 1 ;
window . RING _BUFF _FAILED _TO _SET = 2 ;
2022-09-24 04:01:50 +00:00
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 ) {
2023-01-21 14:53:41 +00:00
let firstPopped = null ;
2022-12-02 02:20:58 +00:00
while ( 0 < this . cnt && this . cnt >= this . n ) {
2022-11-29 13:32:18 +00:00
// Make room for the new element
2023-01-21 14:53:41 +00:00
const popped = this . pop ( ) ;
if ( null == firstPopped )
firstPopped = popped ;
2022-11-29 13:32:18 +00:00
}
2022-09-24 04:01:50 +00:00
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
}
2023-01-21 14:53:41 +00:00
return firstPopped ;
2022-09-24 04:01:50 +00:00
} ;
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 ;
} ;
2022-10-02 16:22:05 +00:00
RingBuffer . prototype . getArrIdxByOffset = function ( offsetFromSt ) {
if ( 0 > offsetFromSt || 0 == this . cnt ) {
2022-09-24 04:01:50 +00:00
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 ) {
2022-10-02 16:22:05 +00:00
return arrIdx ;
2022-09-24 04:01:50 +00:00
}
} 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 ) {
2022-10-02 16:22:05 +00:00
return arrIdx ;
2022-09-24 04:01:50 +00:00
}
}
return null ;
} ;
RingBuffer . prototype . getByFrameId = function ( frameId ) {
2022-12-02 02:20:58 +00:00
if ( frameId >= this . edFrameId || frameId < this . stFrameId ) return null ;
2022-10-02 16:22:05 +00:00
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 ) {
2022-11-29 13:32:18 +00:00
const oldStFrameId = this . stFrameId ,
oldEdFrameId = this . edFrameId ;
2022-11-30 13:51:06 +00:00
if ( frameId < oldStFrameId ) {
2022-11-29 13:32:18 +00:00
return [ window . RING _BUFF _FAILED _TO _SET , oldStFrameId , oldEdFrameId ] ;
2022-10-02 16:22:05 +00:00
}
2022-11-29 13:32:18 +00:00
// By now "this.stFrameId <= frameId"
2022-11-30 13:51:06 +00:00
if ( oldEdFrameId > frameId ) {
2022-11-29 13:32:18 +00:00
const arrIdx = this . getArrIdxByOffset ( frameId - this . stFrameId ) ;
if ( null != arrIdx ) {
this . eles [ arrIdx ] = item ;
return [ window . RING _BUFF _CONSECUTIVE _SET , oldStFrameId , oldEdFrameId ] ;
}
2022-10-02 16:22:05 +00:00
}
2022-11-29 13:32:18 +00:00
// By now "this.edFrameId <= frameId"
2022-10-02 16:22:05 +00:00
let ret = window . RING _BUFF _CONSECUTIVE _SET ;
2022-11-30 13:51:06 +00:00
if ( oldEdFrameId < frameId ) {
2022-10-02 16:22:05 +00:00
this . st = this . ed = 0 ;
this . stFrameId = this . edFrameId = frameId ;
this . cnt = 0 ;
ret = window . RING _BUFF _NON _CONSECUTIVE _SET ;
}
2022-11-30 13:51:06 +00:00
// By now "this.edFrameId == frameId"
this . put ( item ) ;
2022-11-29 13:32:18 +00:00
return [ ret , oldStFrameId , oldEdFrameId ] ;
2022-09-24 04:01:50 +00:00
} ;
module . exports = RingBuffer ;