fix tests not passing for RingTx

This commit is contained in:
soypat
2025-01-24 21:46:39 -03:00
parent 5e54c8ef78
commit 83719ffe98
3 changed files with 155 additions and 82 deletions
+17 -9
View File
@@ -1,11 +1,13 @@
package lneto package lneto_test
import ( import (
"bytes" "bytes"
"math/rand" "math/rand"
"testing" "testing"
"github.com/soypat/lneto"
"github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/internal/ltesto"
"github.com/soypat/lneto/tcp"
) )
func TestTCPMarshalUnmarshal(t *testing.T) { func TestTCPMarshalUnmarshal(t *testing.T) {
@@ -16,7 +18,13 @@ func TestTCPMarshalUnmarshal(t *testing.T) {
src := make([]byte, maxSize) src := make([]byte, maxSize)
dst := make([]byte, maxSize) dst := make([]byte, maxSize)
for i := 0; i < 512; i++ { for i := 0; i < 512; i++ {
src = gen.AppendRandomIPv4TCPPacket(src[:0], rng) src = gen.AppendRandomIPv4TCPPacket(src[:0], rng, tcp.Segment{
SEQ: tcp.Value(rng.Int()),
ACK: tcp.Value(rng.Int()),
DATALEN: tcp.Size(rng.Intn(256)),
WND: tcp.Size(rng.Intn(1024)),
Flags: tcp.FlagACK,
})
dst = dst[:len(src)] dst = dst[:len(src)]
testMoveTCPPacket(t, src, dst) testMoveTCPPacket(t, src, dst)
if !bytes.Equal(src, dst) { if !bytes.Equal(src, dst) {
@@ -29,31 +37,31 @@ func testMoveTCPPacket(t *testing.T, src, dst []byte) {
if len(src) != len(dst) { if len(src) != len(dst) {
panic("expect src and dst same length") panic("expect src and dst same length")
} }
efrm, err := NewEthFrame(src) efrm, err := lneto.NewEthFrame(src)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
epl := efrm.Payload() epl := efrm.Payload()
ifrm, err := NewIPv4Frame(epl) ifrm, err := lneto.NewIPv4Frame(epl)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
ipl := ifrm.Payload() ipl := ifrm.Payload()
tfrm, err := NewTCPFrame(ipl) tfrm, err := lneto.NewTCPFrame(ipl)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
efrm2, _ := NewEthFrame(dst) efrm2, _ := lneto.NewEthFrame(dst)
*efrm2.DestinationHardwareAddr() = *efrm.DestinationHardwareAddr() *efrm2.DestinationHardwareAddr() = *efrm.DestinationHardwareAddr()
*efrm2.SourceHardwareAddr() = *efrm.SourceHardwareAddr() *efrm2.SourceHardwareAddr() = *efrm.SourceHardwareAddr()
efrm2.SetEtherType(efrm.EtherTypeOrSize()) efrm2.SetEtherType(efrm.EtherTypeOrSize())
if efrm.EtherTypeOrSize() == EtherTypeVLAN { if efrm.EtherTypeOrSize() == lneto.EtherTypeVLAN {
efrm2.SetVLANTag(efrm.VLANTag()) efrm2.SetVLANTag(efrm.VLANTag())
efrm2.SetVLANEtherType(efrm.VLANEtherType()) efrm2.SetVLANEtherType(efrm.VLANEtherType())
} }
ifrm2, _ := NewIPv4Frame(efrm2.Payload()) ifrm2, _ := lneto.NewIPv4Frame(efrm2.Payload())
ifrm2.SetVersionAndIHL(ifrm.VersionAndIHL()) ifrm2.SetVersionAndIHL(ifrm.VersionAndIHL())
ifrm2.SetToS(ifrm.ToS()) ifrm2.SetToS(ifrm.ToS())
ifrm2.SetFlags(ifrm.Flags()) ifrm2.SetFlags(ifrm.Flags())
@@ -65,7 +73,7 @@ func testMoveTCPPacket(t *testing.T, src, dst []byte) {
*ifrm2.SourceAddr() = *ifrm.SourceAddr() *ifrm2.SourceAddr() = *ifrm.SourceAddr()
*ifrm2.DestinationAddr() = *ifrm.DestinationAddr() *ifrm2.DestinationAddr() = *ifrm.DestinationAddr()
tfrm2, _ := NewTCPFrame(ifrm2.Payload()) tfrm2, _ := lneto.NewTCPFrame(ifrm2.Payload())
tfrm2.SetSourcePort(tfrm.SourcePort()) tfrm2.SetSourcePort(tfrm.SourcePort())
tfrm2.SetDestinationPort(tfrm.DestinationPort()) tfrm2.SetDestinationPort(tfrm.DestinationPort())
tfrm2.SetSeq(tfrm.Seq()) tfrm2.SetSeq(tfrm.Seq())
+101 -56
View File
@@ -7,15 +7,10 @@ import (
"github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal"
) )
func newRingTx(buf []byte, maxQueuedPackets int) *ringTx { const (
if maxQueuedPackets <= 0 || len(buf) < 2 || len(buf) < maxQueuedPackets { // this must be at least 2 for buffer to work.
panic("invalid argument to NewRingTx") minBufferSize = 2
} )
return &ringTx{
rawbuf: buf,
packets: make([]ringidx, maxQueuedPackets),
}
}
// ringTx is a ring buffer with retransmission queue functionality added. // ringTx is a ring buffer with retransmission queue functionality added.
type ringTx struct { type ringTx struct {
@@ -24,12 +19,15 @@ type ringTx struct {
// packets contains // packets contains
packets []ringidx packets []ringidx
// _firstPkt is the index of the oldest packet in the packets field. // _firstPkt is the index of the oldest packet in the packets field.
_firstPkt int // _firstPkt int
_lastPkt int // _lastPkt int
// unsentOff is the offset of start of unsent data into rawbuf. // unsentOff is the offset of start of unsent data into rawbuf.
unsentoff int unsentoff int
// unsentend is the offset of end of unsent data in rawbuf. // unsentend is the offset of end of unsent data in rawbuf.
unsentend int unsentend int
seq Value
// always empty ring.
emptyRing ringidx
} }
// ringidx represents packet data inside RingTx // ringidx represents packet data inside RingTx
@@ -45,6 +43,40 @@ type ringidx struct {
// acked bool // acked bool
} }
// Reset resets the RingTx's internal state to use buf as the main ring buffer and creates or reuses
// the packet ring buffer.
func (rx *ringTx) Reset(buf []byte, maxqueuedPackets int, seq Value) error {
if maxqueuedPackets <= 0 {
return errors.New("queued packets <=0")
} else if len(buf) < minBufferSize || len(buf) < maxqueuedPackets {
return errors.New("invalid buffer size")
}
if cap(rx.packets) < maxqueuedPackets {
rx.packets = make([]ringidx, maxqueuedPackets)
}
*rx = ringTx{
rawbuf: buf,
packets: rx.packets[:maxqueuedPackets],
seq: seq,
}
for i := range rx.packets {
rx.packets[i].markRcvd()
}
return nil
}
// ResetOrReuse is identical to a call to [ringTx.Reset] with the additional detail that
// the zero value of buf (nil) and maxQueuedPackets (0) will selectively reuse existing data buffer and/or packet index buffer.
func (rx *ringTx) ResetOrReuse(buf []byte, maxQueuedPackets int, ack Value) error {
if buf == nil {
buf = rx.rawbuf
}
if maxQueuedPackets == 0 {
maxQueuedPackets = len(rx.packets)
}
return rx.Reset(buf, maxQueuedPackets, ack)
}
// Buffered returns the amount of unsent bytes. // Buffered returns the amount of unsent bytes.
func (tx *ringTx) Buffered() int { func (tx *ringTx) Buffered() int {
r := tx.unsentRing() r := tx.unsentRing()
@@ -59,9 +91,9 @@ func (tx *ringTx) BufferedSent() int {
// Write writes data to the underlying unsent data ring buffer. // Write writes data to the underlying unsent data ring buffer.
func (tx *ringTx) Write(b []byte) (n int, err error) { func (tx *ringTx) Write(b []byte) (n int, err error) {
first := tx.packets[tx._firstPkt] first := tx.pkt(tx.firstPkt())
r := tx.unsentRing() r := tx.unsentRing()
if first.off < 0 { if !first.sent() {
// No packets in queue case. // No packets in queue case.
n, err = r.Write(b) n, err = r.Write(b)
} else { } else {
@@ -76,32 +108,26 @@ func (tx *ringTx) Write(b []byte) (n int, err error) {
// MakePacket reads from the unsent data ring buffer and generates a new packet segment. // MakePacket reads from the unsent data ring buffer and generates a new packet segment.
// It fails if the sent packet queue is full. // It fails if the sent packet queue is full.
func (tx *ringTx) MakePacket(b []byte) (int, error) { func (tx *ringTx) MakePacket(b []byte) (int, Value, error) {
nxtpkt := (tx._lastPkt + 1) % len(tx.packets) nxtpkt := tx.nextPkt()
if tx._firstPkt == nxtpkt { if tx.nextPkt() < 0 {
return 0, errors.New("packet queue full") return 0, 0, errors.New("queue full")
} }
r := tx.unsentRing() r := tx.unsentRing()
start := r.Off start := r.Off
n, err := r.Read(b) n, err := r.Read(b)
if err != nil { if err != nil {
return n, err return n, 0, err
} }
last := &tx.packets[tx._lastPkt] plen := Value(n)
rlast := tx.packetRing(tx._lastPkt) seq := tx.seq
tx.packets[nxtpkt].off = start tx.packets[nxtpkt].off = start
tx.packets[nxtpkt].end = tx.addOff(start, n) tx.packets[nxtpkt].end = tx.addOff(start, n)
tx.packets[nxtpkt].seq = last.seq + Value(rlast.Buffered()) tx.packets[nxtpkt].seq = seq + plen
tx._lastPkt = nxtpkt
tx.unsentoff = tx.addOff(tx.unsentoff, n)
return n, nil
}
// IsQueueFull returns true if the sent packet queue is full in which tx.unsentoff = tx.addOff(tx.unsentoff, n)
// case a call to ReadPacket is guaranteed to fail. tx.seq += plen
func (tx *ringTx) IsQueueFull() bool { return n, seq, nil
return tx._firstPkt == (tx._lastPkt+1)%len(tx.packets)
} }
func (tx *ringTx) packetRing(i int) internal.Ring { func (tx *ringTx) packetRing(i int) internal.Ring {
@@ -114,41 +140,29 @@ func (tx *ringTx) packetRing(i int) internal.Ring {
// RecvSegment processes an incoming segment and updates the sent packet queue // RecvSegment processes an incoming segment and updates the sent packet queue
func (tx *ringTx) RecvACK(ack Value) error { func (tx *ringTx) RecvACK(ack Value) error {
i := tx._firstPkt for i := range tx.packets {
for {
pkt := &tx.packets[i] pkt := &tx.packets[i]
if ack >= pkt.seq { if pkt.sent() && pkt.seq.LessThanEq(ack) {
// Packet was received by remote. Mark it as acked. pkt.markRcvd()
pkt.off = -1
tx._firstPkt++
continue
} }
if i == tx._lastPkt {
break
}
i = (i + 1) % len(tx.packets)
} }
return nil return nil
} }
func (tx *ringTx) unsentRing() internal.Ring { func (tx *ringTx) unsentRing() internal.Ring {
return tx.ring(tx.unsentoff, tx.unsentend) off := tx.unsentoff
} if off == tx.unsentend && off != 0 {
off--
func (tx *ringTx) freeRing() (internal.Ring, int) { }
return tx.ring(tx.unsentoff, tx.unsentend), 0 return tx.ring(off, tx.unsentend)
}
func (tx *ringTx) a() {
} }
func (tx *ringTx) sentRing() internal.Ring { func (tx *ringTx) sentRing() internal.Ring {
first := tx.packets[tx._firstPkt] first := tx.pkt(tx.firstPkt())
if first.off < 0 { if !first.sent() {
return tx.ring(0, 0) return internal.Ring{}
} }
last := tx.packets[tx._lastPkt] last := tx.pkt(tx.lastPkt())
return tx.ring(first.off, last.end) return tx.ring(first.off, last.end)
} }
@@ -165,12 +179,21 @@ func (tx *ringTx) addOff(a, b int) int {
return off return off
} }
func (tx *ringTx) pkt(i int) *ringidx {
if i == -1 {
return &tx.emptyRing
} else if i < 0 || i >= len(tx.packets) {
panic("invalid packet index")
}
return &tx.packets[i]
}
func (tx *ringTx) firstPkt() int { func (tx *ringTx) firstPkt() int {
seq := tx.packets[0].seq seq := tx.packets[0].seq
idx := -1 idx := -1
for i := 0; i < len(tx.packets); i++ { for i := 0; i < len(tx.packets); i++ {
pkt := &tx.packets[i] pkt := &tx.packets[i]
if (pkt.end != 0 || pkt.off != 0) && seq.LessThanEq(pkt.seq) { if pkt.sent() && seq.LessThanEq(pkt.seq) {
seq = pkt.seq seq = pkt.seq
idx = i idx = i
} }
@@ -183,10 +206,32 @@ func (tx *ringTx) lastPkt() int {
idx := -1 idx := -1
for i := 0; i < len(tx.packets); i++ { for i := 0; i < len(tx.packets); i++ {
pkt := &tx.packets[i] pkt := &tx.packets[i]
if (pkt.end != 0 || pkt.off != 0) && pkt.seq.LessThanEq(seq) { if pkt.sent() && pkt.seq.LessThanEq(seq) {
seq = pkt.seq seq = pkt.seq
idx = i idx = i
} }
} }
return idx return idx
} }
func (tx *ringTx) nextPkt() int {
idx := -1
for i := 0; i < len(tx.packets); i++ {
pkt := &tx.packets[i]
if !pkt.sent() {
idx = i
break
}
}
return idx
}
func (pkt *ringidx) sent() bool {
return pkt.end != 0 || pkt.off != 0
}
func (pkt *ringidx) markRcvd() {
*pkt = ringidx{}
// pkt.end = 0
// pkt.off = 0
}
+37 -17
View File
@@ -5,32 +5,52 @@ import (
"testing" "testing"
) )
func TestTxQueueWrite(t *testing.T) { func TestTxQueue_SequentialMessages(t *testing.T) {
const ( const (
bufsize = 1024 bufsize = 2
maxPkt = 3 maxPkt = 1
msg = "hello world" msg = "hello world"
startAck = 0 // this is the initial sequence number.
) )
buf := make([]byte, bufsize) buf := make([]byte, bufsize)
rtx := newRingTx(buf, maxPkt) var rtx ringTx
err := rtx.Reset(buf, maxPkt, startAck)
bufs := bytes.SplitAfter([]byte(msg), []byte("e")) if err != nil {
t.Fatal(err)
}
// msgs := bytes.SplitAfter([]byte(msg), []byte("e"))
msgs := bytes.Split([]byte(msg), []byte(""))
var data [bufsize]byte var data [bufsize]byte
for i, buf := range bufs { prevSeq := Value(startAck)
n, err := rtx.Write(buf) for i, msg := range msgs {
n, err := rtx.Write(msg)
if err != nil { if err != nil {
t.Fatalf("writing packet %d: %s", i, err) t.Fatalf("writing packet %d: %s", i, err)
} else if n != len(buf) { } else if n != len(msg) {
t.Fatalf("want %d written, got %d", len(buf), n) t.Fatalf("want %d written, got %d", len(msg), n)
} }
unsent := rtx.Buffered()
n, err = rtx.MakePacket(data[:]) if len(msg) != unsent {
t.Fatalf("want %d unsent buffered, got %d", unsent, len(msg))
}
sent := rtx.BufferedSent()
if sent > 0 {
t.Fatalf("want 0 bytes sent, got %d", sent)
}
n, seq, err := rtx.MakePacket(data[:])
if err != nil { if err != nil {
t.Fatalf("making packet %d: %s", i, err) t.Fatalf("making packet %d: %s", i, err)
} else if n != len(buf) { } else if n != len(msg) {
t.Fatalf("want %d packet read, got %d", len(buf), n) t.Fatalf("want %d packet read, got %d", len(msg), n)
} else if !bytes.Equal(buf, data[:n]) { } else if !bytes.Equal(msg, data[:n]) {
t.Fatalf("want data %q, got data read %q", buf, data[:n]) t.Fatalf("want data %q, got data read %q", msg, data[:n])
} else if seq != prevSeq {
t.Fatalf("want seq %d, got %d", prevSeq, seq)
}
prevSeq += Value(n)
err = rtx.RecvACK(prevSeq)
if err != nil {
t.Fatal(err)
} }
} }
} }