package sctp
import (
"fmt"
"math/bits"
)
type receivePayloadQueue struct {
tailTSN uint32
chunkSize int
tsnBitmask []uint64
dupTSN []uint32
maxTSNOffset uint32
cumulativeTSN uint32
}
func newReceivePayloadQueue(maxTSNOffset uint32 ) *receivePayloadQueue {
maxTSNOffset = ((maxTSNOffset + 63 ) / 64 ) * 64
return &receivePayloadQueue {
tsnBitmask : make ([]uint64 , maxTSNOffset /64 ),
maxTSNOffset : maxTSNOffset ,
}
}
func (q *receivePayloadQueue ) init (cumulativeTSN uint32 ) {
q .cumulativeTSN = cumulativeTSN
q .tailTSN = cumulativeTSN
q .chunkSize = 0
for i := range q .tsnBitmask {
q .tsnBitmask [i ] = 0
}
q .dupTSN = q .dupTSN [:0 ]
}
func (q *receivePayloadQueue ) hasChunk (tsn uint32 ) bool {
if q .chunkSize == 0 || sna32LTE (tsn , q .cumulativeTSN ) || sna32GT (tsn , q .tailTSN ) {
return false
}
index , offset := int (tsn /64 )%len (q .tsnBitmask ), tsn %64
return q .tsnBitmask [index ]&(1 <<offset ) != 0
}
func (q *receivePayloadQueue ) canPush (tsn uint32 ) bool {
ok := q .hasChunk (tsn )
if ok || sna32LTE (tsn , q .cumulativeTSN ) || sna32GT (tsn , q .cumulativeTSN +q .maxTSNOffset ) {
return false
}
return true
}
func (q *receivePayloadQueue ) push (tsn uint32 ) bool {
if sna32GT (tsn , q .cumulativeTSN +q .maxTSNOffset ) {
return false
}
if sna32LTE (tsn , q .cumulativeTSN ) || q .hasChunk (tsn ) {
q .dupTSN = append (q .dupTSN , tsn )
return false
}
index , offset := int (tsn /64 )%len (q .tsnBitmask ), tsn %64
q .tsnBitmask [index ] |= (1 << offset )
q .chunkSize ++
if sna32GT (tsn , q .tailTSN ) {
q .tailTSN = tsn
}
return true
}
func (q *receivePayloadQueue ) pop (force bool ) bool {
tsn := q .cumulativeTSN + 1
if q .hasChunk (tsn ) {
index , offset := int (tsn /64 )%len (q .tsnBitmask ), int (tsn %64 )
q .tsnBitmask [index ] &= ^uint64 (1 << (offset ))
q .chunkSize --
q .cumulativeTSN ++
return true
}
if force {
q .cumulativeTSN ++
if q .chunkSize == 0 {
q .tailTSN = q .cumulativeTSN
}
}
return false
}
func (q *receivePayloadQueue ) popDuplicates () []uint32 {
dups := q .dupTSN
q .dupTSN = []uint32 {}
return dups
}
func (q *receivePayloadQueue ) getGapAckBlocks () (gapAckBlocks []gapAckBlock ) {
var ackBlock gapAckBlock
if q .chunkSize == 0 {
return nil
}
startTSN , endTSN := q .cumulativeTSN +1 , q .tailTSN
var findEnd bool
for tsn := startTSN ; sna32LTE (tsn , endTSN ); {
index , offset := int (tsn /64 )%len (q .tsnBitmask ), int (tsn %64 )
if !findEnd {
if nonZeroBit , ok := getFirstNonZeroBit (q .tsnBitmask [index ], offset , 64 ); ok {
ackBlock .start = uint16 (tsn + uint32 (nonZeroBit -offset ) - q .cumulativeTSN )
tsn += uint32 (nonZeroBit - offset )
findEnd = true
} else {
tsn += uint32 (64 - offset )
}
} else {
if zeroBit , ok := getFirstZeroBit (q .tsnBitmask [index ], offset , 64 ); ok {
ackBlock .end = uint16 (tsn + uint32 (zeroBit -offset ) - 1 - q .cumulativeTSN )
tsn += uint32 (zeroBit - offset )
if sna32LTE (tsn , endTSN ) {
gapAckBlocks = append (gapAckBlocks , gapAckBlock {
start : ackBlock .start ,
end : ackBlock .end ,
})
}
findEnd = false
} else {
tsn += uint32 (64 - offset )
}
if sna32GT (tsn , endTSN ) {
ackBlock .end = uint16 (endTSN - q .cumulativeTSN )
gapAckBlocks = append (gapAckBlocks , gapAckBlock {
start : ackBlock .start ,
end : ackBlock .end ,
})
break
}
}
}
return gapAckBlocks
}
func (q *receivePayloadQueue ) getGapAckBlocksString () string {
gapAckBlocks := q .getGapAckBlocks ()
str := fmt .Sprintf ("cumTSN=%d" , q .cumulativeTSN )
for _ , b := range gapAckBlocks {
str += fmt .Sprintf (",%d-%d" , b .start , b .end )
}
return str
}
func (q *receivePayloadQueue ) getLastTSNReceived () (uint32 , bool ) {
if q .chunkSize == 0 {
return 0 , false
}
return q .tailTSN , true
}
func (q *receivePayloadQueue ) getcumulativeTSN () uint32 {
return q .cumulativeTSN
}
func (q *receivePayloadQueue ) size () int {
return q .chunkSize
}
func getFirstNonZeroBit(val uint64 , start , end int ) (int , bool ) {
i := bits .TrailingZeros64 (val >> uint64 (start ))
return i + start , i +start < end
}
func getFirstZeroBit(val uint64 , start , end int ) (int , bool ) {
return getFirstNonZeroBit (^val , start , end )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .