Skip to content

Commit d79c17c

Browse files
committed
Add refactored GCC implementation
1 parent 5e02561 commit d79c17c

21 files changed

+1698
-0
lines changed

gcc/acknowledgment.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-FileCopyrightText: 2025 The Pion community <https://pion.ly>
2+
// SPDX-License-Identifier: MIT
3+
4+
package gcc
5+
6+
import (
7+
"fmt"
8+
"time"
9+
)
10+
11+
// ECN represents the ECN bits of an IP packet header.
12+
type ECN uint8
13+
14+
const (
15+
// ECNNonECT signals Non ECN-Capable Transport, Non-ECT.
16+
// nolint:misspell
17+
ECNNonECT ECN = iota // 00
18+
19+
// ECNECT1 signals ECN Capable Transport, ECT(0).
20+
// nolint:misspell
21+
ECNECT1 // 01
22+
23+
// ECNECT0 signals ECN Capable Transport, ECT(1).
24+
// nolint:misspell
25+
ECNECT0 // 10
26+
27+
// ECNCE signals ECN Congestion Encountered, CE.
28+
// nolint:misspell
29+
ECNCE // 11
30+
)
31+
32+
// An Acknowledgment stores send and receive information about a packet.
33+
type Acknowledgment struct {
34+
SeqNr uint64
35+
Size uint16
36+
Departure time.Time
37+
Arrived bool
38+
Arrival time.Time
39+
ECN ECN
40+
}
41+
42+
func (a Acknowledgment) String() string {
43+
return fmt.Sprintf("seq=%v, departure=%v, arrival=%v", a.SeqNr, a.Departure, a.Arrival)
44+
}

gcc/arrival_group_accumulator.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-FileCopyrightText: 2025 The Pion community <https://pion.ly>
2+
// SPDX-License-Identifier: MIT
3+
4+
package gcc
5+
6+
import (
7+
"time"
8+
)
9+
10+
type arrivalGroup []Acknowledgment
11+
12+
type arrivalGroupAccumulator struct {
13+
next arrivalGroup
14+
burstInterval time.Duration
15+
maxBurstDuration time.Duration
16+
}
17+
18+
func newArrivalGroupAccumulator() *arrivalGroupAccumulator {
19+
return &arrivalGroupAccumulator{
20+
next: make([]Acknowledgment, 0),
21+
burstInterval: 5 * time.Millisecond,
22+
maxBurstDuration: 100 * time.Millisecond,
23+
}
24+
}
25+
26+
func (a *arrivalGroupAccumulator) onPacketAcked(ack Acknowledgment) arrivalGroup {
27+
if len(a.next) == 0 {
28+
a.next = append(a.next, ack)
29+
30+
return nil
31+
}
32+
33+
sendTimeDelta := ack.Departure.Sub(a.next[0].Departure)
34+
if sendTimeDelta < a.burstInterval {
35+
a.next = append(a.next, ack)
36+
37+
return nil
38+
}
39+
40+
arrivalTimeDeltaLast := ack.Arrival.Sub(a.next[len(a.next)-1].Arrival)
41+
arrivalTimeDeltaFirst := ack.Arrival.Sub(a.next[0].Arrival)
42+
propagationDelta := arrivalTimeDeltaFirst - sendTimeDelta
43+
44+
if propagationDelta < 0 && arrivalTimeDeltaLast <= a.burstInterval && arrivalTimeDeltaFirst < a.maxBurstDuration {
45+
a.next = append(a.next, ack)
46+
47+
return nil
48+
}
49+
50+
group := make(arrivalGroup, len(a.next))
51+
copy(group, a.next)
52+
a.next = arrivalGroup{ack}
53+
54+
return group
55+
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// SPDX-FileCopyrightText: 2025 The Pion community <https://pion.ly>
2+
// SPDX-License-Identifier: MIT
3+
4+
package gcc
5+
6+
import (
7+
"testing"
8+
"time"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestArrivalGroupAccumulator(t *testing.T) {
14+
triggerNewGroupElement := Acknowledgment{
15+
Departure: time.Time{}.Add(time.Second),
16+
Arrival: time.Time{}.Add(time.Second),
17+
}
18+
cases := []struct {
19+
name string
20+
log []Acknowledgment
21+
exp []arrivalGroup
22+
}{
23+
{
24+
name: "emptyCreatesNoGroups",
25+
log: []Acknowledgment{},
26+
exp: []arrivalGroup{},
27+
},
28+
{
29+
name: "createsSingleElementGroup",
30+
log: []Acknowledgment{
31+
{
32+
Departure: time.Time{},
33+
Arrival: time.Time{}.Add(time.Millisecond),
34+
},
35+
triggerNewGroupElement,
36+
},
37+
exp: []arrivalGroup{
38+
{
39+
{
40+
Departure: time.Time{},
41+
Arrival: time.Time{}.Add(time.Millisecond),
42+
},
43+
},
44+
},
45+
},
46+
{
47+
name: "createsTwoElementGroup",
48+
log: []Acknowledgment{
49+
{
50+
Departure: time.Time{},
51+
Arrival: time.Time{}.Add(15 * time.Millisecond),
52+
},
53+
{
54+
Departure: time.Time{}.Add(3 * time.Millisecond),
55+
Arrival: time.Time{}.Add(20 * time.Millisecond),
56+
},
57+
triggerNewGroupElement,
58+
},
59+
exp: []arrivalGroup{{
60+
{
61+
Departure: time.Time{},
62+
Arrival: time.Time{}.Add(15 * time.Millisecond),
63+
},
64+
{
65+
Departure: time.Time{}.Add(3 * time.Millisecond),
66+
Arrival: time.Time{}.Add(20 * time.Millisecond),
67+
},
68+
}},
69+
},
70+
{
71+
name: "createsTwoArrivalGroups1",
72+
log: []Acknowledgment{
73+
{
74+
Departure: time.Time{},
75+
Arrival: time.Time{}.Add(15 * time.Millisecond),
76+
},
77+
{
78+
Departure: time.Time{}.Add(3 * time.Millisecond),
79+
Arrival: time.Time{}.Add(20 * time.Millisecond),
80+
},
81+
{
82+
Departure: time.Time{}.Add(9 * time.Millisecond),
83+
Arrival: time.Time{}.Add(24 * time.Millisecond),
84+
},
85+
triggerNewGroupElement,
86+
},
87+
exp: []arrivalGroup{
88+
{
89+
{
90+
Departure: time.Time{},
91+
Arrival: time.Time{}.Add(15 * time.Millisecond),
92+
},
93+
{
94+
Departure: time.Time{}.Add(3 * time.Millisecond),
95+
Arrival: time.Time{}.Add(20 * time.Millisecond),
96+
},
97+
},
98+
{
99+
{
100+
Departure: time.Time{}.Add(9 * time.Millisecond),
101+
Arrival: time.Time{}.Add(24 * time.Millisecond),
102+
},
103+
},
104+
},
105+
},
106+
{
107+
name: "ignoresOutOfOrderPackets",
108+
log: []Acknowledgment{
109+
{
110+
Departure: time.Time{},
111+
Arrival: time.Time{}.Add(15 * time.Millisecond),
112+
},
113+
{
114+
Departure: time.Time{}.Add(6 * time.Millisecond),
115+
Arrival: time.Time{}.Add(34 * time.Millisecond),
116+
},
117+
{
118+
Departure: time.Time{}.Add(8 * time.Millisecond),
119+
Arrival: time.Time{}.Add(30 * time.Millisecond),
120+
},
121+
triggerNewGroupElement,
122+
},
123+
exp: []arrivalGroup{
124+
{
125+
{
126+
Departure: time.Time{},
127+
Arrival: time.Time{}.Add(15 * time.Millisecond),
128+
},
129+
},
130+
{
131+
{
132+
Departure: time.Time{}.Add(6 * time.Millisecond),
133+
Arrival: time.Time{}.Add(34 * time.Millisecond),
134+
},
135+
{
136+
Departure: time.Time{}.Add(8 * time.Millisecond),
137+
Arrival: time.Time{}.Add(30 * time.Millisecond),
138+
},
139+
},
140+
},
141+
},
142+
{
143+
name: "newGroupBecauseOfInterDepartureTime",
144+
log: []Acknowledgment{
145+
{
146+
SeqNr: 0,
147+
Departure: time.Time{},
148+
Arrival: time.Time{}.Add(4 * time.Millisecond),
149+
},
150+
{
151+
SeqNr: 1,
152+
Departure: time.Time{}.Add(3 * time.Millisecond),
153+
Arrival: time.Time{}.Add(4 * time.Millisecond),
154+
},
155+
{
156+
SeqNr: 2,
157+
Departure: time.Time{}.Add(6 * time.Millisecond),
158+
Arrival: time.Time{}.Add(10 * time.Millisecond),
159+
},
160+
{
161+
SeqNr: 3,
162+
Departure: time.Time{}.Add(9 * time.Millisecond),
163+
Arrival: time.Time{}.Add(10 * time.Millisecond),
164+
},
165+
triggerNewGroupElement,
166+
},
167+
exp: []arrivalGroup{
168+
{
169+
{
170+
SeqNr: 0,
171+
Departure: time.Time{},
172+
Arrival: time.Time{}.Add(4 * time.Millisecond),
173+
},
174+
{
175+
SeqNr: 1,
176+
Departure: time.Time{}.Add(3 * time.Millisecond),
177+
Arrival: time.Time{}.Add(4 * time.Millisecond),
178+
},
179+
},
180+
{
181+
{
182+
SeqNr: 2,
183+
Departure: time.Time{}.Add(6 * time.Millisecond),
184+
Arrival: time.Time{}.Add(10 * time.Millisecond),
185+
},
186+
{
187+
SeqNr: 3,
188+
Departure: time.Time{}.Add(9 * time.Millisecond),
189+
Arrival: time.Time{}.Add(10 * time.Millisecond),
190+
},
191+
},
192+
},
193+
},
194+
}
195+
196+
for _, tc := range cases {
197+
tc := tc
198+
t.Run(tc.name, func(t *testing.T) {
199+
aga := newArrivalGroupAccumulator()
200+
received := []arrivalGroup{}
201+
for _, ack := range tc.log {
202+
next := aga.onPacketAcked(ack)
203+
if next != nil {
204+
received = append(received, next)
205+
}
206+
}
207+
assert.Equal(t, tc.exp, received)
208+
})
209+
}
210+
}

0 commit comments

Comments
 (0)