@cryptotaxi247 / netdata-1 / commits / a6d9848ea

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 +}