chore(go.d/pkg/iprange): add iterator (#19688)
Ilya Mashchenko committed
Feb 21, 2025 at 12:51 UTC
a6d9848ea134180656d4b0f16b3165597174308e
4 files changed
+107
src/go/plugin/go.d/pkg/iprange/README.md
+1
@@ -11,6 +11,7 @@ type Range interface {
11
Family() Family
12
Contains(ip net.IP) bool
13
Size() *big.Int
14
+ Iterate() iter.Seq[net.IP]
15
fmt.Stringer
16
}
17
```
src/go/plugin/go.d/pkg/iprange/iterator.go
new
+41
@@ -0,0 +1,41 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package iprange
4
+
5
+import (
6
+ "iter"
7
+ "net"
8
+)
9
+
10
+func iterate(r Range) iter.Seq[net.IP] {
11
+ ipCopy := make(net.IP, len(r.getStart()))
12
+ nextBuf := make(net.IP, len(r.getStart()))
13
+
14
+ return func(yield func(net.IP) bool) {
15
+ for ip := r.getStart(); ip != nil; ip = nextIP(ip, nextBuf) {
16
+ copy(ipCopy, ip)
17
+ if !yield(ipCopy) {
18
+ return
19
+ }
20
+ if ip.Equal(r.getEnd()) {
21
+ break
22
+ }
23
+ }
24
+ }
25
+}
26
+
27
+func nextIP(ip net.IP, buf net.IP) net.IP {
28
+ ip = ip.To16()
29
+ if ip == nil {
30
+ return nil
31
+ }
32
+ copy(buf, ip)
33
+
34
+ for i := len(buf) - 1; i >= 0; i-- {
35
+ buf[i]++
36
+ if buf[i] != 0 {
37
+ break
38
+ }
39
+ }
40
+ return buf
41
+}
src/go/plugin/go.d/pkg/iprange/range.go
+19
@@ -5,6 +5,7 @@ package iprange
5
import (
6
"bytes"
7
"fmt"
8
+ "iter"
9
"math/big"
10
"net"
11
)
@@ -24,7 +25,11 @@ type Range interface {
25
Family() Family
26
Contains(ip net.IP) bool
27
Size() *big.Int
28
+ Iterate() iter.Seq[net.IP]
29
fmt.Stringer
30
+
31
+ getStart() net.IP
32
+ getEnd() net.IP
33
}
34
35
// New returns new IP Range.
@@ -45,6 +50,13 @@ type v4Range struct {
50
end net.IP
51
}
52
53
+func (r v4Range) getStart() net.IP { return r.start }
54
+func (r v4Range) getEnd() net.IP { return r.end }
55
+
56
+func (r v4Range) Iterate() iter.Seq[net.IP] {
57
+ return iterate(r)
58
+}
59
+
60
// String returns the string form of the range.
61
func (r v4Range) String() string {
62
return fmt.Sprintf("%s-%s", r.start, r.end)
@@ -70,6 +82,13 @@ type v6Range struct {
82
end net.IP
83
}
84
85
+func (r v6Range) getStart() net.IP { return r.start }
86
+func (r v6Range) getEnd() net.IP { return r.end }
87
+
88
+func (r v6Range) Iterate() iter.Seq[net.IP] {
89
+ return iterate(r)
90
+}
91
+
92
// String returns the string form of the range.
93
func (r v6Range) String() string {
94
return fmt.Sprintf("%s-%s", r.start, r.end)
src/go/plugin/go.d/pkg/iprange/range_test.go
+46
@@ -108,6 +108,29 @@ func TestV4Range_Contains(t *testing.T) {
108
}
109
}
110
111
+func TestV4Range_Iterate(t *testing.T) {
112
+ tests := map[string]struct {
113
+ input string
114
+ }{
115
+ "Single IP": {input: "192.0.2.0"},
116
+ "IP range": {input: "192.0.2.0-192.0.2.10"},
117
+ "IP CIDR": {input: "192.0.2.0/24"},
118
+ }
119
+
120
+ for name, test := range tests {
121
+ t.Run(name, func(t *testing.T) {
122
+ r, err := ParseRange(test.input)
123
+ require.NoError(t, err)
124
+
125
+ var n int64
126
+ for range r.Iterate() {
127
+ n++
128
+ }
129
+ assert.Equal(t, r.Size().Int64(), n)
130
+ })
131
+ }
132
+}
133
+
134
func TestV6Range_String(t *testing.T) {
135
tests := map[string]struct {
136
input string
@@ -198,3 +221,26 @@ func TestV6Range_Contains(t *testing.T) {
221
})
222
}
223
}
224
+
225
+func TestV6Range_Iterate(t *testing.T) {
226
+ tests := map[string]struct {
227
+ input string
228
+ }{
229
+ "Single IP": {input: "2001:db8::5"},
230
+ "IP range": {input: "2001:db8::-2001:db8::10"},
231
+ "IP CIDR": {input: "2001:db8::/124"},
232
+ }
233
+
234
+ for name, test := range tests {
235
+ t.Run(name, func(t *testing.T) {
236
+ r, err := ParseRange(test.input)
237
+ require.NoError(t, err)
238
+
239
+ var n int64
240
+ for range r.Iterate() {
241
+ n++
242
+ }
243
+ assert.Equal(t, r.Size().Int64(), n)
244
+ })
245
+ }
246
+}