Add out-of-order segment reassembly (#148)

* feat(tcp): add out-of-order segment reassembly

Add an opt-in, bounded out-of-order reassembly buffer so a single lost
segment can be recovered by retransmitting the gap while later segments
are held and delivered once the gap fills.

The receiver also subtracts buffered out-of-order bytes from the
advertised receive window and avoids challenge-ACK aborts for in-window
future data. Reassembly is disabled by default.

Generated with LLM assistance.

Signed-off-by: Marvin Drees <marvin.drees@9elements.com>

* implement review feedback around rx buffer reuse

Signed-off-by: Marvin Drees <marvin.drees@9elements.com>

---------

Signed-off-by: Marvin Drees <marvin.drees@9elements.com>
This commit is contained in:
Marvin Drees
2026-07-10 15:36:15 +02:00
committed by GitHub
parent 3c1f0e0281
commit ab1a0c735a
7 changed files with 629 additions and 7 deletions
+53
View File
@@ -13,6 +13,7 @@ import (
var (
ErrRingBufferFull = lneto.ErrBufferFull
errRingNoData = errors.New("lneto/ring: empty write")
errInvalidCommit = errors.New("lneto/ring: invalid commit amount")
errInvalidDiscard = errors.New("lneto/ring: invalid discard amount")
errDiscardExceeds = errors.New("lneto/ring: discard exceeds length")
errOffsetOverflow = errors.New("lneto/ring: offset too large (32 bit overflow)")
@@ -92,6 +93,58 @@ func (r *Ring) Write(b []byte) (int, error) {
return n, nil
}
// writeStart returns the buffer index where the next [Ring.Write] or
// [Ring.Commit] begins, matching [Ring.Write]'s placement (including wrap).
func (r *Ring) writeStart() int {
if r.End == 0 {
return r.Off // Empty: writing begins at Off.
}
if r.End == len(r.Buf) {
return 0 // Tail full: next byte wraps to the start.
}
return r.End
}
// PeekWrite stages b offset bytes past the write position (see
// [Ring.writeStart]) without advancing it, so the bytes are not yet readable; a
// later [Ring.Commit] reveals them. It reports false, writing nothing, when
// offset is negative or offset+len(b) exceeds [Ring.Free]. Used to place
// out-of-order data ahead of a gap that a normal Write later fills.
func (r *Ring) PeekWrite(b []byte, offset int) bool {
if offset < 0 || offset+len(b) > r.Free() {
return false
}
off := r.writeStart() + offset
if off >= len(r.Buf) {
off -= len(r.Buf)
}
n := copy(r.Buf[off:], b)
if n < len(b) {
copy(r.Buf, b[n:])
}
return true
}
// Commit advances the write pointer by n bytes, making readable any bytes
// previously staged with [Ring.PeekWrite]. It copies nothing and errors if n is
// not positive or exceeds [Ring.Free].
func (r *Ring) Commit(n int) error {
if n <= 0 {
return errInvalidCommit
} else if n > r.Free() {
return ErrRingBufferFull
}
if r.End == 0 {
r.End = r.Off // Match Write: commit begins at Off when empty.
}
end := r.End + n
if end > len(r.Buf) {
end -= len(r.Buf)
}
r.End = end // Never 0 here: end==len(Buf) is kept.
return nil
}
// ReadDiscard is a performance auxiliary method that performs a dummy read or no-op read
// for advancing the read pointer n bytes without actually copying data.
// This method panics if amount of bytes is more than buffered (see [Ring.Buffered]).