diff --git a/internal/quic/frame_debug.go b/internal/quic/frame_debug.go index 34a0039ba..93ddf5513 100644 --- a/internal/quic/frame_debug.go +++ b/internal/quic/frame_debug.go @@ -116,15 +116,15 @@ func (f debugFramePing) write(w *packetWriter) bool { // debugFrameAck is an ACK frame. type debugFrameAck struct { ackDelay time.Duration - ranges []i64range + ranges []i64range[packetNumber] } func parseDebugFrameAck(b []byte) (f debugFrameAck, n int) { f.ranges = nil _, f.ackDelay, n = consumeAckFrame(b, ackDelayExponent, func(start, end packetNumber) { - f.ranges = append(f.ranges, i64range{ - start: int64(start), - end: int64(end), + f.ranges = append(f.ranges, i64range[packetNumber]{ + start: start, + end: end, }) }) // Ranges are parsed smallest to highest; reverse ranges slice to order them high to low. @@ -144,7 +144,7 @@ func (f debugFrameAck) String() string { } func (f debugFrameAck) write(w *packetWriter) bool { - return w.appendAckFrame(rangeset(f.ranges), ackDelayExponent, f.ackDelay) + return w.appendAckFrame(rangeset[packetNumber](f.ranges), ackDelayExponent, f.ackDelay) } // debugFrameResetStream is a RESET_STREAM frame. diff --git a/internal/quic/packet_codec_test.go b/internal/quic/packet_codec_test.go index 2a2043b99..efd519b1f 100644 --- a/internal/quic/packet_codec_test.go +++ b/internal/quic/packet_codec_test.go @@ -222,7 +222,7 @@ func TestFrameEncodeDecode(t *testing.T) { s: "ACK Delay=80µs [0,16) [17,32) [48,64)", f: debugFrameAck{ ackDelay: (10 << ackDelayExponent) * time.Microsecond, - ranges: []i64range{ + ranges: []i64range[packetNumber]{ {0x00, 0x10}, {0x11, 0x20}, {0x30, 0x40}, @@ -595,7 +595,7 @@ func TestFrameDecode(t *testing.T) { desc: "ACK frame with ECN counts", want: debugFrameAck{ ackDelay: (10 << ackDelayExponent) * time.Microsecond, - ranges: []i64range{ + ranges: []i64range[packetNumber]{ {0, 1}, }, }, diff --git a/internal/quic/packet_writer.go b/internal/quic/packet_writer.go index 494105eff..bfe9af796 100644 --- a/internal/quic/packet_writer.go +++ b/internal/quic/packet_writer.go @@ -257,7 +257,7 @@ func (w *packetWriter) appendPingFrame() (added bool) { // to the peer potentially failing to receive an acknowledgement // for an older packet during a period of high packet loss or // reordering. This may result in unnecessary retransmissions. -func (w *packetWriter) appendAckFrame(seen rangeset, ackDelayExponent uint8, delay time.Duration) (added bool) { +func (w *packetWriter) appendAckFrame(seen rangeset[packetNumber], ackDelayExponent uint8, delay time.Duration) (added bool) { if len(seen) == 0 { return false } diff --git a/internal/quic/rangeset.go b/internal/quic/rangeset.go index ea331ab9e..5339c5ac5 100644 --- a/internal/quic/rangeset.go +++ b/internal/quic/rangeset.go @@ -11,27 +11,24 @@ package quic // // Rangesets are efficient for small numbers of ranges, // which is expected to be the common case. -// -// Once we're willing to drop support for pre-generics versions of Go, this can -// be made into a parameterized type to permit use with packetNumber without casts. -type rangeset []i64range +type rangeset[T ~int64] []i64range[T] -type i64range struct { - start, end int64 // [start, end) +type i64range[T ~int64] struct { + start, end T // [start, end) } // size returns the size of the range. -func (r i64range) size() int64 { +func (r i64range[T]) size() T { return r.end - r.start } // contains reports whether v is in the range. -func (r i64range) contains(v int64) bool { +func (r i64range[T]) contains(v T) bool { return r.start <= v && v < r.end } // add adds [start, end) to the set, combining it with existing ranges if necessary. -func (s *rangeset) add(start, end int64) { +func (s *rangeset[T]) add(start, end T) { if start == end { return } @@ -65,11 +62,11 @@ func (s *rangeset) add(start, end int64) { s.removeranges(i+1, j) return } - *s = append(*s, i64range{start, end}) + *s = append(*s, i64range[T]{start, end}) } // sub removes [start, end) from the set. -func (s *rangeset) sub(start, end int64) { +func (s *rangeset[T]) sub(start, end T) { removefrom, removeto := -1, -1 for i := range *s { r := &(*s)[i] @@ -106,7 +103,7 @@ func (s *rangeset) sub(start, end int64) { } // contains reports whether s contains v. -func (s rangeset) contains(v int64) bool { +func (s rangeset[T]) contains(v T) bool { for _, r := range s { if v >= r.end { continue @@ -120,7 +117,7 @@ func (s rangeset) contains(v int64) bool { } // rangeContaining returns the range containing v, or the range [0,0) if v is not in s. -func (s rangeset) rangeContaining(v int64) i64range { +func (s rangeset[T]) rangeContaining(v T) i64range[T] { for _, r := range s { if v >= r.end { continue @@ -130,11 +127,11 @@ func (s rangeset) rangeContaining(v int64) i64range { } break } - return i64range{0, 0} + return i64range[T]{0, 0} } // min returns the minimum value in the set, or 0 if empty. -func (s rangeset) min() int64 { +func (s rangeset[T]) min() T { if len(s) == 0 { return 0 } @@ -142,7 +139,7 @@ func (s rangeset) min() int64 { } // max returns the maximum value in the set, or 0 if empty. -func (s rangeset) max() int64 { +func (s rangeset[T]) max() T { if len(s) == 0 { return 0 } @@ -150,7 +147,7 @@ func (s rangeset) max() int64 { } // end returns the end of the last range in the set, or 0 if empty. -func (s rangeset) end() int64 { +func (s rangeset[T]) end() T { if len(s) == 0 { return 0 } @@ -158,7 +155,7 @@ func (s rangeset) end() int64 { } // isrange reports if the rangeset covers exactly the range [start, end). -func (s rangeset) isrange(start, end int64) bool { +func (s rangeset[T]) isrange(start, end T) bool { switch len(s) { case 0: return start == 0 && end == 0 @@ -169,7 +166,7 @@ func (s rangeset) isrange(start, end int64) bool { } // removeranges removes ranges [i,j). -func (s *rangeset) removeranges(i, j int) { +func (s *rangeset[T]) removeranges(i, j int) { if i == j { return } @@ -178,8 +175,8 @@ func (s *rangeset) removeranges(i, j int) { } // insert adds a new range at index i. -func (s *rangeset) insertrange(i int, start, end int64) { - *s = append(*s, i64range{}) +func (s *rangeset[T]) insertrange(i int, start, end T) { + *s = append(*s, i64range[T]{}) copy((*s)[i+1:], (*s)[i:]) - (*s)[i] = i64range{start, end} + (*s)[i] = i64range[T]{start, end} } diff --git a/internal/quic/rangeset_test.go b/internal/quic/rangeset_test.go index 082f9201c..308046905 100644 --- a/internal/quic/rangeset_test.go +++ b/internal/quic/rangeset_test.go @@ -13,13 +13,13 @@ import ( func TestRangeSize(t *testing.T) { for _, test := range []struct { - r i64range + r i64range[int64] want int64 }{{ - r: i64range{0, 100}, + r: i64range[int64]{0, 100}, want: 100, }, { - r: i64range{10, 20}, + r: i64range[int64]{10, 20}, want: 10, }} { if got := test.r.size(); got != test.want { @@ -29,7 +29,7 @@ func TestRangeSize(t *testing.T) { } func TestRangeContains(t *testing.T) { - r := i64range{5, 10} + r := i64range[int64]{5, 10} for _, i := range []int64{0, 4, 10, 15} { if r.contains(i) { t.Errorf("%v.contains(%v) = true, want false", r, i) @@ -45,69 +45,69 @@ func TestRangeContains(t *testing.T) { func TestRangesetAdd(t *testing.T) { for _, test := range []struct { desc string - set rangeset - add i64range - want rangeset + set rangeset[int64] + add i64range[int64] + want rangeset[int64] }{{ desc: "add to empty set", - set: rangeset{}, - add: i64range{0, 100}, - want: rangeset{{0, 100}}, + set: rangeset[int64]{}, + add: i64range[int64]{0, 100}, + want: rangeset[int64]{{0, 100}}, }, { desc: "add empty range", - set: rangeset{}, - add: i64range{100, 100}, - want: rangeset{}, + set: rangeset[int64]{}, + add: i64range[int64]{100, 100}, + want: rangeset[int64]{}, }, { desc: "append nonadjacent range", - set: rangeset{{100, 200}}, - add: i64range{300, 400}, - want: rangeset{{100, 200}, {300, 400}}, + set: rangeset[int64]{{100, 200}}, + add: i64range[int64]{300, 400}, + want: rangeset[int64]{{100, 200}, {300, 400}}, }, { desc: "prepend nonadjacent range", - set: rangeset{{100, 200}}, - add: i64range{0, 50}, - want: rangeset{{0, 50}, {100, 200}}, + set: rangeset[int64]{{100, 200}}, + add: i64range[int64]{0, 50}, + want: rangeset[int64]{{0, 50}, {100, 200}}, }, { desc: "insert nonadjacent range", - set: rangeset{{100, 200}, {500, 600}}, - add: i64range{300, 400}, - want: rangeset{{100, 200}, {300, 400}, {500, 600}}, + set: rangeset[int64]{{100, 200}, {500, 600}}, + add: i64range[int64]{300, 400}, + want: rangeset[int64]{{100, 200}, {300, 400}, {500, 600}}, }, { desc: "prepend adjacent range", - set: rangeset{{100, 200}}, - add: i64range{50, 100}, - want: rangeset{{50, 200}}, + set: rangeset[int64]{{100, 200}}, + add: i64range[int64]{50, 100}, + want: rangeset[int64]{{50, 200}}, }, { desc: "append adjacent range", - set: rangeset{{100, 200}}, - add: i64range{200, 250}, - want: rangeset{{100, 250}}, + set: rangeset[int64]{{100, 200}}, + add: i64range[int64]{200, 250}, + want: rangeset[int64]{{100, 250}}, }, { desc: "prepend overlapping range", - set: rangeset{{100, 200}}, - add: i64range{50, 150}, - want: rangeset{{50, 200}}, + set: rangeset[int64]{{100, 200}}, + add: i64range[int64]{50, 150}, + want: rangeset[int64]{{50, 200}}, }, { desc: "append overlapping range", - set: rangeset{{100, 200}}, - add: i64range{150, 250}, - want: rangeset{{100, 250}}, + set: rangeset[int64]{{100, 200}}, + add: i64range[int64]{150, 250}, + want: rangeset[int64]{{100, 250}}, }, { desc: "replace range", - set: rangeset{{100, 200}}, - add: i64range{50, 250}, - want: rangeset{{50, 250}}, + set: rangeset[int64]{{100, 200}}, + add: i64range[int64]{50, 250}, + want: rangeset[int64]{{50, 250}}, }, { desc: "prepend and combine", - set: rangeset{{100, 200}, {300, 400}, {500, 600}}, - add: i64range{50, 300}, - want: rangeset{{50, 400}, {500, 600}}, + set: rangeset[int64]{{100, 200}, {300, 400}, {500, 600}}, + add: i64range[int64]{50, 300}, + want: rangeset[int64]{{50, 400}, {500, 600}}, }, { desc: "combine several ranges", - set: rangeset{{100, 200}, {300, 400}, {500, 600}, {700, 800}, {900, 1000}}, - add: i64range{300, 850}, - want: rangeset{{100, 200}, {300, 850}, {900, 1000}}, + set: rangeset[int64]{{100, 200}, {300, 400}, {500, 600}, {700, 800}, {900, 1000}}, + add: i64range[int64]{300, 850}, + want: rangeset[int64]{{100, 200}, {300, 850}, {900, 1000}}, }} { test := test t.Run(test.desc, func(t *testing.T) { @@ -125,59 +125,59 @@ func TestRangesetAdd(t *testing.T) { func TestRangesetSub(t *testing.T) { for _, test := range []struct { desc string - set rangeset - sub i64range - want rangeset + set rangeset[int64] + sub i64range[int64] + want rangeset[int64] }{{ desc: "subtract from empty set", - set: rangeset{}, - sub: i64range{0, 100}, - want: rangeset{}, + set: rangeset[int64]{}, + sub: i64range[int64]{0, 100}, + want: rangeset[int64]{}, }, { desc: "subtract empty range", - set: rangeset{{0, 100}}, - sub: i64range{0, 0}, - want: rangeset{{0, 100}}, + set: rangeset[int64]{{0, 100}}, + sub: i64range[int64]{0, 0}, + want: rangeset[int64]{{0, 100}}, }, { desc: "subtract not present in set", - set: rangeset{{0, 100}, {200, 300}}, - sub: i64range{100, 200}, - want: rangeset{{0, 100}, {200, 300}}, + set: rangeset[int64]{{0, 100}, {200, 300}}, + sub: i64range[int64]{100, 200}, + want: rangeset[int64]{{0, 100}, {200, 300}}, }, { desc: "subtract prefix", - set: rangeset{{100, 200}}, - sub: i64range{0, 150}, - want: rangeset{{150, 200}}, + set: rangeset[int64]{{100, 200}}, + sub: i64range[int64]{0, 150}, + want: rangeset[int64]{{150, 200}}, }, { desc: "subtract suffix", - set: rangeset{{100, 200}}, - sub: i64range{150, 300}, - want: rangeset{{100, 150}}, + set: rangeset[int64]{{100, 200}}, + sub: i64range[int64]{150, 300}, + want: rangeset[int64]{{100, 150}}, }, { desc: "subtract middle", - set: rangeset{{0, 100}}, - sub: i64range{40, 60}, - want: rangeset{{0, 40}, {60, 100}}, + set: rangeset[int64]{{0, 100}}, + sub: i64range[int64]{40, 60}, + want: rangeset[int64]{{0, 40}, {60, 100}}, }, { desc: "subtract from two ranges", - set: rangeset{{0, 100}, {200, 300}}, - sub: i64range{50, 250}, - want: rangeset{{0, 50}, {250, 300}}, + set: rangeset[int64]{{0, 100}, {200, 300}}, + sub: i64range[int64]{50, 250}, + want: rangeset[int64]{{0, 50}, {250, 300}}, }, { desc: "subtract removes range", - set: rangeset{{0, 100}, {200, 300}, {400, 500}}, - sub: i64range{200, 300}, - want: rangeset{{0, 100}, {400, 500}}, + set: rangeset[int64]{{0, 100}, {200, 300}, {400, 500}}, + sub: i64range[int64]{200, 300}, + want: rangeset[int64]{{0, 100}, {400, 500}}, }, { desc: "subtract removes multiple ranges", - set: rangeset{{0, 100}, {200, 300}, {400, 500}, {600, 700}}, - sub: i64range{50, 650}, - want: rangeset{{0, 50}, {650, 700}}, + set: rangeset[int64]{{0, 100}, {200, 300}, {400, 500}, {600, 700}}, + sub: i64range[int64]{50, 650}, + want: rangeset[int64]{{0, 50}, {650, 700}}, }, { desc: "subtract only range", - set: rangeset{{0, 100}}, - sub: i64range{0, 100}, - want: rangeset{}, + set: rangeset[int64]{{0, 100}}, + sub: i64range[int64]{0, 100}, + want: rangeset[int64]{}, }} { test := test t.Run(test.desc, func(t *testing.T) { @@ -193,7 +193,7 @@ func TestRangesetSub(t *testing.T) { } func TestRangesetContains(t *testing.T) { - var s rangeset + var s rangeset[int64] s.add(10, 20) s.add(30, 40) for i := int64(0); i < 50; i++ { @@ -205,23 +205,23 @@ func TestRangesetContains(t *testing.T) { } func TestRangesetRangeContaining(t *testing.T) { - var s rangeset + var s rangeset[int64] s.add(10, 20) s.add(30, 40) for _, test := range []struct { v int64 - want i64range + want i64range[int64] }{ - {0, i64range{0, 0}}, - {9, i64range{0, 0}}, - {10, i64range{10, 20}}, - {15, i64range{10, 20}}, - {19, i64range{10, 20}}, - {20, i64range{0, 0}}, - {29, i64range{0, 0}}, - {30, i64range{30, 40}}, - {39, i64range{30, 40}}, - {40, i64range{0, 0}}, + {0, i64range[int64]{0, 0}}, + {9, i64range[int64]{0, 0}}, + {10, i64range[int64]{10, 20}}, + {15, i64range[int64]{10, 20}}, + {19, i64range[int64]{10, 20}}, + {20, i64range[int64]{0, 0}}, + {29, i64range[int64]{0, 0}}, + {30, i64range[int64]{30, 40}}, + {39, i64range[int64]{30, 40}}, + {40, i64range[int64]{0, 0}}, } { got := s.rangeContaining(test.v) if got != test.want { @@ -232,22 +232,22 @@ func TestRangesetRangeContaining(t *testing.T) { func TestRangesetLimits(t *testing.T) { for _, test := range []struct { - s rangeset + s rangeset[int64] wantMin int64 wantMax int64 wantEnd int64 }{{ - s: rangeset{}, + s: rangeset[int64]{}, wantMin: 0, wantMax: 0, wantEnd: 0, }, { - s: rangeset{{10, 20}}, + s: rangeset[int64]{{10, 20}}, wantMin: 10, wantMax: 19, wantEnd: 20, }, { - s: rangeset{{10, 20}, {30, 40}, {50, 60}}, + s: rangeset[int64]{{10, 20}, {30, 40}, {50, 60}}, wantMin: 10, wantMax: 59, wantEnd: 60, @@ -266,28 +266,28 @@ func TestRangesetLimits(t *testing.T) { func TestRangesetIsRange(t *testing.T) { for _, test := range []struct { - s rangeset - r i64range + s rangeset[int64] + r i64range[int64] want bool }{{ - s: rangeset{{0, 100}}, - r: i64range{0, 100}, + s: rangeset[int64]{{0, 100}}, + r: i64range[int64]{0, 100}, want: true, }, { - s: rangeset{{0, 100}}, - r: i64range{0, 101}, + s: rangeset[int64]{{0, 100}}, + r: i64range[int64]{0, 101}, want: false, }, { - s: rangeset{{0, 10}, {11, 100}}, - r: i64range{0, 100}, + s: rangeset[int64]{{0, 10}, {11, 100}}, + r: i64range[int64]{0, 100}, want: false, }, { - s: rangeset{}, - r: i64range{0, 0}, + s: rangeset[int64]{}, + r: i64range[int64]{0, 0}, want: true, }, { - s: rangeset{}, - r: i64range{0, 1}, + s: rangeset[int64]{}, + r: i64range[int64]{0, 1}, want: false, }} { if got := test.s.isrange(test.r.start, test.r.end); got != test.want { diff --git a/internal/quic/sent_packet_test.go b/internal/quic/sent_packet_test.go index c01a2fb33..c0b04e676 100644 --- a/internal/quic/sent_packet_test.go +++ b/internal/quic/sent_packet_test.go @@ -13,7 +13,7 @@ func TestSentPacket(t *testing.T) { byte(frameTypePing), byte(frameTypeStreamBase), uint64(1), - i64range{1 << 20, 1<<20 + 1024}, + i64range[int64]{1 << 20, 1<<20 + 1024}, } // Record sent frames. sent := newSentPacket() @@ -23,7 +23,7 @@ func TestSentPacket(t *testing.T) { sent.appendAckElicitingFrame(f) case uint64: sent.appendInt(f) - case i64range: + case i64range[int64]: sent.appendOffAndSize(f.start, int(f.size())) } } @@ -41,7 +41,7 @@ func TestSentPacket(t *testing.T) { if got := sent.nextInt(); got != want { t.Fatalf("%v: sent.nextInt() = %v, want %v", i, got, want) } - case i64range: + case i64range[int64]: if start, end := sent.nextRange(); start != want.start || end != want.end { t.Fatalf("%v: sent.nextRange() = [%v,%v), want %v", i, start, end, want) }