mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
224 lines
5.8 KiB
Go
224 lines
5.8 KiB
Go
package quic
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
)
|
|
|
|
type frameSorterEntry struct {
|
|
Data []byte
|
|
DoneCb func()
|
|
}
|
|
|
|
type frameSorter struct {
|
|
queue map[protocol.ByteCount]frameSorterEntry
|
|
readPos protocol.ByteCount
|
|
gaps *utils.ByteIntervalList
|
|
}
|
|
|
|
var errDuplicateStreamData = errors.New("duplicate stream data")
|
|
|
|
func newFrameSorter() *frameSorter {
|
|
s := frameSorter{
|
|
gaps: utils.NewByteIntervalList(),
|
|
queue: make(map[protocol.ByteCount]frameSorterEntry),
|
|
}
|
|
s.gaps.PushFront(utils.ByteInterval{Start: 0, End: protocol.MaxByteCount})
|
|
return &s
|
|
}
|
|
|
|
func (s *frameSorter) Push(data []byte, offset protocol.ByteCount, doneCb func()) error {
|
|
err := s.push(data, offset, doneCb)
|
|
if err == errDuplicateStreamData {
|
|
if doneCb != nil {
|
|
doneCb()
|
|
}
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (s *frameSorter) push(data []byte, offset protocol.ByteCount, doneCb func()) error {
|
|
if len(data) == 0 {
|
|
return errDuplicateStreamData
|
|
}
|
|
|
|
start := offset
|
|
end := offset + protocol.ByteCount(len(data))
|
|
|
|
if end <= s.gaps.Front().Value.Start {
|
|
return errDuplicateStreamData
|
|
}
|
|
|
|
startGap, startsInGap := s.findStartGap(start)
|
|
endGap, endsInGap := s.findEndGap(startGap, end)
|
|
|
|
startGapEqualsEndGap := startGap == endGap
|
|
|
|
if (startGapEqualsEndGap && end <= startGap.Value.Start) ||
|
|
(!startGapEqualsEndGap && startGap.Value.End >= endGap.Value.Start && end <= startGap.Value.Start) {
|
|
return errDuplicateStreamData
|
|
}
|
|
|
|
startGapNext := startGap.Next()
|
|
startGapEnd := startGap.Value.End // save it, in case startGap is modified
|
|
endGapStart := endGap.Value.Start // save it, in case endGap is modified
|
|
endGapEnd := endGap.Value.End // save it, in case endGap is modified
|
|
var adjustedStartGapEnd bool
|
|
var wasCut bool
|
|
|
|
pos := start
|
|
var hasReplacedAtLeastOne bool
|
|
for {
|
|
oldEntry, ok := s.queue[pos]
|
|
if !ok {
|
|
break
|
|
}
|
|
oldEntryLen := protocol.ByteCount(len(oldEntry.Data))
|
|
if end-pos > oldEntryLen || (hasReplacedAtLeastOne && end-pos == oldEntryLen) {
|
|
// The existing frame is shorter than the new frame. Replace it.
|
|
delete(s.queue, pos)
|
|
pos += oldEntryLen
|
|
hasReplacedAtLeastOne = true
|
|
if oldEntry.DoneCb != nil {
|
|
oldEntry.DoneCb()
|
|
}
|
|
} else {
|
|
if !hasReplacedAtLeastOne {
|
|
return errDuplicateStreamData
|
|
}
|
|
// The existing frame is longer than the new frame.
|
|
// Cut the new frame such that the end aligns with the start of the existing frame.
|
|
data = data[:pos-start]
|
|
end = pos
|
|
wasCut = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !startsInGap && !hasReplacedAtLeastOne {
|
|
// cut the frame, such that it starts at the start of the gap
|
|
data = data[startGap.Value.Start-start:]
|
|
start = startGap.Value.Start
|
|
wasCut = true
|
|
}
|
|
if start <= startGap.Value.Start {
|
|
if end >= startGap.Value.End {
|
|
// The frame covers the whole startGap. Delete the gap.
|
|
s.gaps.Remove(startGap)
|
|
} else {
|
|
startGap.Value.Start = end
|
|
}
|
|
} else if !hasReplacedAtLeastOne {
|
|
startGap.Value.End = start
|
|
adjustedStartGapEnd = true
|
|
}
|
|
|
|
if !startGapEqualsEndGap {
|
|
s.deleteConsecutive(startGapEnd)
|
|
var nextGap *utils.ByteIntervalElement
|
|
for gap := startGapNext; gap.Value.End < endGapStart; gap = nextGap {
|
|
nextGap = gap.Next()
|
|
s.deleteConsecutive(gap.Value.End)
|
|
s.gaps.Remove(gap)
|
|
}
|
|
}
|
|
|
|
if !endsInGap && start != endGapEnd && end > endGapEnd {
|
|
// cut the frame, such that it ends at the end of the gap
|
|
data = data[:endGapEnd-start]
|
|
end = endGapEnd
|
|
wasCut = true
|
|
}
|
|
if end == endGapEnd {
|
|
if !startGapEqualsEndGap {
|
|
// The frame covers the whole endGap. Delete the gap.
|
|
s.gaps.Remove(endGap)
|
|
}
|
|
} else {
|
|
if startGapEqualsEndGap && adjustedStartGapEnd {
|
|
// The frame split the existing gap into two.
|
|
s.gaps.InsertAfter(utils.ByteInterval{Start: end, End: startGapEnd}, startGap)
|
|
} else if !startGapEqualsEndGap {
|
|
endGap.Value.Start = end
|
|
}
|
|
}
|
|
|
|
if wasCut && len(data) < protocol.MinStreamFrameBufferSize {
|
|
newData := make([]byte, len(data))
|
|
copy(newData, data)
|
|
data = newData
|
|
if doneCb != nil {
|
|
doneCb()
|
|
doneCb = nil
|
|
}
|
|
}
|
|
|
|
if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps {
|
|
return errors.New("too many gaps in received data")
|
|
}
|
|
|
|
s.queue[start] = frameSorterEntry{Data: data, DoneCb: doneCb}
|
|
return nil
|
|
}
|
|
|
|
func (s *frameSorter) findStartGap(offset protocol.ByteCount) (*utils.ByteIntervalElement, bool) {
|
|
for gap := s.gaps.Front(); gap != nil; gap = gap.Next() {
|
|
if offset >= gap.Value.Start && offset <= gap.Value.End {
|
|
return gap, true
|
|
}
|
|
if offset < gap.Value.Start {
|
|
return gap, false
|
|
}
|
|
}
|
|
panic("no gap found")
|
|
}
|
|
|
|
func (s *frameSorter) findEndGap(startGap *utils.ByteIntervalElement, offset protocol.ByteCount) (*utils.ByteIntervalElement, bool) {
|
|
for gap := startGap; gap != nil; gap = gap.Next() {
|
|
if offset >= gap.Value.Start && offset < gap.Value.End {
|
|
return gap, true
|
|
}
|
|
if offset < gap.Value.Start {
|
|
return gap.Prev(), false
|
|
}
|
|
}
|
|
panic("no gap found")
|
|
}
|
|
|
|
// deleteConsecutive deletes consecutive frames from the queue, starting at pos
|
|
func (s *frameSorter) deleteConsecutive(pos protocol.ByteCount) {
|
|
for {
|
|
oldEntry, ok := s.queue[pos]
|
|
if !ok {
|
|
break
|
|
}
|
|
oldEntryLen := protocol.ByteCount(len(oldEntry.Data))
|
|
delete(s.queue, pos)
|
|
if oldEntry.DoneCb != nil {
|
|
oldEntry.DoneCb()
|
|
}
|
|
pos += oldEntryLen
|
|
}
|
|
}
|
|
|
|
func (s *frameSorter) Pop() (protocol.ByteCount, []byte, func()) {
|
|
entry, ok := s.queue[s.readPos]
|
|
if !ok {
|
|
return s.readPos, nil, nil
|
|
}
|
|
delete(s.queue, s.readPos)
|
|
offset := s.readPos
|
|
s.readPos += protocol.ByteCount(len(entry.Data))
|
|
if s.gaps.Front().Value.End <= s.readPos {
|
|
panic("frame sorter BUG: read position higher than a gap")
|
|
}
|
|
return offset, entry.Data, entry.DoneCb
|
|
}
|
|
|
|
// HasMoreData says if there is any more data queued at *any* offset.
|
|
func (s *frameSorter) HasMoreData() bool {
|
|
return len(s.queue) > 0
|
|
}
|