status-go/vendor/github.com/pion/sctp/payload_queue.go

183 lines
3.8 KiB
Go

// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package sctp
import (
"fmt"
"sort"
)
type payloadQueue struct {
chunkMap map[uint32]*chunkPayloadData
sorted []uint32
dupTSN []uint32
nBytes int
}
func newPayloadQueue() *payloadQueue {
return &payloadQueue{chunkMap: map[uint32]*chunkPayloadData{}}
}
func (q *payloadQueue) updateSortedKeys() {
if q.sorted != nil {
return
}
q.sorted = make([]uint32, len(q.chunkMap))
i := 0
for k := range q.chunkMap {
q.sorted[i] = k
i++
}
sort.Slice(q.sorted, func(i, j int) bool {
return sna32LT(q.sorted[i], q.sorted[j])
})
}
func (q *payloadQueue) canPush(p *chunkPayloadData, cumulativeTSN uint32, maxTSNOffset uint32) bool {
_, ok := q.chunkMap[p.tsn]
if ok || sna32LTE(p.tsn, cumulativeTSN) || sna32GTE(p.tsn, cumulativeTSN+maxTSNOffset) {
return false
}
return true
}
func (q *payloadQueue) pushNoCheck(p *chunkPayloadData) {
q.chunkMap[p.tsn] = p
q.nBytes += len(p.userData)
q.sorted = nil
}
// push pushes a payload data. If the payload data is already in our queue or
// older than our cumulativeTSN marker, it will be recored as duplications,
// which can later be retrieved using popDuplicates.
func (q *payloadQueue) push(p *chunkPayloadData, cumulativeTSN uint32) bool {
_, ok := q.chunkMap[p.tsn]
if ok || sna32LTE(p.tsn, cumulativeTSN) {
// Found the packet, log in dups
q.dupTSN = append(q.dupTSN, p.tsn)
return false
}
q.chunkMap[p.tsn] = p
q.nBytes += len(p.userData)
q.sorted = nil
return true
}
// pop pops only if the oldest chunk's TSN matches the given TSN.
func (q *payloadQueue) pop(tsn uint32) (*chunkPayloadData, bool) {
q.updateSortedKeys()
if len(q.chunkMap) > 0 && tsn == q.sorted[0] {
q.sorted = q.sorted[1:]
if c, ok := q.chunkMap[tsn]; ok {
delete(q.chunkMap, tsn)
q.nBytes -= len(c.userData)
return c, true
}
}
return nil, false
}
// get returns reference to chunkPayloadData with the given TSN value.
func (q *payloadQueue) get(tsn uint32) (*chunkPayloadData, bool) {
c, ok := q.chunkMap[tsn]
return c, ok
}
// popDuplicates returns an array of TSN values that were found duplicate.
func (q *payloadQueue) popDuplicates() []uint32 {
dups := q.dupTSN
q.dupTSN = []uint32{}
return dups
}
func (q *payloadQueue) getGapAckBlocks(cumulativeTSN uint32) (gapAckBlocks []gapAckBlock) {
var b gapAckBlock
if len(q.chunkMap) == 0 {
return []gapAckBlock{}
}
q.updateSortedKeys()
for i, tsn := range q.sorted {
if i == 0 {
b.start = uint16(tsn - cumulativeTSN)
b.end = b.start
continue
}
diff := uint16(tsn - cumulativeTSN)
if b.end+1 == diff {
b.end++
} else {
gapAckBlocks = append(gapAckBlocks, gapAckBlock{
start: b.start,
end: b.end,
})
b.start = diff
b.end = diff
}
}
gapAckBlocks = append(gapAckBlocks, gapAckBlock{
start: b.start,
end: b.end,
})
return gapAckBlocks
}
func (q *payloadQueue) getGapAckBlocksString(cumulativeTSN uint32) string {
gapAckBlocks := q.getGapAckBlocks(cumulativeTSN)
str := fmt.Sprintf("cumTSN=%d", cumulativeTSN)
for _, b := range gapAckBlocks {
str += fmt.Sprintf(",%d-%d", b.start, b.end)
}
return str
}
func (q *payloadQueue) markAsAcked(tsn uint32) int {
var nBytesAcked int
if c, ok := q.chunkMap[tsn]; ok {
c.acked = true
c.retransmit = false
nBytesAcked = len(c.userData)
q.nBytes -= nBytesAcked
c.userData = []byte{}
}
return nBytesAcked
}
func (q *payloadQueue) getLastTSNReceived() (uint32, bool) {
q.updateSortedKeys()
qlen := len(q.sorted)
if qlen == 0 {
return 0, false
}
return q.sorted[qlen-1], true
}
func (q *payloadQueue) markAllToRetrasmit() {
for _, c := range q.chunkMap {
if c.acked || c.abandoned() {
continue
}
c.retransmit = true
}
}
func (q *payloadQueue) getNumBytes() int {
return q.nBytes
}
func (q *payloadQueue) size() int {
return len(q.chunkMap)
}