// IsSingleIP reports whether p contains exactly one IP.
func (p Prefix) IsSingleIP() bool { return p.IsValid() && p.Bits() == p.ip.BitLen() }
-// compare returns an integer comparing two prefixes.
+// Compare returns an integer comparing two prefixes.
// The result will be 0 if p == p2, -1 if p < p2, and +1 if p > p2.
// Prefixes sort first by validity (invalid before valid), then
-// address family (IPv4 before IPv6), then prefix length, then
-// address.
-//
-// Unexported for Go 1.22 because we may want to compare by p.Addr first.
-// See post-acceptance discussion on go.dev/issue/61642.
-func (p Prefix) compare(p2 Prefix) int {
- if c := cmp.Compare(p.Addr().BitLen(), p2.Addr().BitLen()); c != 0 {
+// address family (IPv4 before IPv6), then masked prefix address, then
+// prefix length, then unmasked address.
+func (p Prefix) Compare(p2 Prefix) int {
+ // Aside from sorting based on the masked address, this use of
+ // Addr.Compare also enforces the valid vs. invalid and address
+ // family ordering for the prefix.
+ if c := p.Masked().Addr().Compare(p2.Masked().Addr()); c != 0 {
return c
}
+
if c := cmp.Compare(p.Bits(), p2.Bits()); c != 0 {
return c
}
+
return p.Addr().Compare(p2.Addr())
}
{mustPrefix("fe80::/48"), mustPrefix("fe80::/64"), -1},
{mustPrefix("1.2.3.0/24"), mustPrefix("fe80::/8"), -1},
+
+ {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.4/24"), -1},
+ {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.0/28"), -1},
}
for _, tt := range tests {
got := tt.a.Compare(tt.b)
Prefix{},
mustPrefix("fe80::/48"),
mustPrefix("1.2.0.0/24"),
+ mustPrefix("1.2.3.4/24"),
+ mustPrefix("1.2.3.0/28"),
}
slices.SortFunc(values, Prefix.Compare)
got := fmt.Sprintf("%s", values)
- want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 fe80::/48 fe80::/64 fe90::/64]`
+ want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 1.2.3.4/24 1.2.3.0/28 fe80::/48 fe80::/64 fe90::/64]`
+ if got != want {
+ t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
+ }
+
+ // Lists from
+ // https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml and
+ // https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml,
+ // to verify that the sort order matches IANA's conventional
+ // ordering.
+ values = []Prefix{
+ mustPrefix("0.0.0.0/8"),
+ mustPrefix("127.0.0.0/8"),
+ mustPrefix("10.0.0.0/8"),
+ mustPrefix("203.0.113.0/24"),
+ mustPrefix("169.254.0.0/16"),
+ mustPrefix("192.0.0.0/24"),
+ mustPrefix("240.0.0.0/4"),
+ mustPrefix("192.0.2.0/24"),
+ mustPrefix("192.0.0.170/32"),
+ mustPrefix("198.18.0.0/15"),
+ mustPrefix("192.0.0.8/32"),
+ mustPrefix("0.0.0.0/32"),
+ mustPrefix("192.0.0.9/32"),
+ mustPrefix("198.51.100.0/24"),
+ mustPrefix("192.168.0.0/16"),
+ mustPrefix("192.0.0.10/32"),
+ mustPrefix("192.175.48.0/24"),
+ mustPrefix("192.52.193.0/24"),
+ mustPrefix("100.64.0.0/10"),
+ mustPrefix("255.255.255.255/32"),
+ mustPrefix("192.31.196.0/24"),
+ mustPrefix("172.16.0.0/12"),
+ mustPrefix("192.0.0.0/29"),
+ mustPrefix("192.88.99.0/24"),
+ mustPrefix("fec0::/10"),
+ mustPrefix("6000::/3"),
+ mustPrefix("fe00::/9"),
+ mustPrefix("8000::/3"),
+ mustPrefix("0000::/8"),
+ mustPrefix("0400::/6"),
+ mustPrefix("f800::/6"),
+ mustPrefix("e000::/4"),
+ mustPrefix("ff00::/8"),
+ mustPrefix("a000::/3"),
+ mustPrefix("fc00::/7"),
+ mustPrefix("1000::/4"),
+ mustPrefix("0800::/5"),
+ mustPrefix("4000::/3"),
+ mustPrefix("0100::/8"),
+ mustPrefix("c000::/3"),
+ mustPrefix("fe80::/10"),
+ mustPrefix("0200::/7"),
+ mustPrefix("f000::/5"),
+ mustPrefix("2000::/3"),
+ }
+ slices.SortFunc(values, func(a, b Prefix) int { return a.Compare(b) })
+ got = fmt.Sprintf("%s", values)
+ want = `[0.0.0.0/8 0.0.0.0/32 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.0.8/32 192.0.0.9/32 192.0.0.10/32 192.0.0.170/32 192.0.2.0/24 192.31.196.0/24 192.52.193.0/24 192.88.99.0/24 192.168.0.0/16 192.175.48.0/24 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 240.0.0.0/4 255.255.255.255/32 ::/8 100::/8 200::/7 400::/6 800::/5 1000::/4 2000::/3 4000::/3 6000::/3 8000::/3 a000::/3 c000::/3 e000::/4 f000::/5 f800::/6 fc00::/7 fe00::/9 fe80::/10 fec0::/10 ff00::/8]`
if got != want {
t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
}