Skip to content

Commit 10d9069

Browse files
committed
quic: add rangeset type
A rangeset is an ordered list of non-overlapping int64 ranges. This type will be used for tracking which packet numbers need to be acknowledged and which parts of a stream have been sent/received. For golang/go#58547 Change-Id: Ia4ab3a47e82d0e7aea738a0f857b2129d4ea1f63 Reviewed-on: https://go-review.googlesource.com/c/net/+/478295 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Damien Neil <dneil@google.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
1 parent f71a821 commit 10d9069

File tree

2 files changed

+478
-0
lines changed

2 files changed

+478
-0
lines changed

internal/quic/rangeset.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package quic
6+
7+
// A rangeset is a set of int64s, stored as an ordered list of non-overlapping,
8+
// non-empty ranges.
9+
//
10+
// Rangesets are efficient for small numbers of ranges,
11+
// which is expected to be the common case.
12+
//
13+
// Once we're willing to drop support for pre-generics versions of Go, this can
14+
// be made into a parameterized type to permit use with packetNumber without casts.
15+
type rangeset []i64range
16+
17+
type i64range struct {
18+
start, end int64 // [start, end)
19+
}
20+
21+
// size returns the size of the range.
22+
func (r i64range) size() int64 {
23+
return r.end - r.start
24+
}
25+
26+
// contains reports whether v is in the range.
27+
func (r i64range) contains(v int64) bool {
28+
return r.start <= v && v < r.end
29+
}
30+
31+
// add adds [start, end) to the set, combining it with existing ranges if necessary.
32+
func (s *rangeset) add(start, end int64) {
33+
if start == end {
34+
return
35+
}
36+
for i := range *s {
37+
r := &(*s)[i]
38+
if r.start > end {
39+
// The new range comes before range i.
40+
s.insertrange(i, start, end)
41+
return
42+
}
43+
if start > r.end {
44+
// The new range comes after range i.
45+
continue
46+
}
47+
// The new range is adjacent to or overlapping range i.
48+
if start < r.start {
49+
r.start = start
50+
}
51+
if end <= r.end {
52+
return
53+
}
54+
// Possibly coalesce subsquent ranges into range i.
55+
r.end = end
56+
j := i + 1
57+
for ; j < len(*s) && r.end >= (*s)[j].start; j++ {
58+
if e := (*s)[j].end; e > r.end {
59+
// Range j ends after the new range.
60+
r.end = e
61+
}
62+
}
63+
s.removeranges(i+1, j)
64+
return
65+
}
66+
*s = append(*s, i64range{start, end})
67+
}
68+
69+
// sub removes [start, end) from the set.
70+
func (s *rangeset) sub(start, end int64) {
71+
removefrom, removeto := -1, -1
72+
for i := range *s {
73+
r := &(*s)[i]
74+
if end < r.start {
75+
break
76+
}
77+
if r.end < start {
78+
continue
79+
}
80+
switch {
81+
case start <= r.start && end >= r.end:
82+
// Remove the entire range.
83+
if removefrom == -1 {
84+
removefrom = i
85+
}
86+
removeto = i + 1
87+
case start <= r.start:
88+
// Remove a prefix.
89+
r.start = end
90+
case end >= r.end:
91+
// Remove a suffix.
92+
r.end = start
93+
default:
94+
// Remove the middle, leaving two new ranges.
95+
rend := r.end
96+
r.end = start
97+
s.insertrange(i+1, end, rend)
98+
return
99+
}
100+
}
101+
if removefrom != -1 {
102+
s.removeranges(removefrom, removeto)
103+
}
104+
}
105+
106+
// contains reports whether s contains v.
107+
func (s rangeset) contains(v int64) bool {
108+
for _, r := range s {
109+
if v >= r.end {
110+
continue
111+
}
112+
if r.start <= v {
113+
return true
114+
}
115+
return false
116+
}
117+
return false
118+
}
119+
120+
// rangeContaining returns the range containing v, or the range [0,0) if v is not in s.
121+
func (s rangeset) rangeContaining(v int64) i64range {
122+
for _, r := range s {
123+
if v >= r.end {
124+
continue
125+
}
126+
if r.start <= v {
127+
return r
128+
}
129+
break
130+
}
131+
return i64range{0, 0}
132+
}
133+
134+
// min returns the minimum value in the set, or 0 if empty.
135+
func (s rangeset) min() int64 {
136+
if len(s) == 0 {
137+
return 0
138+
}
139+
return s[0].start
140+
}
141+
142+
// max returns the maximum value in the set, or 0 if empty.
143+
func (s rangeset) max() int64 {
144+
if len(s) == 0 {
145+
return 0
146+
}
147+
return s[len(s)-1].end - 1
148+
}
149+
150+
// end returns the end of the last range in the set, or 0 if empty.
151+
func (s rangeset) end() int64 {
152+
if len(s) == 0 {
153+
return 0
154+
}
155+
return s[len(s)-1].end
156+
}
157+
158+
// isrange reports if the rangeset covers exactly the range [start, end).
159+
func (s rangeset) isrange(start, end int64) bool {
160+
switch len(s) {
161+
case 0:
162+
return start == 0 && end == 0
163+
case 1:
164+
return s[0].start == start && s[0].end == end
165+
}
166+
return false
167+
}
168+
169+
// removeranges removes ranges [i,j).
170+
func (s *rangeset) removeranges(i, j int) {
171+
if i == j {
172+
return
173+
}
174+
copy((*s)[i:], (*s)[j:])
175+
*s = (*s)[:len(*s)-(j-i)]
176+
}
177+
178+
// insert adds a new range at index i.
179+
func (s *rangeset) insertrange(i int, start, end int64) {
180+
*s = append(*s, i64range{})
181+
copy((*s)[i+1:], (*s)[i:])
182+
(*s)[i] = i64range{start, end}
183+
}

0 commit comments

Comments
 (0)