Repository navigation
Add IPAddr#to_hex - #126
Conversation
@datibbaw I think
|
Thanks @taketo1113. The |
taketo1113
left a comment
There was a problem hiding this comment.
So perhaps exposing
#_reversecould be a consideration? Or have ato_dottedwhich implements%{i}?
@datibbaw I agree that if we make IPAddr#_reverse a public method, it would be better to give it a different name.
However, I would not accept the name to_dotted at this point. There have been several proposals in the IETF in the past to represent IPv6 addresses using dotted notation, so I think we should choose the name carefully, considering that dotted notation might be used for a different purpose in the future.
I think adding IPAddr#to_hex and using it in hton and _reverse would be fine. So, how about adding only to_hex in this Pull Request, without implementing the RFC 7208 %{i} macro?
Returns the hexadecimal representation of the address value, right adjusted to the address size (8 characters for IPv4, 32 for IPv6).
Derp, thanks for spotting that. Removed! |
|
@datibbaw Merged. Thank you! |
|
As a follow-up, I benchmarked the changes and confirmed significant performance improvements for both Benchmark results$ ruby --yjit benchmark_to_hex.rb
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +YJIT +PRISM [arm64-darwin25]
# IPAddr#hton
Rehearsal ---------------------------------------------------
IPAddr#hton_old 0.944692 0.007539 0.952231 ( 0.954063)
IPAddr#hton_new 0.599881 0.005763 0.605644 ( 0.605648)
------------------------------------------ total: 1.557875sec
user system total real
IPAddr#hton_old 0.928315 0.009167 0.937482 ( 0.937473)
IPAddr#hton_new 0.580288 0.005752 0.586040 ( 0.586102)
# IPAddr#reverse
Rehearsal ------------------------------------------------------
IPAddr#reverse_old 6.996867 0.030497 7.027364 ( 7.027258)
IPAddr#reverse_new 2.219429 0.013321 2.232750 ( 2.232718)
--------------------------------------------- total: 9.260114sec
user system total real
IPAddr#reverse_old 7.132512 0.034575 7.167087 ( 7.167466)
IPAddr#reverse_new 2.173082 0.009145 2.182227 ( 2.182194)Benchmark coderequire "./lib/ipaddr"
require "benchmark"
puts RUBY_DESCRIPTION
puts
ipv4 = IPAddr.new("192.0.2.1")
ipv6 = IPAddr.new("2001:db8::1")
n = 1_000_000
puts "# IPAddr#hton"
Benchmark.bmbm do |x|
x.report("IPAddr#hton_old") do
n.times { ipv4.hton_old }
n.times { ipv6.hton_old }
end
x.report("IPAddr#hton_new") do
n.times { ipv4.hton_new }
n.times { ipv6.hton_new }
end
end
puts
puts "# IPAddr#reverse"
Benchmark.bmbm do |x|
x.report("IPAddr#reverse_old") do
n.times { ipv4.reverse_old }
n.times { ipv6.reverse_old }
end
x.report("IPAddr#reverse_new") do
n.times { ipv4.reverse_new }
n.times { ipv6.reverse_new }
end
end# lib/ipaddr.rb
def hton_old
case @family
when Socket::AF_INET
return [@addr].pack('N')
when Socket::AF_INET6
return (0..7).map { |i|
(@addr >> (112 - 16 * i)) & 0xffff
}.pack('n8')
else
raise AddressFamilyError, "unsupported address family"
end
end
def hton_new
case @family
when Socket::AF_INET, Socket::AF_INET6
value = to_hex
return [value].pack("H#{value.length}")
else
raise AddressFamilyError, "unsupported address family"
end
end
...
def reverse_old
case @family
when Socket::AF_INET
return _reverse_old + ".in-addr.arpa"
when Socket::AF_INET6
return ip6_arpa_old
else
raise AddressFamilyError, "unsupported address family"
end
end
def reverse_new
case @family
when Socket::AF_INET
return _reverse_new + ".in-addr.arpa"
when Socket::AF_INET6
return ip6_arpa_new
else
raise AddressFamilyError, "unsupported address family"
end
end
def ip6_arpa_old
if !ipv6?
raise InvalidAddressError, "not an IPv6 address: #{to_s}"
end
return _reverse_old + ".ip6.arpa"
end
def ip6_arpa_new
if !ipv6?
raise InvalidAddressError, "not an IPv6 address: #{to_s}"
end
return _reverse_new + ".ip6.arpa"
end
...
def _reverse_old
case @family
when Socket::AF_INET
return (0..3).map { |i|
(@addr >> (8 * i)) & 0xff
}.join('.')
when Socket::AF_INET6
return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
else
raise AddressFamilyError, "unsupported address family"
end
end
def _reverse_new
case @family
when Socket::AF_INET
return (0..3).map { |i|
(@addr >> (8 * i)) & 0xff
}.join('.')
when Socket::AF_INET6
return to_hex.reverse.chars.join('.')
else
raise AddressFamilyError, "unsupported address family"
end
end |
Adds
IPAddr#to_hex, which returns the hexadecimal representation of the address value, right adjusted to the address size — 8 characters for IPv4, 32 for IPv6:Motivation: consumers that need the padded hex form of an address — most notably SPF macro expansions (RFC 7208
%{ir}), but also DNS reverse-name construction and protocol encodings — currently have to reimplement the family-dependent padding themselves:Having this in the library would help reduce the external branching in other projects.
I decided to make this raise an
AddressFamilyErrordespite it being impossible to create an instance of a family type outside of IPv4 or IPv6.