Skip to content

Commit c10f27f

Browse files
net: Make IPv6ToString do zero compression as described in RFC 5952
1 parent 4b5659c commit c10f27f

File tree

1 file changed

+48
-11
lines changed

1 file changed

+48
-11
lines changed

src/netaddress.cpp

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -556,20 +556,57 @@ static std::string IPv4ToString(Span<const uint8_t> a)
556556
return strprintf("%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
557557
}
558558

559+
// Return an IPv6 address text representation with zero compression as described in RFC 5952
560+
// ("A Recommendation for IPv6 Address Text Representation").
559561
static std::string IPv6ToString(Span<const uint8_t> a)
560562
{
561563
assert(a.size() == ADDR_IPV6_SIZE);
562-
// clang-format off
563-
return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
564-
ReadBE16(&a[0]),
565-
ReadBE16(&a[2]),
566-
ReadBE16(&a[4]),
567-
ReadBE16(&a[6]),
568-
ReadBE16(&a[8]),
569-
ReadBE16(&a[10]),
570-
ReadBE16(&a[12]),
571-
ReadBE16(&a[14]));
572-
// clang-format on
564+
const std::array groups{
565+
ReadBE16(&a[0]),
566+
ReadBE16(&a[2]),
567+
ReadBE16(&a[4]),
568+
ReadBE16(&a[6]),
569+
ReadBE16(&a[8]),
570+
ReadBE16(&a[10]),
571+
ReadBE16(&a[12]),
572+
ReadBE16(&a[14]),
573+
};
574+
575+
// The zero compression implementation is inspired by Rust's std::net::Ipv6Addr, see
576+
// https://github.com/rust-lang/rust/blob/cc4103089f40a163f6d143f06359cba7043da29b/library/std/src/net/ip.rs#L1635-L1683
577+
struct ZeroSpan {
578+
size_t start_index{0};
579+
size_t len{0};
580+
};
581+
582+
// Find longest sequence of consecutive all-zero fields. Use first zero sequence if two or more
583+
// zero sequences of equal length are found.
584+
ZeroSpan longest, current;
585+
for (size_t i{0}; i < groups.size(); ++i) {
586+
if (groups[i] != 0) {
587+
current = {i + 1, 0};
588+
continue;
589+
}
590+
current.len += 1;
591+
if (current.len > longest.len) {
592+
longest = current;
593+
}
594+
}
595+
596+
std::string r;
597+
r.reserve(39);
598+
for (size_t i{0}; i < groups.size(); ++i) {
599+
// Replace the longest sequence of consecutive all-zero fields with two colons ("::").
600+
if (longest.len >= 2 && i >= longest.start_index && i < longest.start_index + longest.len) {
601+
if (i == longest.start_index) {
602+
r += "::";
603+
}
604+
continue;
605+
}
606+
r += strprintf("%s%x", ((!r.empty() && r.back() != ':') ? ":" : ""), groups[i]);
607+
}
608+
609+
return r;
573610
}
574611

575612
std::string CNetAddr::ToStringIP() const

0 commit comments

Comments
 (0)