Skip to content

Commit 9aa147c

Browse files
committed
Add IPAddr#prefixlen
This (kind of) addresses [Feature #11210](https://bugs.ruby-lang.org/issues/11210).
1 parent 2300793 commit 9aa147c

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

lib/ipaddr.rb

+29
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,35 @@ def to_range
371371
return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
372372
end
373373

374+
# Returns the prefix length in bits for the ipaddr.
375+
def prefixlen
376+
case @family
377+
when Socket::AF_INET
378+
n = IN4MASK ^ @mask_addr
379+
i = 32
380+
when Socket::AF_INET6
381+
n = IN6MASK ^ @mask_addr
382+
i = 128
383+
else
384+
raise AddressFamilyError, "unsupported address family"
385+
end
386+
while n.positive?
387+
n >>= 1
388+
i -= 1
389+
end
390+
i
391+
end
392+
393+
# Sets the prefix length in bits.
394+
def prefixlen=(prefixlen)
395+
case prefixlen
396+
when Integer
397+
mask!(prefixlen)
398+
else
399+
raise InvalidPrefixError, "prefixlen must be an integer"
400+
end
401+
end
402+
374403
# Returns a string containing a human-readable representation of the
375404
# ipaddr. ("#<IPAddr: family:address/mask>")
376405
def inspect

test/test_ipaddr.rb

+38-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ def test_s_new
2121
assert_equal("::", a.to_s)
2222
assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
2323
assert_equal(Socket::AF_INET6, a.family)
24+
assert_equal(128, a.prefixlen)
2425

2526
a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
2627
assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
2728
assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
2829
assert_equal(Socket::AF_INET6, a.family)
30+
assert_equal(128, a.prefixlen)
2931

3032
a = IPAddr.new("3ffe:505:2::/48")
3133
assert_equal("3ffe:505:2::", a.to_s)
@@ -34,34 +36,47 @@ def test_s_new
3436
assert_equal(false, a.ipv4?)
3537
assert_equal(true, a.ipv6?)
3638
assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
39+
assert_equal(48, a.prefixlen)
3740

3841
a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
3942
assert_equal("3ffe:505:2::", a.to_s)
4043
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
4144
assert_equal(Socket::AF_INET6, a.family)
45+
assert_equal(48, a.prefixlen)
4246

4347
a = IPAddr.new("0.0.0.0")
4448
assert_equal("0.0.0.0", a.to_s)
4549
assert_equal("0.0.0.0", a.to_string)
4650
assert_equal(Socket::AF_INET, a.family)
51+
assert_equal(32, a.prefixlen)
4752

4853
a = IPAddr.new("192.168.1.2")
4954
assert_equal("192.168.1.2", a.to_s)
5055
assert_equal("192.168.1.2", a.to_string)
5156
assert_equal(Socket::AF_INET, a.family)
5257
assert_equal(true, a.ipv4?)
5358
assert_equal(false, a.ipv6?)
59+
assert_equal(32, a.prefixlen)
5460

55-
a = IPAddr.new("192.168.1.2/24")
61+
a = IPAddr.new("192.168.1.2/26")
5662
assert_equal("192.168.1.0", a.to_s)
5763
assert_equal("192.168.1.0", a.to_string)
5864
assert_equal(Socket::AF_INET, a.family)
59-
assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
65+
assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.192>", a.inspect)
66+
assert_equal(26, a.prefixlen)
6067

6168
a = IPAddr.new("192.168.1.2/255.255.255.0")
6269
assert_equal("192.168.1.0", a.to_s)
6370
assert_equal("192.168.1.0", a.to_string)
6471
assert_equal(Socket::AF_INET, a.family)
72+
assert_equal(24, a.prefixlen)
73+
74+
(0..32).each do |prefixlen|
75+
assert_equal(prefixlen, IPAddr.new("10.20.30.40/#{prefixlen}").prefixlen)
76+
end
77+
(0..128).each do |prefixlen|
78+
assert_equal(prefixlen, IPAddr.new("1:2:3:4:5:6:7:8/#{prefixlen}").prefixlen)
79+
end
6580

6681
assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
6782
assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
@@ -153,6 +168,27 @@ def test_ip6_int
153168
}
154169
end
155170

171+
def test_prefixlen_writer
172+
a = IPAddr.new("192.168.1.2")
173+
["1", "255.255.255.0", "ffff:ffff:ffff:ffff::", nil, 1.0, -1, 33].each { |x|
174+
assert_raise(IPAddr::InvalidPrefixError) { a.prefixlen = x }
175+
}
176+
a = IPAddr.new("1:2:3:4:5:6:7:8")
177+
["1", "255.255.255.0", "ffff:ffff:ffff:ffff::", nil, 1.0, -1, 129].each { |x|
178+
assert_raise(IPAddr::InvalidPrefixError) { a.prefixlen = x }
179+
}
180+
181+
a = IPAddr.new("192.168.1.2")
182+
a.prefixlen = 26
183+
assert_equal(26, a.prefixlen)
184+
assert_equal("192.168.1.0", a.to_s)
185+
186+
a = IPAddr.new("1:2:3:4:5:6:7:8")
187+
a.prefixlen = 52
188+
assert_equal(52, a.prefixlen)
189+
assert_equal("1:2:3::", a.to_s)
190+
end
191+
156192
def test_to_s
157193
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
158194
assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)

0 commit comments

Comments
 (0)