183 lines
3.8 KiB
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)
|
|
}
|