Source file src/net/netip/netip_test.go

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package netip_test
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"flag"
    11  	"fmt"
    12  	"internal/asan"
    13  	"internal/testenv"
    14  	"net"
    15  	. "net/netip"
    16  	"reflect"
    17  	"slices"
    18  	"strings"
    19  	"testing"
    20  	"unique"
    21  )
    22  
    23  var long = flag.Bool("long", false, "run long tests")
    24  
    25  type uint128 = Uint128
    26  
    27  var (
    28  	mustPrefix = MustParsePrefix
    29  	mustIP     = MustParseAddr
    30  	mustIPPort = MustParseAddrPort
    31  )
    32  
    33  func TestParseAddr(t *testing.T) {
    34  	var validIPs = []struct {
    35  		in      string
    36  		ip      Addr   // output of ParseAddr()
    37  		str     string // output of String(). If "", use in.
    38  		wantErr string
    39  	}{
    40  		// Basic zero IPv4 address.
    41  		{
    42  			in: "0.0.0.0",
    43  			ip: MkAddr(Mk128(0, 0xffff00000000), Z4),
    44  		},
    45  		// Basic non-zero IPv4 address.
    46  		{
    47  			in: "192.168.140.255",
    48  			ip: MkAddr(Mk128(0, 0xffffc0a88cff), Z4),
    49  		},
    50  		// IPv4 address in windows-style "print all the digits" form.
    51  		{
    52  			in:      "010.000.015.001",
    53  			wantErr: `ParseAddr("010.000.015.001"): IPv4 field has octet with leading zero`,
    54  		},
    55  		// IPv4 address with a silly amount of leading zeros.
    56  		{
    57  			in:      "000001.00000002.00000003.000000004",
    58  			wantErr: `ParseAddr("000001.00000002.00000003.000000004"): IPv4 field has octet with leading zero`,
    59  		},
    60  		// 4-in-6 with octet with leading zero
    61  		{
    62  			in:      "::ffff:1.2.03.4",
    63  			wantErr: `ParseAddr("::ffff:1.2.03.4"): IPv4 field has octet with leading zero`,
    64  		},
    65  		// 4-in-6 with octet with unexpected character
    66  		{
    67  			in:      "::ffff:1.2.3.z",
    68  			wantErr: `ParseAddr("::ffff:1.2.3.z"): unexpected character (at "z")`,
    69  		},
    70  		// Basic zero IPv6 address.
    71  		{
    72  			in: "::",
    73  			ip: MkAddr(Mk128(0, 0), Z6noz),
    74  		},
    75  		// Localhost IPv6.
    76  		{
    77  			in: "::1",
    78  			ip: MkAddr(Mk128(0, 1), Z6noz),
    79  		},
    80  		// Fully expanded IPv6 address.
    81  		{
    82  			in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b",
    83  			ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), Z6noz),
    84  		},
    85  		// IPv6 with elided fields in the middle.
    86  		{
    87  			in: "fd7a:115c::626b:430b",
    88  			ip: MkAddr(Mk128(0xfd7a115c00000000, 0x00000000626b430b), Z6noz),
    89  		},
    90  		// IPv6 with elided fields at the end.
    91  		{
    92  			in: "fd7a:115c:a1e0:ab12:4843:cd96::",
    93  			ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd9600000000), Z6noz),
    94  		},
    95  		// IPv6 with single elided field at the end.
    96  		{
    97  			in:  "fd7a:115c:a1e0:ab12:4843:cd96:626b::",
    98  			ip:  MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b0000), Z6noz),
    99  			str: "fd7a:115c:a1e0:ab12:4843:cd96:626b:0",
   100  		},
   101  		// IPv6 with single elided field in the middle.
   102  		{
   103  			in:  "fd7a:115c:a1e0::4843:cd96:626b:430b",
   104  			ip:  MkAddr(Mk128(0xfd7a115ca1e00000, 0x4843cd96626b430b), Z6noz),
   105  			str: "fd7a:115c:a1e0:0:4843:cd96:626b:430b",
   106  		},
   107  		// IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6)
   108  		{
   109  			in:  "::ffff:192.168.140.255",
   110  			ip:  MkAddr(Mk128(0, 0x0000ffffc0a88cff), Z6noz),
   111  			str: "::ffff:192.168.140.255",
   112  		},
   113  		// IPv6 with a zone specifier.
   114  		{
   115  			in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0",
   116  			ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), unique.Make(MakeAddrDetail(true, "eth0"))),
   117  		},
   118  		// IPv6 with dotted decimal and zone specifier.
   119  		{
   120  			in:  "1:2::ffff:192.168.140.255%eth1",
   121  			ip:  MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
   122  			str: "1:2::ffff:c0a8:8cff%eth1",
   123  		},
   124  		// 4-in-6 with zone
   125  		{
   126  			in:  "::ffff:192.168.140.255%eth1",
   127  			ip:  MkAddr(Mk128(0, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
   128  			str: "::ffff:192.168.140.255%eth1",
   129  		},
   130  		// IPv6 with capital letters.
   131  		{
   132  			in:  "FD9E:1A04:F01D::1",
   133  			ip:  MkAddr(Mk128(0xfd9e1a04f01d0000, 0x1), Z6noz),
   134  			str: "fd9e:1a04:f01d::1",
   135  		},
   136  	}
   137  
   138  	for _, test := range validIPs {
   139  		t.Run(test.in, func(t *testing.T) {
   140  			got, err := ParseAddr(test.in)
   141  			if err != nil {
   142  				if err.Error() == test.wantErr {
   143  					return
   144  				}
   145  				t.Fatal(err)
   146  			}
   147  			if test.wantErr != "" {
   148  				t.Fatalf("wanted error %q; got none", test.wantErr)
   149  			}
   150  			if got != test.ip {
   151  				t.Errorf("got %#v, want %#v", got, test.ip)
   152  			}
   153  
   154  			// Check that ParseAddr is a pure function.
   155  			got2, err := ParseAddr(test.in)
   156  			if err != nil {
   157  				t.Fatal(err)
   158  			}
   159  			if got != got2 {
   160  				t.Errorf("ParseAddr(%q) got 2 different results: %#v, %#v", test.in, got, got2)
   161  			}
   162  
   163  			// Check that ParseAddr(ip.String()) is the identity function.
   164  			s := got.String()
   165  			got3, err := ParseAddr(s)
   166  			if err != nil {
   167  				t.Fatal(err)
   168  			}
   169  			if got != got3 {
   170  				t.Errorf("ParseAddr(%q) != ParseAddr(ParseIP(%q).String()). Got %#v, want %#v", test.in, test.in, got3, got)
   171  			}
   172  
   173  			// Check that the slow-but-readable parser produces the same result.
   174  			slow, err := parseIPSlow(test.in)
   175  			if err != nil {
   176  				t.Fatal(err)
   177  			}
   178  			if got != slow {
   179  				t.Errorf("ParseAddr(%q) = %#v, parseIPSlow(%q) = %#v", test.in, got, test.in, slow)
   180  			}
   181  
   182  			// Check that the parsed IP formats as expected.
   183  			s = got.String()
   184  			wants := test.str
   185  			if wants == "" {
   186  				wants = test.in
   187  			}
   188  			if s != wants {
   189  				t.Errorf("ParseAddr(%q).String() got %q, want %q", test.in, s, wants)
   190  			}
   191  
   192  			// Check that AppendTo matches MarshalText.
   193  			TestAppendToMarshal(t, got)
   194  
   195  			// Check that MarshalText/UnmarshalText work similarly to
   196  			// ParseAddr/String (see TestIPMarshalUnmarshal for
   197  			// marshal-specific behavior that's not common with
   198  			// ParseAddr/String).
   199  			js := `"` + test.in + `"`
   200  			var jsgot Addr
   201  			if err := json.Unmarshal([]byte(js), &jsgot); err != nil {
   202  				t.Fatal(err)
   203  			}
   204  			if jsgot != got {
   205  				t.Errorf("json.Unmarshal(%q) = %#v, want %#v", test.in, jsgot, got)
   206  			}
   207  			jsb, err := json.Marshal(jsgot)
   208  			if err != nil {
   209  				t.Fatal(err)
   210  			}
   211  			jswant := `"` + wants + `"`
   212  			jsback := string(jsb)
   213  			if jsback != jswant {
   214  				t.Errorf("Marshal(Unmarshal(%q)) = %s, want %s", test.in, jsback, jswant)
   215  			}
   216  		})
   217  	}
   218  
   219  	var invalidIPs = []string{
   220  		// Empty string
   221  		"",
   222  		// Garbage non-IP
   223  		"bad",
   224  		// Single number. Some parsers accept this as an IPv4 address in
   225  		// big-endian uint32 form, but we don't.
   226  		"1234",
   227  		// IPv4 with a zone specifier
   228  		"1.2.3.4%eth0",
   229  		// IPv4 field must have at least one digit
   230  		".1.2.3",
   231  		"1.2.3.",
   232  		"1..2.3",
   233  		// IPv4 address too long
   234  		"1.2.3.4.5",
   235  		// IPv4 in dotted octal form
   236  		"0300.0250.0214.0377",
   237  		// IPv4 in dotted hex form
   238  		"0xc0.0xa8.0x8c.0xff",
   239  		// IPv4 in class B form
   240  		"192.168.12345",
   241  		// IPv4 in class B form, with a small enough number to be
   242  		// parseable as a regular dotted decimal field.
   243  		"127.0.1",
   244  		// IPv4 in class A form
   245  		"192.1234567",
   246  		// IPv4 in class A form, with a small enough number to be
   247  		// parseable as a regular dotted decimal field.
   248  		"127.1",
   249  		// IPv4 field has value >255
   250  		"192.168.300.1",
   251  		// IPv4 with too many fields
   252  		"192.168.0.1.5.6",
   253  		// IPv6 with not enough fields
   254  		"1:2:3:4:5:6:7",
   255  		// IPv6 with too many fields
   256  		"1:2:3:4:5:6:7:8:9",
   257  		// IPv6 with 8 fields and a :: expander
   258  		"1:2:3:4::5:6:7:8",
   259  		// IPv6 with a field bigger than 2b
   260  		"fe801::1",
   261  		// IPv6 with non-hex values in field
   262  		"fe80:tail:scal:e::",
   263  		// IPv6 with a zone delimiter but no zone.
   264  		"fe80::1%",
   265  		// IPv6 (without ellipsis) with too many fields for trailing embedded IPv4.
   266  		"ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
   267  		// IPv6 (with ellipsis) with too many fields for trailing embedded IPv4.
   268  		"ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
   269  		// IPv6 with invalid embedded IPv4.
   270  		"::ffff:192.168.140.bad",
   271  		// IPv6 with multiple ellipsis ::.
   272  		"fe80::1::1",
   273  		// IPv6 with invalid non hex/colon character.
   274  		"fe80:1?:1",
   275  		// IPv6 with truncated bytes after single colon.
   276  		"fe80:",
   277  		// IPv6 with 5 zeros in last group
   278  		"0:0:0:0:0:ffff:0:00000",
   279  		// IPv6 with 5 zeros in one group and embedded IPv4
   280  		"0:0:0:0:00000:ffff:127.1.2.3",
   281  	}
   282  
   283  	for _, s := range invalidIPs {
   284  		t.Run(s, func(t *testing.T) {
   285  			got, err := ParseAddr(s)
   286  			if err == nil {
   287  				t.Errorf("ParseAddr(%q) = %#v, want error", s, got)
   288  			}
   289  
   290  			slow, err := parseIPSlow(s)
   291  			if err == nil {
   292  				t.Errorf("parseIPSlow(%q) = %#v, want error", s, slow)
   293  			}
   294  
   295  			std := net.ParseIP(s)
   296  			if std != nil {
   297  				t.Errorf("net.ParseIP(%q) = %#v, want error", s, std)
   298  			}
   299  
   300  			if s == "" {
   301  				// Don't test unmarshaling of "" here, do it in
   302  				// IPMarshalUnmarshal.
   303  				return
   304  			}
   305  			var jsgot Addr
   306  			js := []byte(`"` + s + `"`)
   307  			if err := json.Unmarshal(js, &jsgot); err == nil {
   308  				t.Errorf("json.Unmarshal(%q) = %#v, want error", s, jsgot)
   309  			}
   310  		})
   311  	}
   312  }
   313  
   314  func TestAddrFromSlice(t *testing.T) {
   315  	tests := []struct {
   316  		ip       []byte
   317  		wantAddr Addr
   318  		wantOK   bool
   319  	}{
   320  		{
   321  			ip:       []byte{10, 0, 0, 1},
   322  			wantAddr: mustIP("10.0.0.1"),
   323  			wantOK:   true,
   324  		},
   325  		{
   326  			ip:       []byte{0xfe, 0x80, 15: 0x01},
   327  			wantAddr: mustIP("fe80::01"),
   328  			wantOK:   true,
   329  		},
   330  		{
   331  			ip:       []byte{0, 1, 2},
   332  			wantAddr: Addr{},
   333  			wantOK:   false,
   334  		},
   335  		{
   336  			ip:       nil,
   337  			wantAddr: Addr{},
   338  			wantOK:   false,
   339  		},
   340  	}
   341  	for _, tt := range tests {
   342  		addr, ok := AddrFromSlice(tt.ip)
   343  		if ok != tt.wantOK || addr != tt.wantAddr {
   344  			t.Errorf("AddrFromSlice(%#v) = %#v, %v, want %#v, %v", tt.ip, addr, ok, tt.wantAddr, tt.wantOK)
   345  		}
   346  	}
   347  }
   348  
   349  func TestIPv4Constructors(t *testing.T) {
   350  	if AddrFrom4([4]byte{1, 2, 3, 4}) != MustParseAddr("1.2.3.4") {
   351  		t.Errorf("don't match")
   352  	}
   353  }
   354  
   355  func TestAddrAppendText(t *testing.T) {
   356  	tests := []struct {
   357  		ip   Addr
   358  		want string
   359  	}{
   360  		{Addr{}, ""}, // zero IP
   361  		{mustIP("1.2.3.4"), "1.2.3.4"},
   362  		{mustIP("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
   363  		{mustIP("::ffff:192.168.140.255"), "::ffff:192.168.140.255"},
   364  		{mustIP("::ffff:192.168.140.255%en0"), "::ffff:192.168.140.255%en0"},
   365  	}
   366  	for i, tc := range tests {
   367  		ip := tc.ip
   368  
   369  		mtAppend := make([]byte, 4, 32)
   370  		mtAppend, err := ip.AppendText(mtAppend)
   371  		mtAppend = mtAppend[4:]
   372  		if err != nil {
   373  			t.Fatal(err)
   374  		}
   375  		if string(mtAppend) != tc.want {
   376  			t.Errorf("%d. for (%v) AppendText = %q; want %q", i, ip, mtAppend, tc.want)
   377  		}
   378  	}
   379  }
   380  
   381  func TestAddrMarshalUnmarshalBinary(t *testing.T) {
   382  	tests := []struct {
   383  		ip       string
   384  		wantSize int
   385  	}{
   386  		{"", 0}, // zero IP
   387  		{"1.2.3.4", 4},
   388  		{"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", 16},
   389  		{"::ffff:c000:0280", 16},
   390  		{"::ffff:c000:0280%eth0", 20},
   391  	}
   392  	for _, tc := range tests {
   393  		var ip Addr
   394  		if len(tc.ip) > 0 {
   395  			ip = mustIP(tc.ip)
   396  		}
   397  		b, err := ip.MarshalBinary()
   398  		if err != nil {
   399  			t.Fatal(err)
   400  		}
   401  		if len(b) != tc.wantSize {
   402  			t.Fatalf("%q encoded to size %d; want %d", tc.ip, len(b), tc.wantSize)
   403  		}
   404  		var ip2 Addr
   405  		if err := ip2.UnmarshalBinary(b); err != nil {
   406  			t.Fatal(err)
   407  		}
   408  		if ip != ip2 {
   409  			t.Fatalf("got %v; want %v", ip2, ip)
   410  		}
   411  
   412  		bAppend := make([]byte, 4, 32)
   413  		bAppend, err = ip.AppendBinary(bAppend)
   414  		bAppend = bAppend[4:]
   415  		if err != nil {
   416  			t.Fatal(err)
   417  		}
   418  		if len(bAppend) != tc.wantSize {
   419  			t.Fatalf("%q encoded to size %d; want %d", tc.ip, len(bAppend), tc.wantSize)
   420  		}
   421  		var ip3 Addr
   422  		if err := ip3.UnmarshalBinary(bAppend); err != nil {
   423  			t.Fatal(err)
   424  		}
   425  		if ip != ip3 {
   426  			t.Fatalf("got %v; want %v", ip3, ip)
   427  		}
   428  	}
   429  
   430  	// Cannot unmarshal from unexpected IP length.
   431  	for _, n := range []int{3, 5} {
   432  		var ip2 Addr
   433  		if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
   434  			t.Fatalf("unmarshaled from unexpected IP length %d", n)
   435  		}
   436  	}
   437  }
   438  
   439  func TestAddrPortMarshalTextString(t *testing.T) {
   440  	tests := []struct {
   441  		in   AddrPort
   442  		want string
   443  	}{
   444  		{mustIPPort("1.2.3.4:80"), "1.2.3.4:80"},
   445  		{mustIPPort("[::]:80"), "[::]:80"},
   446  		{mustIPPort("[1::CAFE]:80"), "[1::cafe]:80"},
   447  		{mustIPPort("[1::CAFE%en0]:80"), "[1::cafe%en0]:80"},
   448  		{mustIPPort("[::FFFF:192.168.140.255]:80"), "[::ffff:192.168.140.255]:80"},
   449  		{mustIPPort("[::FFFF:192.168.140.255%en0]:80"), "[::ffff:192.168.140.255%en0]:80"},
   450  	}
   451  	for i, tt := range tests {
   452  		if got := tt.in.String(); got != tt.want {
   453  			t.Errorf("%d. for (%v, %v) String = %q; want %q", i, tt.in.Addr(), tt.in.Port(), got, tt.want)
   454  		}
   455  		mt, err := tt.in.MarshalText()
   456  		if err != nil {
   457  			t.Errorf("%d. for (%v, %v) MarshalText error: %v", i, tt.in.Addr(), tt.in.Port(), err)
   458  			continue
   459  		}
   460  		if string(mt) != tt.want {
   461  			t.Errorf("%d. for (%v, %v) MarshalText = %q; want %q", i, tt.in.Addr(), tt.in.Port(), mt, tt.want)
   462  		}
   463  
   464  		mtAppend := make([]byte, 4, 32)
   465  		mtAppend, err = tt.in.AppendText(mtAppend)
   466  		mtAppend = mtAppend[4:]
   467  		if err != nil {
   468  			t.Errorf("%d. for (%v, %v) AppendText error: %v", i, tt.in.Addr(), tt.in.Port(), err)
   469  			continue
   470  		}
   471  		if string(mtAppend) != tt.want {
   472  			t.Errorf("%d. for (%v, %v) AppendText = %q; want %q", i, tt.in.Addr(), tt.in.Port(), mtAppend, tt.want)
   473  		}
   474  	}
   475  }
   476  
   477  func TestAddrPortMarshalUnmarshalBinary(t *testing.T) {
   478  	tests := []struct {
   479  		ipport   string
   480  		wantSize int
   481  	}{
   482  		{"1.2.3.4:51820", 4 + 2},
   483  		{"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", 16 + 2},
   484  		{"[::ffff:c000:0280]:65535", 16 + 2},
   485  		{"[::ffff:c000:0280%eth0]:1", 20 + 2},
   486  	}
   487  	for _, tc := range tests {
   488  		var ipport AddrPort
   489  		if len(tc.ipport) > 0 {
   490  			ipport = mustIPPort(tc.ipport)
   491  		}
   492  		b, err := ipport.MarshalBinary()
   493  		if err != nil {
   494  			t.Fatal(err)
   495  		}
   496  		if len(b) != tc.wantSize {
   497  			t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(b), tc.wantSize)
   498  		}
   499  		var ipport2 AddrPort
   500  		if err := ipport2.UnmarshalBinary(b); err != nil {
   501  			t.Fatal(err)
   502  		}
   503  		if ipport != ipport2 {
   504  			t.Fatalf("got %v; want %v", ipport2, ipport)
   505  		}
   506  
   507  		bAppend := make([]byte, 4, 32)
   508  		bAppend, err = ipport.AppendBinary(bAppend)
   509  		bAppend = bAppend[4:]
   510  		if err != nil {
   511  			t.Fatal(err)
   512  		}
   513  		if len(bAppend) != tc.wantSize {
   514  			t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(bAppend), tc.wantSize)
   515  		}
   516  		var ipport3 AddrPort
   517  		if err := ipport3.UnmarshalBinary(bAppend); err != nil {
   518  			t.Fatal(err)
   519  		}
   520  		if ipport != ipport3 {
   521  			t.Fatalf("got %v; want %v", ipport3, ipport)
   522  		}
   523  	}
   524  
   525  	// Cannot unmarshal from unexpected lengths.
   526  	for _, n := range []int{3, 7} {
   527  		var ipport2 AddrPort
   528  		if err := ipport2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
   529  			t.Fatalf("unmarshaled from unexpected length %d", n)
   530  		}
   531  	}
   532  }
   533  
   534  func TestPrefixMarshalTextString(t *testing.T) {
   535  	tests := []struct {
   536  		in   Prefix
   537  		want string
   538  	}{
   539  		{mustPrefix("1.2.3.4/24"), "1.2.3.4/24"},
   540  		{mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"},
   541  		{mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"},
   542  		{mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"},
   543  		{PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"}, // Zone should be stripped
   544  	}
   545  	for i, tt := range tests {
   546  		if got := tt.in.String(); got != tt.want {
   547  			t.Errorf("%d. for %v String = %q; want %q", i, tt.in, got, tt.want)
   548  		}
   549  		mt, err := tt.in.MarshalText()
   550  		if err != nil {
   551  			t.Errorf("%d. for %v MarshalText error: %v", i, tt.in, err)
   552  			continue
   553  		}
   554  		if string(mt) != tt.want {
   555  			t.Errorf("%d. for %v MarshalText = %q; want %q", i, tt.in, mt, tt.want)
   556  		}
   557  
   558  		mtAppend := make([]byte, 4, 64)
   559  		mtAppend, err = tt.in.AppendText(mtAppend)
   560  		mtAppend = mtAppend[4:]
   561  		if err != nil {
   562  			t.Errorf("%d. for %v AppendText error: %v", i, tt.in, err)
   563  			continue
   564  		}
   565  		if string(mtAppend) != tt.want {
   566  			t.Errorf("%d. for %v AppendText = %q; want %q", i, tt.in, mtAppend, tt.want)
   567  		}
   568  	}
   569  }
   570  
   571  func TestPrefixMarshalUnmarshalBinary(t *testing.T) {
   572  	type testCase struct {
   573  		prefix   Prefix
   574  		wantSize int
   575  	}
   576  	tests := []testCase{
   577  		{mustPrefix("1.2.3.4/24"), 4 + 1},
   578  		{mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1},
   579  		{mustPrefix("::ffff:c000:0280/96"), 16 + 1},
   580  		{PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1}, // Zone should be stripped
   581  	}
   582  	tests = append(tests,
   583  		testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize},
   584  		testCase{PrefixFrom(tests[1].prefix.Addr(), 129), tests[1].wantSize})
   585  	for _, tc := range tests {
   586  		prefix := tc.prefix
   587  		b, err := prefix.MarshalBinary()
   588  		if err != nil {
   589  			t.Fatal(err)
   590  		}
   591  		if len(b) != tc.wantSize {
   592  			t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(b), tc.wantSize)
   593  		}
   594  		var prefix2 Prefix
   595  		if err := prefix2.UnmarshalBinary(b); err != nil {
   596  			t.Fatal(err)
   597  		}
   598  		if prefix != prefix2 {
   599  			t.Fatalf("got %v; want %v", prefix2, prefix)
   600  		}
   601  
   602  		bAppend := make([]byte, 4, 32)
   603  		bAppend, err = prefix.AppendBinary(bAppend)
   604  		bAppend = bAppend[4:]
   605  		if err != nil {
   606  			t.Fatal(err)
   607  		}
   608  		if len(bAppend) != tc.wantSize {
   609  			t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(bAppend), tc.wantSize)
   610  		}
   611  		var prefix3 Prefix
   612  		if err := prefix3.UnmarshalBinary(bAppend); err != nil {
   613  			t.Fatal(err)
   614  		}
   615  		if prefix != prefix3 {
   616  			t.Fatalf("got %v; want %v", prefix3, prefix)
   617  		}
   618  	}
   619  
   620  	// Cannot unmarshal from unexpected lengths.
   621  	for _, n := range []int{3, 6} {
   622  		var prefix2 Prefix
   623  		if err := prefix2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
   624  			t.Fatalf("unmarshaled from unexpected length %d", n)
   625  		}
   626  	}
   627  }
   628  
   629  func TestAddrMarshalUnmarshal(t *testing.T) {
   630  	// This only tests the cases where Marshal/Unmarshal diverges from
   631  	// the behavior of ParseAddr/String. For the rest of the test cases,
   632  	// see TestParseAddr above.
   633  	orig := `""`
   634  	var ip Addr
   635  	if err := json.Unmarshal([]byte(orig), &ip); err != nil {
   636  		t.Fatalf("Unmarshal(%q) got error %v", orig, err)
   637  	}
   638  	if ip != (Addr{}) {
   639  		t.Errorf("Unmarshal(%q) is not the zero Addr", orig)
   640  	}
   641  
   642  	jsb, err := json.Marshal(ip)
   643  	if err != nil {
   644  		t.Fatalf("Marshal(%v) got error %v", ip, err)
   645  	}
   646  	back := string(jsb)
   647  	if back != orig {
   648  		t.Errorf("Marshal(Unmarshal(%q)) got %q, want %q", orig, back, orig)
   649  	}
   650  }
   651  
   652  func TestAddrFrom16(t *testing.T) {
   653  	tests := []struct {
   654  		name string
   655  		in   [16]byte
   656  		want Addr
   657  	}{
   658  		{
   659  			name: "v6-raw",
   660  			in:   [...]byte{15: 1},
   661  			want: MkAddr(Mk128(0, 1), Z6noz),
   662  		},
   663  		{
   664  			name: "v4-raw",
   665  			in:   [...]byte{10: 0xff, 11: 0xff, 12: 1, 13: 2, 14: 3, 15: 4},
   666  			want: MkAddr(Mk128(0, 0xffff01020304), Z6noz),
   667  		},
   668  	}
   669  	for _, tt := range tests {
   670  		t.Run(tt.name, func(t *testing.T) {
   671  			got := AddrFrom16(tt.in)
   672  			if got != tt.want {
   673  				t.Errorf("got %#v; want %#v", got, tt.want)
   674  			}
   675  		})
   676  	}
   677  }
   678  
   679  func TestIPProperties(t *testing.T) {
   680  	var (
   681  		nilIP Addr
   682  
   683  		unicast4           = mustIP("192.0.2.1")
   684  		unicast6           = mustIP("2001:db8::1")
   685  		unicastZone6       = mustIP("2001:db8::1%eth0")
   686  		unicast6Unassigned = mustIP("4000::1") // not in 2000::/3.
   687  
   688  		multicast4     = mustIP("224.0.0.1")
   689  		multicast6     = mustIP("ff02::1")
   690  		multicastZone6 = mustIP("ff02::1%eth0")
   691  
   692  		llu4     = mustIP("169.254.0.1")
   693  		llu6     = mustIP("fe80::1")
   694  		llu6Last = mustIP("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
   695  		lluZone6 = mustIP("fe80::1%eth0")
   696  
   697  		loopback4 = mustIP("127.0.0.1")
   698  
   699  		ilm6     = mustIP("ff01::1")
   700  		ilmZone6 = mustIP("ff01::1%eth0")
   701  
   702  		private4a        = mustIP("10.0.0.1")
   703  		private4b        = mustIP("172.16.0.1")
   704  		private4c        = mustIP("192.168.1.1")
   705  		private6         = mustIP("fd00::1")
   706  		private6mapped4a = mustIP("::ffff:10.0.0.1")
   707  		private6mapped4b = mustIP("::ffff:172.16.0.1")
   708  		private6mapped4c = mustIP("::ffff:192.168.1.1")
   709  	)
   710  
   711  	tests := []struct {
   712  		name                    string
   713  		ip                      Addr
   714  		globalUnicast           bool
   715  		interfaceLocalMulticast bool
   716  		linkLocalMulticast      bool
   717  		linkLocalUnicast        bool
   718  		loopback                bool
   719  		multicast               bool
   720  		private                 bool
   721  		unspecified             bool
   722  	}{
   723  		{
   724  			name: "nil",
   725  			ip:   nilIP,
   726  		},
   727  		{
   728  			name:          "unicast v4Addr",
   729  			ip:            unicast4,
   730  			globalUnicast: true,
   731  		},
   732  		{
   733  			name:          "unicast v6 mapped v4Addr",
   734  			ip:            AddrFrom16(unicast4.As16()),
   735  			globalUnicast: true,
   736  		},
   737  		{
   738  			name:          "unicast v6Addr",
   739  			ip:            unicast6,
   740  			globalUnicast: true,
   741  		},
   742  		{
   743  			name:          "unicast v6AddrZone",
   744  			ip:            unicastZone6,
   745  			globalUnicast: true,
   746  		},
   747  		{
   748  			name:          "unicast v6Addr unassigned",
   749  			ip:            unicast6Unassigned,
   750  			globalUnicast: true,
   751  		},
   752  		{
   753  			name:               "multicast v4Addr",
   754  			ip:                 multicast4,
   755  			linkLocalMulticast: true,
   756  			multicast:          true,
   757  		},
   758  		{
   759  			name:               "multicast v6 mapped v4Addr",
   760  			ip:                 AddrFrom16(multicast4.As16()),
   761  			linkLocalMulticast: true,
   762  			multicast:          true,
   763  		},
   764  		{
   765  			name:               "multicast v6Addr",
   766  			ip:                 multicast6,
   767  			linkLocalMulticast: true,
   768  			multicast:          true,
   769  		},
   770  		{
   771  			name:               "multicast v6AddrZone",
   772  			ip:                 multicastZone6,
   773  			linkLocalMulticast: true,
   774  			multicast:          true,
   775  		},
   776  		{
   777  			name:             "link-local unicast v4Addr",
   778  			ip:               llu4,
   779  			linkLocalUnicast: true,
   780  		},
   781  		{
   782  			name:             "link-local unicast v6 mapped v4Addr",
   783  			ip:               AddrFrom16(llu4.As16()),
   784  			linkLocalUnicast: true,
   785  		},
   786  		{
   787  			name:             "link-local unicast v6Addr",
   788  			ip:               llu6,
   789  			linkLocalUnicast: true,
   790  		},
   791  		{
   792  			name:             "link-local unicast v6Addr upper bound",
   793  			ip:               llu6Last,
   794  			linkLocalUnicast: true,
   795  		},
   796  		{
   797  			name:             "link-local unicast v6AddrZone",
   798  			ip:               lluZone6,
   799  			linkLocalUnicast: true,
   800  		},
   801  		{
   802  			name:     "loopback v4Addr",
   803  			ip:       loopback4,
   804  			loopback: true,
   805  		},
   806  		{
   807  			name:     "loopback v6Addr",
   808  			ip:       IPv6Loopback(),
   809  			loopback: true,
   810  		},
   811  		{
   812  			name:     "loopback v6 mapped v4Addr",
   813  			ip:       AddrFrom16(IPv6Loopback().As16()),
   814  			loopback: true,
   815  		},
   816  		{
   817  			name:                    "interface-local multicast v6Addr",
   818  			ip:                      ilm6,
   819  			interfaceLocalMulticast: true,
   820  			multicast:               true,
   821  		},
   822  		{
   823  			name:                    "interface-local multicast v6AddrZone",
   824  			ip:                      ilmZone6,
   825  			interfaceLocalMulticast: true,
   826  			multicast:               true,
   827  		},
   828  		{
   829  			name:          "private v4Addr 10/8",
   830  			ip:            private4a,
   831  			globalUnicast: true,
   832  			private:       true,
   833  		},
   834  		{
   835  			name:          "private v4Addr 172.16/12",
   836  			ip:            private4b,
   837  			globalUnicast: true,
   838  			private:       true,
   839  		},
   840  		{
   841  			name:          "private v4Addr 192.168/16",
   842  			ip:            private4c,
   843  			globalUnicast: true,
   844  			private:       true,
   845  		},
   846  		{
   847  			name:          "private v6Addr",
   848  			ip:            private6,
   849  			globalUnicast: true,
   850  			private:       true,
   851  		},
   852  		{
   853  			name:          "private v6 mapped v4Addr 10/8",
   854  			ip:            private6mapped4a,
   855  			globalUnicast: true,
   856  			private:       true,
   857  		},
   858  		{
   859  			name:          "private v6 mapped v4Addr 172.16/12",
   860  			ip:            private6mapped4b,
   861  			globalUnicast: true,
   862  			private:       true,
   863  		},
   864  		{
   865  			name:          "private v6 mapped v4Addr 192.168/16",
   866  			ip:            private6mapped4c,
   867  			globalUnicast: true,
   868  			private:       true,
   869  		},
   870  		{
   871  			name:        "unspecified v4Addr",
   872  			ip:          IPv4Unspecified(),
   873  			unspecified: true,
   874  		},
   875  		{
   876  			name:        "unspecified v6Addr",
   877  			ip:          IPv6Unspecified(),
   878  			unspecified: true,
   879  		},
   880  	}
   881  
   882  	for _, tt := range tests {
   883  		t.Run(tt.name, func(t *testing.T) {
   884  			gu := tt.ip.IsGlobalUnicast()
   885  			if gu != tt.globalUnicast {
   886  				t.Errorf("IsGlobalUnicast(%v) = %v; want %v", tt.ip, gu, tt.globalUnicast)
   887  			}
   888  
   889  			ilm := tt.ip.IsInterfaceLocalMulticast()
   890  			if ilm != tt.interfaceLocalMulticast {
   891  				t.Errorf("IsInterfaceLocalMulticast(%v) = %v; want %v", tt.ip, ilm, tt.interfaceLocalMulticast)
   892  			}
   893  
   894  			llu := tt.ip.IsLinkLocalUnicast()
   895  			if llu != tt.linkLocalUnicast {
   896  				t.Errorf("IsLinkLocalUnicast(%v) = %v; want %v", tt.ip, llu, tt.linkLocalUnicast)
   897  			}
   898  
   899  			llm := tt.ip.IsLinkLocalMulticast()
   900  			if llm != tt.linkLocalMulticast {
   901  				t.Errorf("IsLinkLocalMulticast(%v) = %v; want %v", tt.ip, llm, tt.linkLocalMulticast)
   902  			}
   903  
   904  			lo := tt.ip.IsLoopback()
   905  			if lo != tt.loopback {
   906  				t.Errorf("IsLoopback(%v) = %v; want %v", tt.ip, lo, tt.loopback)
   907  			}
   908  
   909  			multicast := tt.ip.IsMulticast()
   910  			if multicast != tt.multicast {
   911  				t.Errorf("IsMulticast(%v) = %v; want %v", tt.ip, multicast, tt.multicast)
   912  			}
   913  
   914  			private := tt.ip.IsPrivate()
   915  			if private != tt.private {
   916  				t.Errorf("IsPrivate(%v) = %v; want %v", tt.ip, private, tt.private)
   917  			}
   918  
   919  			unspecified := tt.ip.IsUnspecified()
   920  			if unspecified != tt.unspecified {
   921  				t.Errorf("IsUnspecified(%v) = %v; want %v", tt.ip, unspecified, tt.unspecified)
   922  			}
   923  		})
   924  	}
   925  }
   926  
   927  func TestAddrWellKnown(t *testing.T) {
   928  	tests := []struct {
   929  		name string
   930  		ip   Addr
   931  		std  net.IP
   932  	}{
   933  		{
   934  			name: "IPv4 unspecified",
   935  			ip:   IPv4Unspecified(),
   936  			std:  net.IPv4zero,
   937  		},
   938  		{
   939  			name: "IPv6 link-local all nodes",
   940  			ip:   IPv6LinkLocalAllNodes(),
   941  			std:  net.IPv6linklocalallnodes,
   942  		},
   943  		{
   944  			name: "IPv6 link-local all routers",
   945  			ip:   IPv6LinkLocalAllRouters(),
   946  			std:  net.IPv6linklocalallrouters,
   947  		},
   948  		{
   949  			name: "IPv6 loopback",
   950  			ip:   IPv6Loopback(),
   951  			std:  net.IPv6loopback,
   952  		},
   953  		{
   954  			name: "IPv6 unspecified",
   955  			ip:   IPv6Unspecified(),
   956  			std:  net.IPv6unspecified,
   957  		},
   958  	}
   959  
   960  	for _, tt := range tests {
   961  		t.Run(tt.name, func(t *testing.T) {
   962  			want := tt.std.String()
   963  			got := tt.ip.String()
   964  
   965  			if got != want {
   966  				t.Fatalf("got %s, want %s", got, want)
   967  			}
   968  		})
   969  	}
   970  }
   971  
   972  func TestAddrLessCompare(t *testing.T) {
   973  	tests := []struct {
   974  		a, b Addr
   975  		want bool
   976  	}{
   977  		{Addr{}, Addr{}, false},
   978  		{Addr{}, mustIP("1.2.3.4"), true},
   979  		{mustIP("1.2.3.4"), Addr{}, false},
   980  
   981  		{mustIP("1.2.3.4"), mustIP("0102:0304::0"), true},
   982  		{mustIP("0102:0304::0"), mustIP("1.2.3.4"), false},
   983  		{mustIP("1.2.3.4"), mustIP("1.2.3.4"), false},
   984  
   985  		{mustIP("::1"), mustIP("::2"), true},
   986  		{mustIP("::1"), mustIP("::1%foo"), true},
   987  		{mustIP("::1%foo"), mustIP("::2"), true},
   988  		{mustIP("::2"), mustIP("::3"), true},
   989  
   990  		{mustIP("::"), mustIP("0.0.0.0"), false},
   991  		{mustIP("0.0.0.0"), mustIP("::"), true},
   992  
   993  		{mustIP("::1%a"), mustIP("::1%b"), true},
   994  		{mustIP("::1%a"), mustIP("::1%a"), false},
   995  		{mustIP("::1%b"), mustIP("::1%a"), false},
   996  
   997  		// For Issue 68113, verify that an IPv4 address and a
   998  		// v4-mapped-IPv6 address differing only in their zone
   999  		// pointer are unequal via all three of
  1000  		// ==/Compare/reflect.DeepEqual. In Go 1.22 and
  1001  		// earlier, these were accidentally equal via
  1002  		// DeepEqual due to their zone pointers (z) differing
  1003  		// but pointing to identical structures.
  1004  		{mustIP("::ffff:11.1.1.12"), mustIP("11.1.1.12"), false},
  1005  	}
  1006  	for _, tt := range tests {
  1007  		got := tt.a.Less(tt.b)
  1008  		if got != tt.want {
  1009  			t.Errorf("Less(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
  1010  		}
  1011  		cmp := tt.a.Compare(tt.b)
  1012  		if got && cmp != -1 {
  1013  			t.Errorf("Less(%q, %q) = true, but Compare = %v (not -1)", tt.a, tt.b, cmp)
  1014  		}
  1015  		if cmp < -1 || cmp > 1 {
  1016  			t.Errorf("bogus Compare return value %v", cmp)
  1017  		}
  1018  		if cmp == 0 && tt.a != tt.b {
  1019  			t.Errorf("Compare(%q, %q) = 0; but not equal", tt.a, tt.b)
  1020  		}
  1021  		if cmp == 1 && !tt.b.Less(tt.a) {
  1022  			t.Errorf("Compare(%q, %q) = 1; but b.Less(a) isn't true", tt.a, tt.b)
  1023  		}
  1024  
  1025  		// Also check inverse.
  1026  		if got == tt.want && got {
  1027  			got2 := tt.b.Less(tt.a)
  1028  			if got2 {
  1029  				t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a)
  1030  			}
  1031  		}
  1032  
  1033  		// Also check reflect.DeepEqual. See issue 68113.
  1034  		deepEq := reflect.DeepEqual(tt.a, tt.b)
  1035  		if (cmp == 0) != deepEq {
  1036  			t.Errorf("%q and %q differ in == (%v) vs reflect.DeepEqual (%v)", tt.a, tt.b, cmp == 0, deepEq)
  1037  		}
  1038  	}
  1039  
  1040  	// And just sort.
  1041  	values := []Addr{
  1042  		mustIP("::1"),
  1043  		mustIP("::2"),
  1044  		Addr{},
  1045  		mustIP("1.2.3.4"),
  1046  		mustIP("8.8.8.8"),
  1047  		mustIP("::1%foo"),
  1048  	}
  1049  	slices.SortFunc(values, Addr.Compare)
  1050  	got := fmt.Sprintf("%s", values)
  1051  	want := `[invalid IP 1.2.3.4 8.8.8.8 ::1 ::1%foo ::2]`
  1052  	if got != want {
  1053  		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
  1054  	}
  1055  }
  1056  
  1057  func TestAddrPortCompare(t *testing.T) {
  1058  	tests := []struct {
  1059  		a, b AddrPort
  1060  		want int
  1061  	}{
  1062  		{AddrPort{}, AddrPort{}, 0},
  1063  		{AddrPort{}, mustIPPort("1.2.3.4:80"), -1},
  1064  
  1065  		{mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:80"), 0},
  1066  		{mustIPPort("[::1]:80"), mustIPPort("[::1]:80"), 0},
  1067  
  1068  		{mustIPPort("1.2.3.4:80"), mustIPPort("2.3.4.5:22"), -1},
  1069  		{mustIPPort("[::1]:80"), mustIPPort("[::2]:22"), -1},
  1070  
  1071  		{mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:443"), -1},
  1072  		{mustIPPort("[::1]:80"), mustIPPort("[::1]:443"), -1},
  1073  
  1074  		{mustIPPort("1.2.3.4:80"), mustIPPort("[0102:0304::0]:80"), -1},
  1075  	}
  1076  	for _, tt := range tests {
  1077  		got := tt.a.Compare(tt.b)
  1078  		if got != tt.want {
  1079  			t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
  1080  		}
  1081  
  1082  		// Also check inverse.
  1083  		if got == tt.want {
  1084  			got2 := tt.b.Compare(tt.a)
  1085  			if want2 := -1 * tt.want; got2 != want2 {
  1086  				t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
  1087  			}
  1088  		}
  1089  	}
  1090  
  1091  	// And just sort.
  1092  	values := []AddrPort{
  1093  		mustIPPort("[::1]:80"),
  1094  		mustIPPort("[::2]:80"),
  1095  		AddrPort{},
  1096  		mustIPPort("1.2.3.4:443"),
  1097  		mustIPPort("8.8.8.8:8080"),
  1098  		mustIPPort("[::1%foo]:1024"),
  1099  	}
  1100  	slices.SortFunc(values, AddrPort.Compare)
  1101  	got := fmt.Sprintf("%s", values)
  1102  	want := `[invalid AddrPort 1.2.3.4:443 8.8.8.8:8080 [::1]:80 [::1%foo]:1024 [::2]:80]`
  1103  	if got != want {
  1104  		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
  1105  	}
  1106  }
  1107  
  1108  func TestPrefixCompare(t *testing.T) {
  1109  	tests := []struct {
  1110  		a, b Prefix
  1111  		want int
  1112  	}{
  1113  		{Prefix{}, Prefix{}, 0},
  1114  		{Prefix{}, mustPrefix("1.2.3.0/24"), -1},
  1115  
  1116  		{mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.0/24"), 0},
  1117  		{mustPrefix("fe80::/64"), mustPrefix("fe80::/64"), 0},
  1118  
  1119  		{mustPrefix("1.2.3.0/24"), mustPrefix("1.2.4.0/24"), -1},
  1120  		{mustPrefix("fe80::/64"), mustPrefix("fe90::/64"), -1},
  1121  
  1122  		{mustPrefix("1.2.0.0/16"), mustPrefix("1.2.0.0/24"), -1},
  1123  		{mustPrefix("fe80::/48"), mustPrefix("fe80::/64"), -1},
  1124  
  1125  		{mustPrefix("1.2.3.0/24"), mustPrefix("fe80::/8"), -1},
  1126  
  1127  		{mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.4/24"), -1},
  1128  		{mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.0/28"), -1},
  1129  	}
  1130  	for _, tt := range tests {
  1131  		got := tt.a.Compare(tt.b)
  1132  		if got != tt.want {
  1133  			t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
  1134  		}
  1135  
  1136  		// Also check inverse.
  1137  		if got == tt.want {
  1138  			got2 := tt.b.Compare(tt.a)
  1139  			if want2 := -1 * tt.want; got2 != want2 {
  1140  				t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
  1141  			}
  1142  		}
  1143  	}
  1144  
  1145  	// And just sort.
  1146  	values := []Prefix{
  1147  		mustPrefix("1.2.3.0/24"),
  1148  		mustPrefix("fe90::/64"),
  1149  		mustPrefix("fe80::/64"),
  1150  		mustPrefix("1.2.0.0/16"),
  1151  		Prefix{},
  1152  		mustPrefix("fe80::/48"),
  1153  		mustPrefix("1.2.0.0/24"),
  1154  		mustPrefix("1.2.3.4/24"),
  1155  		mustPrefix("1.2.3.0/28"),
  1156  	}
  1157  	slices.SortFunc(values, Prefix.Compare)
  1158  	got := fmt.Sprintf("%s", values)
  1159  	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]`
  1160  	if got != want {
  1161  		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
  1162  	}
  1163  
  1164  	// Lists from
  1165  	// https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml and
  1166  	// https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml,
  1167  	// to verify that the sort order matches IANA's conventional
  1168  	// ordering.
  1169  	values = []Prefix{
  1170  		mustPrefix("0.0.0.0/8"),
  1171  		mustPrefix("127.0.0.0/8"),
  1172  		mustPrefix("10.0.0.0/8"),
  1173  		mustPrefix("203.0.113.0/24"),
  1174  		mustPrefix("169.254.0.0/16"),
  1175  		mustPrefix("192.0.0.0/24"),
  1176  		mustPrefix("240.0.0.0/4"),
  1177  		mustPrefix("192.0.2.0/24"),
  1178  		mustPrefix("192.0.0.170/32"),
  1179  		mustPrefix("198.18.0.0/15"),
  1180  		mustPrefix("192.0.0.8/32"),
  1181  		mustPrefix("0.0.0.0/32"),
  1182  		mustPrefix("192.0.0.9/32"),
  1183  		mustPrefix("198.51.100.0/24"),
  1184  		mustPrefix("192.168.0.0/16"),
  1185  		mustPrefix("192.0.0.10/32"),
  1186  		mustPrefix("192.175.48.0/24"),
  1187  		mustPrefix("192.52.193.0/24"),
  1188  		mustPrefix("100.64.0.0/10"),
  1189  		mustPrefix("255.255.255.255/32"),
  1190  		mustPrefix("192.31.196.0/24"),
  1191  		mustPrefix("172.16.0.0/12"),
  1192  		mustPrefix("192.0.0.0/29"),
  1193  		mustPrefix("192.88.99.0/24"),
  1194  		mustPrefix("fec0::/10"),
  1195  		mustPrefix("6000::/3"),
  1196  		mustPrefix("fe00::/9"),
  1197  		mustPrefix("8000::/3"),
  1198  		mustPrefix("0000::/8"),
  1199  		mustPrefix("0400::/6"),
  1200  		mustPrefix("f800::/6"),
  1201  		mustPrefix("e000::/4"),
  1202  		mustPrefix("ff00::/8"),
  1203  		mustPrefix("a000::/3"),
  1204  		mustPrefix("fc00::/7"),
  1205  		mustPrefix("1000::/4"),
  1206  		mustPrefix("0800::/5"),
  1207  		mustPrefix("4000::/3"),
  1208  		mustPrefix("0100::/8"),
  1209  		mustPrefix("c000::/3"),
  1210  		mustPrefix("fe80::/10"),
  1211  		mustPrefix("0200::/7"),
  1212  		mustPrefix("f000::/5"),
  1213  		mustPrefix("2000::/3"),
  1214  	}
  1215  	slices.SortFunc(values, func(a, b Prefix) int { return a.Compare(b) })
  1216  	got = fmt.Sprintf("%s", values)
  1217  	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]`
  1218  	if got != want {
  1219  		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
  1220  	}
  1221  }
  1222  
  1223  func TestIPStringExpanded(t *testing.T) {
  1224  	tests := []struct {
  1225  		ip Addr
  1226  		s  string
  1227  	}{
  1228  		{
  1229  			ip: Addr{},
  1230  			s:  "invalid IP",
  1231  		},
  1232  		{
  1233  			ip: mustIP("192.0.2.1"),
  1234  			s:  "192.0.2.1",
  1235  		},
  1236  		{
  1237  			ip: mustIP("::ffff:192.0.2.1"),
  1238  			s:  "0000:0000:0000:0000:0000:ffff:c000:0201",
  1239  		},
  1240  		{
  1241  			ip: mustIP("2001:db8::1"),
  1242  			s:  "2001:0db8:0000:0000:0000:0000:0000:0001",
  1243  		},
  1244  		{
  1245  			ip: mustIP("2001:db8::1%eth0"),
  1246  			s:  "2001:0db8:0000:0000:0000:0000:0000:0001%eth0",
  1247  		},
  1248  	}
  1249  
  1250  	for _, tt := range tests {
  1251  		t.Run(tt.ip.String(), func(t *testing.T) {
  1252  			want := tt.s
  1253  			got := tt.ip.StringExpanded()
  1254  
  1255  			if got != want {
  1256  				t.Fatalf("got %s, want %s", got, want)
  1257  			}
  1258  		})
  1259  	}
  1260  }
  1261  
  1262  func TestPrefixMasking(t *testing.T) {
  1263  	type subtest struct {
  1264  		ip   Addr
  1265  		bits uint8
  1266  		p    Prefix
  1267  		ok   bool
  1268  	}
  1269  
  1270  	// makeIPv6 produces a set of IPv6 subtests with an optional zone identifier.
  1271  	makeIPv6 := func(zone string) []subtest {
  1272  		if zone != "" {
  1273  			zone = "%" + zone
  1274  		}
  1275  
  1276  		return []subtest{
  1277  			{
  1278  				ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
  1279  				bits: 255,
  1280  			},
  1281  			{
  1282  				ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
  1283  				bits: 32,
  1284  				p:    mustPrefix("2001:db8::/32"),
  1285  				ok:   true,
  1286  			},
  1287  			{
  1288  				ip:   mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
  1289  				bits: 96,
  1290  				p:    mustPrefix("fe80::dead:beef:0:0/96"),
  1291  				ok:   true,
  1292  			},
  1293  			{
  1294  				ip:   mustIP(fmt.Sprintf("aaaa::%s", zone)),
  1295  				bits: 4,
  1296  				p:    mustPrefix("a000::/4"),
  1297  				ok:   true,
  1298  			},
  1299  			{
  1300  				ip:   mustIP(fmt.Sprintf("::%s", zone)),
  1301  				bits: 63,
  1302  				p:    mustPrefix("::/63"),
  1303  				ok:   true,
  1304  			},
  1305  		}
  1306  	}
  1307  
  1308  	tests := []struct {
  1309  		family   string
  1310  		subtests []subtest
  1311  	}{
  1312  		{
  1313  			family: "nil",
  1314  			subtests: []subtest{
  1315  				{
  1316  					bits: 255,
  1317  					ok:   true,
  1318  				},
  1319  				{
  1320  					bits: 16,
  1321  					ok:   true,
  1322  				},
  1323  			},
  1324  		},
  1325  		{
  1326  			family: "IPv4",
  1327  			subtests: []subtest{
  1328  				{
  1329  					ip:   mustIP("192.0.2.0"),
  1330  					bits: 255,
  1331  				},
  1332  				{
  1333  					ip:   mustIP("192.0.2.0"),
  1334  					bits: 16,
  1335  					p:    mustPrefix("192.0.0.0/16"),
  1336  					ok:   true,
  1337  				},
  1338  				{
  1339  					ip:   mustIP("255.255.255.255"),
  1340  					bits: 20,
  1341  					p:    mustPrefix("255.255.240.0/20"),
  1342  					ok:   true,
  1343  				},
  1344  				{
  1345  					// Partially masking one byte that contains both
  1346  					// 1s and 0s on either side of the mask limit.
  1347  					ip:   mustIP("100.98.156.66"),
  1348  					bits: 10,
  1349  					p:    mustPrefix("100.64.0.0/10"),
  1350  					ok:   true,
  1351  				},
  1352  			},
  1353  		},
  1354  		{
  1355  			family:   "IPv6",
  1356  			subtests: makeIPv6(""),
  1357  		},
  1358  		{
  1359  			family:   "IPv6 zone",
  1360  			subtests: makeIPv6("eth0"),
  1361  		},
  1362  	}
  1363  
  1364  	for _, tt := range tests {
  1365  		t.Run(tt.family, func(t *testing.T) {
  1366  			for _, st := range tt.subtests {
  1367  				t.Run(st.p.String(), func(t *testing.T) {
  1368  					// Ensure st.ip is not mutated.
  1369  					orig := st.ip.String()
  1370  
  1371  					p, err := st.ip.Prefix(int(st.bits))
  1372  					if st.ok && err != nil {
  1373  						t.Fatalf("failed to produce prefix: %v", err)
  1374  					}
  1375  					if !st.ok && err == nil {
  1376  						t.Fatal("expected an error, but none occurred")
  1377  					}
  1378  					if err != nil {
  1379  						t.Logf("err: %v", err)
  1380  						return
  1381  					}
  1382  
  1383  					if !reflect.DeepEqual(p, st.p) {
  1384  						t.Errorf("prefix = %q, want %q", p, st.p)
  1385  					}
  1386  
  1387  					if got := st.ip.String(); got != orig {
  1388  						t.Errorf("IP was mutated: %q, want %q", got, orig)
  1389  					}
  1390  				})
  1391  			}
  1392  		})
  1393  	}
  1394  }
  1395  
  1396  func TestPrefixMarshalUnmarshal(t *testing.T) {
  1397  	tests := []string{
  1398  		"",
  1399  		"1.2.3.4/32",
  1400  		"0.0.0.0/0",
  1401  		"::/0",
  1402  		"::1/128",
  1403  		"2001:db8::/32",
  1404  	}
  1405  
  1406  	for _, s := range tests {
  1407  		t.Run(s, func(t *testing.T) {
  1408  			// Ensure that JSON  (and by extension, text) marshaling is
  1409  			// sane by entering quoted input.
  1410  			orig := `"` + s + `"`
  1411  
  1412  			var p Prefix
  1413  			if err := json.Unmarshal([]byte(orig), &p); err != nil {
  1414  				t.Fatalf("failed to unmarshal: %v", err)
  1415  			}
  1416  
  1417  			pb, err := json.Marshal(p)
  1418  			if err != nil {
  1419  				t.Fatalf("failed to marshal: %v", err)
  1420  			}
  1421  
  1422  			back := string(pb)
  1423  			if orig != back {
  1424  				t.Errorf("Marshal = %q; want %q", back, orig)
  1425  			}
  1426  		})
  1427  	}
  1428  }
  1429  
  1430  func TestPrefixUnmarshalTextNonZero(t *testing.T) {
  1431  	ip := mustPrefix("fe80::/64")
  1432  	if err := ip.UnmarshalText([]byte("xxx")); err == nil {
  1433  		t.Fatal("unmarshaled into non-empty Prefix")
  1434  	}
  1435  }
  1436  
  1437  func TestIs4AndIs6(t *testing.T) {
  1438  	tests := []struct {
  1439  		ip  Addr
  1440  		is4 bool
  1441  		is6 bool
  1442  	}{
  1443  		{Addr{}, false, false},
  1444  		{mustIP("1.2.3.4"), true, false},
  1445  		{mustIP("127.0.0.2"), true, false},
  1446  		{mustIP("::1"), false, true},
  1447  		{mustIP("::ffff:192.0.2.128"), false, true},
  1448  		{mustIP("::fffe:c000:0280"), false, true},
  1449  		{mustIP("::1%eth0"), false, true},
  1450  	}
  1451  	for _, tt := range tests {
  1452  		got4 := tt.ip.Is4()
  1453  		if got4 != tt.is4 {
  1454  			t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4)
  1455  		}
  1456  
  1457  		got6 := tt.ip.Is6()
  1458  		if got6 != tt.is6 {
  1459  			t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6)
  1460  		}
  1461  	}
  1462  }
  1463  
  1464  func TestIs4In6(t *testing.T) {
  1465  	tests := []struct {
  1466  		ip        Addr
  1467  		want      bool
  1468  		wantUnmap Addr
  1469  	}{
  1470  		{Addr{}, false, Addr{}},
  1471  		{mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")},
  1472  		{mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")},
  1473  		{mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")},
  1474  		{mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")},
  1475  		{mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
  1476  		{mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")},
  1477  		{mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
  1478  		{mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
  1479  		{mustIP("::1"), false, mustIP("::1")},
  1480  		{mustIP("1.2.3.4"), false, mustIP("1.2.3.4")},
  1481  	}
  1482  	for _, tt := range tests {
  1483  		got := tt.ip.Is4In6()
  1484  		if got != tt.want {
  1485  			t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want)
  1486  		}
  1487  		u := tt.ip.Unmap()
  1488  		if u != tt.wantUnmap {
  1489  			t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap)
  1490  		}
  1491  	}
  1492  }
  1493  
  1494  func TestPrefixMasked(t *testing.T) {
  1495  	tests := []struct {
  1496  		prefix Prefix
  1497  		masked Prefix
  1498  	}{
  1499  		{
  1500  			prefix: mustPrefix("192.168.0.255/24"),
  1501  			masked: mustPrefix("192.168.0.0/24"),
  1502  		},
  1503  		{
  1504  			prefix: mustPrefix("2100::/3"),
  1505  			masked: mustPrefix("2000::/3"),
  1506  		},
  1507  		{
  1508  			prefix: PrefixFrom(mustIP("2000::"), 129),
  1509  			masked: Prefix{},
  1510  		},
  1511  		{
  1512  			prefix: PrefixFrom(mustIP("1.2.3.4"), 33),
  1513  			masked: Prefix{},
  1514  		},
  1515  	}
  1516  	for _, test := range tests {
  1517  		t.Run(test.prefix.String(), func(t *testing.T) {
  1518  			got := test.prefix.Masked()
  1519  			if got != test.masked {
  1520  				t.Errorf("Masked=%s, want %s", got, test.masked)
  1521  			}
  1522  		})
  1523  	}
  1524  }
  1525  
  1526  func TestPrefix(t *testing.T) {
  1527  	tests := []struct {
  1528  		prefix      string
  1529  		ip          Addr
  1530  		bits        int
  1531  		str         string
  1532  		contains    []Addr
  1533  		notContains []Addr
  1534  	}{
  1535  		{
  1536  			prefix:      "192.168.0.0/24",
  1537  			ip:          mustIP("192.168.0.0"),
  1538  			bits:        24,
  1539  			contains:    mustIPs("192.168.0.1", "192.168.0.55"),
  1540  			notContains: mustIPs("192.168.1.1", "1.1.1.1"),
  1541  		},
  1542  		{
  1543  			prefix:      "192.168.1.1/32",
  1544  			ip:          mustIP("192.168.1.1"),
  1545  			bits:        32,
  1546  			contains:    mustIPs("192.168.1.1"),
  1547  			notContains: mustIPs("192.168.1.2"),
  1548  		},
  1549  		{
  1550  			prefix:      "100.64.0.0/10", // CGNAT range; prefix not multiple of 8
  1551  			ip:          mustIP("100.64.0.0"),
  1552  			bits:        10,
  1553  			contains:    mustIPs("100.64.0.0", "100.64.0.1", "100.81.251.94", "100.100.100.100", "100.127.255.254", "100.127.255.255"),
  1554  			notContains: mustIPs("100.63.255.255", "100.128.0.0"),
  1555  		},
  1556  		{
  1557  			prefix:      "2001:db8::/96",
  1558  			ip:          mustIP("2001:db8::"),
  1559  			bits:        96,
  1560  			contains:    mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"),
  1561  			notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"),
  1562  		},
  1563  		{
  1564  			prefix:      "0.0.0.0/0",
  1565  			ip:          mustIP("0.0.0.0"),
  1566  			bits:        0,
  1567  			contains:    mustIPs("192.168.0.1", "1.1.1.1"),
  1568  			notContains: append(mustIPs("2001:db8::1"), Addr{}),
  1569  		},
  1570  		{
  1571  			prefix:      "::/0",
  1572  			ip:          mustIP("::"),
  1573  			bits:        0,
  1574  			contains:    mustIPs("::1", "2001:db8::1"),
  1575  			notContains: mustIPs("192.0.2.1"),
  1576  		},
  1577  		{
  1578  			prefix:      "2000::/3",
  1579  			ip:          mustIP("2000::"),
  1580  			bits:        3,
  1581  			contains:    mustIPs("2001:db8::1"),
  1582  			notContains: mustIPs("fe80::1"),
  1583  		},
  1584  	}
  1585  	for _, test := range tests {
  1586  		t.Run(test.prefix, func(t *testing.T) {
  1587  			prefix, err := ParsePrefix(test.prefix)
  1588  			if err != nil {
  1589  				t.Fatal(err)
  1590  			}
  1591  			if prefix.Addr() != test.ip {
  1592  				t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip)
  1593  			}
  1594  			if prefix.Bits() != test.bits {
  1595  				t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits)
  1596  			}
  1597  			for _, ip := range test.contains {
  1598  				if !prefix.Contains(ip) {
  1599  					t.Errorf("does not contain %s", ip)
  1600  				}
  1601  			}
  1602  			for _, ip := range test.notContains {
  1603  				if prefix.Contains(ip) {
  1604  					t.Errorf("contains %s", ip)
  1605  				}
  1606  			}
  1607  			want := test.str
  1608  			if want == "" {
  1609  				want = test.prefix
  1610  			}
  1611  			if got := prefix.String(); got != want {
  1612  				t.Errorf("prefix.String()=%q, want %q", got, want)
  1613  			}
  1614  
  1615  			TestAppendToMarshal(t, prefix)
  1616  		})
  1617  	}
  1618  }
  1619  
  1620  func TestPrefixFromInvalidBits(t *testing.T) {
  1621  	v4 := MustParseAddr("1.2.3.4")
  1622  	v6 := MustParseAddr("66::66")
  1623  	tests := []struct {
  1624  		ip       Addr
  1625  		in, want int
  1626  	}{
  1627  		{v4, 0, 0},
  1628  		{v6, 0, 0},
  1629  		{v4, 1, 1},
  1630  		{v4, 33, -1},
  1631  		{v6, 33, 33},
  1632  		{v6, 127, 127},
  1633  		{v6, 128, 128},
  1634  		{v4, 254, -1},
  1635  		{v4, 255, -1},
  1636  		{v4, -1, -1},
  1637  		{v6, -1, -1},
  1638  		{v4, -5, -1},
  1639  		{v6, -5, -1},
  1640  	}
  1641  	for _, tt := range tests {
  1642  		p := PrefixFrom(tt.ip, tt.in)
  1643  		if got := p.Bits(); got != tt.want {
  1644  			t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want)
  1645  		}
  1646  	}
  1647  }
  1648  
  1649  func TestParsePrefixAllocs(t *testing.T) {
  1650  	tests := []struct {
  1651  		ip    string
  1652  		slash string
  1653  	}{
  1654  		{"192.168.1.0", "/24"},
  1655  		{"aaaa:bbbb:cccc::", "/24"},
  1656  	}
  1657  	for _, test := range tests {
  1658  		prefix := test.ip + test.slash
  1659  		t.Run(prefix, func(t *testing.T) {
  1660  			ipAllocs := int(testing.AllocsPerRun(5, func() {
  1661  				ParseAddr(test.ip)
  1662  			}))
  1663  			prefixAllocs := int(testing.AllocsPerRun(5, func() {
  1664  				ParsePrefix(prefix)
  1665  			}))
  1666  			if got := prefixAllocs - ipAllocs; got != 0 {
  1667  				t.Errorf("allocs=%d, want 0", got)
  1668  			}
  1669  		})
  1670  	}
  1671  }
  1672  
  1673  func TestParsePrefixError(t *testing.T) {
  1674  	tests := []struct {
  1675  		prefix string
  1676  		errstr string
  1677  	}{
  1678  		{
  1679  			prefix: "192.168.0.0",
  1680  			errstr: "no '/'",
  1681  		},
  1682  		{
  1683  			prefix: "1.257.1.1/24",
  1684  			errstr: "value >255",
  1685  		},
  1686  		{
  1687  			prefix: "1.1.1.0/q",
  1688  			errstr: "bad bits",
  1689  		},
  1690  		{
  1691  			prefix: "1.1.1.0/-1",
  1692  			errstr: "bad bits",
  1693  		},
  1694  		{
  1695  			prefix: "1.1.1.0/33",
  1696  			errstr: "out of range",
  1697  		},
  1698  		{
  1699  			prefix: "2001::/129",
  1700  			errstr: "out of range",
  1701  		},
  1702  		// Zones are not allowed: https://go.dev/issue/51899
  1703  		{
  1704  			prefix: "1.1.1.0%a/24",
  1705  			errstr: "unexpected character",
  1706  		},
  1707  		{
  1708  			prefix: "2001:db8::%a/32",
  1709  			errstr: "zones cannot be present",
  1710  		},
  1711  		{
  1712  			prefix: "1.1.1.0/+32",
  1713  			errstr: "bad bits",
  1714  		},
  1715  		{
  1716  			prefix: "1.1.1.0/-32",
  1717  			errstr: "bad bits",
  1718  		},
  1719  		{
  1720  			prefix: "1.1.1.0/032",
  1721  			errstr: "bad bits",
  1722  		},
  1723  		{
  1724  			prefix: "1.1.1.0/0032",
  1725  			errstr: "bad bits",
  1726  		},
  1727  	}
  1728  	for _, test := range tests {
  1729  		t.Run(test.prefix, func(t *testing.T) {
  1730  			_, err := ParsePrefix(test.prefix)
  1731  			if err == nil {
  1732  				t.Fatal("no error")
  1733  			}
  1734  			if got := err.Error(); !strings.Contains(got, test.errstr) {
  1735  				t.Errorf("error is missing substring %q: %s", test.errstr, got)
  1736  			}
  1737  		})
  1738  	}
  1739  }
  1740  
  1741  func TestPrefixIsSingleIP(t *testing.T) {
  1742  	tests := []struct {
  1743  		ipp  Prefix
  1744  		want bool
  1745  	}{
  1746  		{ipp: mustPrefix("127.0.0.1/32"), want: true},
  1747  		{ipp: mustPrefix("127.0.0.1/31"), want: false},
  1748  		{ipp: mustPrefix("127.0.0.1/0"), want: false},
  1749  		{ipp: mustPrefix("::1/128"), want: true},
  1750  		{ipp: mustPrefix("::1/127"), want: false},
  1751  		{ipp: mustPrefix("::1/0"), want: false},
  1752  		{ipp: Prefix{}, want: false},
  1753  	}
  1754  	for _, tt := range tests {
  1755  		got := tt.ipp.IsSingleIP()
  1756  		if got != tt.want {
  1757  			t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want)
  1758  		}
  1759  	}
  1760  }
  1761  
  1762  func mustIPs(strs ...string) []Addr {
  1763  	var res []Addr
  1764  	for _, s := range strs {
  1765  		res = append(res, mustIP(s))
  1766  	}
  1767  	return res
  1768  }
  1769  
  1770  func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
  1771  	b.ReportAllocs()
  1772  	tests := []struct {
  1773  		name string
  1774  		ip   string
  1775  	}{
  1776  		{"ipv4", "1.2.3.4"},
  1777  		{"ipv6", "2001:db8::1"},
  1778  		{"ipv6+zone", "2001:db8::1%eth0"},
  1779  	}
  1780  	for _, tc := range tests {
  1781  		b.Run(tc.name, func(b *testing.B) {
  1782  			ip := mustIP(tc.ip)
  1783  			for i := 0; i < b.N; i++ {
  1784  				bt, err := ip.MarshalBinary()
  1785  				if err != nil {
  1786  					b.Fatal(err)
  1787  				}
  1788  				var ip2 Addr
  1789  				if err := ip2.UnmarshalBinary(bt); err != nil {
  1790  					b.Fatal(err)
  1791  				}
  1792  			}
  1793  		})
  1794  	}
  1795  }
  1796  
  1797  func BenchmarkStdIPv4(b *testing.B) {
  1798  	b.ReportAllocs()
  1799  	ips := []net.IP{}
  1800  	for i := 0; i < b.N; i++ {
  1801  		ip := net.IPv4(8, 8, 8, 8)
  1802  		ips = ips[:0]
  1803  		for i := 0; i < 100; i++ {
  1804  			ips = append(ips, ip)
  1805  		}
  1806  	}
  1807  }
  1808  
  1809  func BenchmarkIPv4(b *testing.B) {
  1810  	b.ReportAllocs()
  1811  	ips := []Addr{}
  1812  	for i := 0; i < b.N; i++ {
  1813  		ip := IPv4(8, 8, 8, 8)
  1814  		ips = ips[:0]
  1815  		for i := 0; i < 100; i++ {
  1816  			ips = append(ips, ip)
  1817  		}
  1818  	}
  1819  }
  1820  
  1821  // ip4i was one of the possible representations of IP that came up in
  1822  // discussions, inlining IPv4 addresses, but having an "overflow"
  1823  // interface for IPv6 or IPv6 + zone. This is here for benchmarking.
  1824  type ip4i struct {
  1825  	ip4    [4]byte
  1826  	flags1 byte
  1827  	flags2 byte
  1828  	flags3 byte
  1829  	flags4 byte
  1830  	ipv6   any
  1831  }
  1832  
  1833  func newip4i_v4(a, b, c, d byte) ip4i {
  1834  	return ip4i{ip4: [4]byte{a, b, c, d}}
  1835  }
  1836  
  1837  // BenchmarkIPv4_inline benchmarks the candidate representation, ip4i.
  1838  func BenchmarkIPv4_inline(b *testing.B) {
  1839  	b.ReportAllocs()
  1840  	ips := []ip4i{}
  1841  	for i := 0; i < b.N; i++ {
  1842  		ip := newip4i_v4(8, 8, 8, 8)
  1843  		ips = ips[:0]
  1844  		for i := 0; i < 100; i++ {
  1845  			ips = append(ips, ip)
  1846  		}
  1847  	}
  1848  }
  1849  
  1850  func BenchmarkStdIPv6(b *testing.B) {
  1851  	b.ReportAllocs()
  1852  	ips := []net.IP{}
  1853  	for i := 0; i < b.N; i++ {
  1854  		ip := net.ParseIP("2001:db8::1")
  1855  		ips = ips[:0]
  1856  		for i := 0; i < 100; i++ {
  1857  			ips = append(ips, ip)
  1858  		}
  1859  	}
  1860  }
  1861  
  1862  func BenchmarkIPv6(b *testing.B) {
  1863  	b.ReportAllocs()
  1864  	ips := []Addr{}
  1865  	for i := 0; i < b.N; i++ {
  1866  		ip := mustIP("2001:db8::1")
  1867  		ips = ips[:0]
  1868  		for i := 0; i < 100; i++ {
  1869  			ips = append(ips, ip)
  1870  		}
  1871  	}
  1872  }
  1873  
  1874  func BenchmarkIPv4Contains(b *testing.B) {
  1875  	b.ReportAllocs()
  1876  	prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24)
  1877  	ip := IPv4(192, 168, 1, 1)
  1878  	for i := 0; i < b.N; i++ {
  1879  		prefix.Contains(ip)
  1880  	}
  1881  }
  1882  
  1883  func BenchmarkIPv6Contains(b *testing.B) {
  1884  	b.ReportAllocs()
  1885  	prefix := MustParsePrefix("::1/128")
  1886  	ip := MustParseAddr("::1")
  1887  	for i := 0; i < b.N; i++ {
  1888  		prefix.Contains(ip)
  1889  	}
  1890  }
  1891  
  1892  var parseBenchInputs = []struct {
  1893  	name string
  1894  	ip   string
  1895  }{
  1896  	{"v4", "192.168.1.1"},
  1897  	{"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
  1898  	{"v6_ellipsis", "fd7a:115c::626b:430b"},
  1899  	{"v6_v4", "::ffff:192.168.140.255"},
  1900  	{"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
  1901  }
  1902  
  1903  func BenchmarkParseAddr(b *testing.B) {
  1904  	sinkInternValue = unique.Make(MakeAddrDetail(true, "eth1")) // Pin to not benchmark the intern package
  1905  	for _, test := range parseBenchInputs {
  1906  		b.Run(test.name, func(b *testing.B) {
  1907  			b.ReportAllocs()
  1908  			for i := 0; i < b.N; i++ {
  1909  				sinkIP, _ = ParseAddr(test.ip)
  1910  			}
  1911  		})
  1912  	}
  1913  }
  1914  
  1915  func BenchmarkStdParseIP(b *testing.B) {
  1916  	for _, test := range parseBenchInputs {
  1917  		b.Run(test.name, func(b *testing.B) {
  1918  			b.ReportAllocs()
  1919  			for i := 0; i < b.N; i++ {
  1920  				sinkStdIP = net.ParseIP(test.ip)
  1921  			}
  1922  		})
  1923  	}
  1924  }
  1925  
  1926  func BenchmarkAddrString(b *testing.B) {
  1927  	for _, test := range parseBenchInputs {
  1928  		ip := MustParseAddr(test.ip)
  1929  		b.Run(test.name, func(b *testing.B) {
  1930  			b.ReportAllocs()
  1931  			for i := 0; i < b.N; i++ {
  1932  				sinkString = ip.String()
  1933  			}
  1934  		})
  1935  	}
  1936  }
  1937  
  1938  func BenchmarkIPStringExpanded(b *testing.B) {
  1939  	for _, test := range parseBenchInputs {
  1940  		ip := MustParseAddr(test.ip)
  1941  		b.Run(test.name, func(b *testing.B) {
  1942  			b.ReportAllocs()
  1943  			for i := 0; i < b.N; i++ {
  1944  				sinkString = ip.StringExpanded()
  1945  			}
  1946  		})
  1947  	}
  1948  }
  1949  
  1950  func BenchmarkAddrMarshalText(b *testing.B) {
  1951  	for _, test := range parseBenchInputs {
  1952  		ip := MustParseAddr(test.ip)
  1953  		b.Run(test.name, func(b *testing.B) {
  1954  			b.ReportAllocs()
  1955  			for i := 0; i < b.N; i++ {
  1956  				sinkBytes, _ = ip.MarshalText()
  1957  			}
  1958  		})
  1959  	}
  1960  }
  1961  
  1962  func BenchmarkAddrPortString(b *testing.B) {
  1963  	for _, test := range parseBenchInputs {
  1964  		ip := MustParseAddr(test.ip)
  1965  		ipp := AddrPortFrom(ip, 60000)
  1966  		b.Run(test.name, func(b *testing.B) {
  1967  			b.ReportAllocs()
  1968  			for i := 0; i < b.N; i++ {
  1969  				sinkString = ipp.String()
  1970  			}
  1971  		})
  1972  	}
  1973  }
  1974  
  1975  func BenchmarkAddrPortMarshalText(b *testing.B) {
  1976  	for _, test := range parseBenchInputs {
  1977  		ip := MustParseAddr(test.ip)
  1978  		ipp := AddrPortFrom(ip, 60000)
  1979  		b.Run(test.name, func(b *testing.B) {
  1980  			b.ReportAllocs()
  1981  			for i := 0; i < b.N; i++ {
  1982  				sinkBytes, _ = ipp.MarshalText()
  1983  			}
  1984  		})
  1985  	}
  1986  }
  1987  
  1988  func BenchmarkPrefixMasking(b *testing.B) {
  1989  	tests := []struct {
  1990  		name string
  1991  		ip   Addr
  1992  		bits int
  1993  	}{
  1994  		{
  1995  			name: "IPv4 /32",
  1996  			ip:   IPv4(192, 0, 2, 0),
  1997  			bits: 32,
  1998  		},
  1999  		{
  2000  			name: "IPv4 /17",
  2001  			ip:   IPv4(192, 0, 2, 0),
  2002  			bits: 17,
  2003  		},
  2004  		{
  2005  			name: "IPv4 /0",
  2006  			ip:   IPv4(192, 0, 2, 0),
  2007  			bits: 0,
  2008  		},
  2009  		{
  2010  			name: "IPv6 /128",
  2011  			ip:   mustIP("2001:db8::1"),
  2012  			bits: 128,
  2013  		},
  2014  		{
  2015  			name: "IPv6 /65",
  2016  			ip:   mustIP("2001:db8::1"),
  2017  			bits: 65,
  2018  		},
  2019  		{
  2020  			name: "IPv6 /0",
  2021  			ip:   mustIP("2001:db8::1"),
  2022  			bits: 0,
  2023  		},
  2024  		{
  2025  			name: "IPv6 zone /128",
  2026  			ip:   mustIP("2001:db8::1%eth0"),
  2027  			bits: 128,
  2028  		},
  2029  		{
  2030  			name: "IPv6 zone /65",
  2031  			ip:   mustIP("2001:db8::1%eth0"),
  2032  			bits: 65,
  2033  		},
  2034  		{
  2035  			name: "IPv6 zone /0",
  2036  			ip:   mustIP("2001:db8::1%eth0"),
  2037  			bits: 0,
  2038  		},
  2039  	}
  2040  
  2041  	for _, tt := range tests {
  2042  		b.Run(tt.name, func(b *testing.B) {
  2043  			b.ReportAllocs()
  2044  
  2045  			for i := 0; i < b.N; i++ {
  2046  				sinkPrefix, _ = tt.ip.Prefix(tt.bits)
  2047  			}
  2048  		})
  2049  	}
  2050  }
  2051  
  2052  func BenchmarkPrefixMarshalText(b *testing.B) {
  2053  	b.ReportAllocs()
  2054  	ipp := MustParsePrefix("66.55.44.33/22")
  2055  	for i := 0; i < b.N; i++ {
  2056  		sinkBytes, _ = ipp.MarshalText()
  2057  	}
  2058  }
  2059  
  2060  func BenchmarkParseAddrPort(b *testing.B) {
  2061  	for _, test := range parseBenchInputs {
  2062  		var ipp string
  2063  		if strings.HasPrefix(test.name, "v6") {
  2064  			ipp = fmt.Sprintf("[%s]:1234", test.ip)
  2065  		} else {
  2066  			ipp = fmt.Sprintf("%s:1234", test.ip)
  2067  		}
  2068  		b.Run(test.name, func(b *testing.B) {
  2069  			b.ReportAllocs()
  2070  
  2071  			for i := 0; i < b.N; i++ {
  2072  				sinkAddrPort, _ = ParseAddrPort(ipp)
  2073  			}
  2074  		})
  2075  	}
  2076  }
  2077  
  2078  func TestAs4(t *testing.T) {
  2079  	tests := []struct {
  2080  		ip        Addr
  2081  		want      [4]byte
  2082  		wantPanic bool
  2083  	}{
  2084  		{
  2085  			ip:   mustIP("1.2.3.4"),
  2086  			want: [4]byte{1, 2, 3, 4},
  2087  		},
  2088  		{
  2089  			ip:   AddrFrom16(mustIP("1.2.3.4").As16()), // IPv4-in-IPv6
  2090  			want: [4]byte{1, 2, 3, 4},
  2091  		},
  2092  		{
  2093  			ip:   mustIP("0.0.0.0"),
  2094  			want: [4]byte{0, 0, 0, 0},
  2095  		},
  2096  		{
  2097  			ip:        Addr{},
  2098  			wantPanic: true,
  2099  		},
  2100  		{
  2101  			ip:        mustIP("::1"),
  2102  			wantPanic: true,
  2103  		},
  2104  	}
  2105  	as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
  2106  		defer func() {
  2107  			if recover() != nil {
  2108  				gotPanic = true
  2109  				return
  2110  			}
  2111  		}()
  2112  		v = ip.As4()
  2113  		return
  2114  	}
  2115  	for i, tt := range tests {
  2116  		got, gotPanic := as4(tt.ip)
  2117  		if gotPanic != tt.wantPanic {
  2118  			t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
  2119  			continue
  2120  		}
  2121  		if got != tt.want {
  2122  			t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
  2123  		}
  2124  	}
  2125  }
  2126  
  2127  func TestPrefixOverlaps(t *testing.T) {
  2128  	pfx := mustPrefix
  2129  	tests := []struct {
  2130  		a, b Prefix
  2131  		want bool
  2132  	}{
  2133  		{Prefix{}, pfx("1.2.0.0/16"), false},    // first zero
  2134  		{pfx("1.2.0.0/16"), Prefix{}, false},    // second zero
  2135  		{pfx("::0/3"), pfx("0.0.0.0/3"), false}, // different families
  2136  
  2137  		{pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true}, // equal
  2138  
  2139  		{pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
  2140  		{pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
  2141  
  2142  		{pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
  2143  		{pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
  2144  
  2145  		// Match /0 either order
  2146  		{pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
  2147  		{pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
  2148  
  2149  		{pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true}, // normalization not required; /0 means true
  2150  
  2151  		// IPv6 overlapping
  2152  		{pfx("5::1/128"), pfx("5::0/8"), true},
  2153  		{pfx("5::0/8"), pfx("5::1/128"), true},
  2154  
  2155  		// IPv6 not overlapping
  2156  		{pfx("1::1/128"), pfx("2::2/128"), false},
  2157  		{pfx("0100::0/8"), pfx("::1/128"), false},
  2158  
  2159  		// IPv4-mapped IPv6 addresses should not overlap with IPv4.
  2160  		{PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
  2161  
  2162  		// Invalid prefixes
  2163  		{PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
  2164  		{PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
  2165  	}
  2166  	for i, tt := range tests {
  2167  		if got := tt.a.Overlaps(tt.b); got != tt.want {
  2168  			t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
  2169  		}
  2170  		// Overlaps is commutative
  2171  		if got := tt.b.Overlaps(tt.a); got != tt.want {
  2172  			t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
  2173  		}
  2174  	}
  2175  }
  2176  
  2177  // Sink variables are here to force the compiler to not elide
  2178  // seemingly useless work in benchmarks and allocation tests. If you
  2179  // were to just `_ = foo()` within a test function, the compiler could
  2180  // correctly deduce that foo() does nothing and doesn't need to be
  2181  // called. By writing results to a global variable, we hide that fact
  2182  // from the compiler and force it to keep the code under test.
  2183  var (
  2184  	sinkIP          Addr
  2185  	sinkStdIP       net.IP
  2186  	sinkAddrPort    AddrPort
  2187  	sinkPrefix      Prefix
  2188  	sinkPrefixSlice []Prefix
  2189  	sinkInternValue unique.Handle[AddrDetail]
  2190  	sinkIP16        [16]byte
  2191  	sinkIP4         [4]byte
  2192  	sinkBool        bool
  2193  	sinkString      string
  2194  	sinkBytes       []byte
  2195  	sinkUDPAddr     = &net.UDPAddr{IP: make(net.IP, 0, 16)}
  2196  )
  2197  
  2198  func TestNoAllocs(t *testing.T) {
  2199  	if asan.Enabled {
  2200  		t.Skip("test allocates more with -asan; see #70079")
  2201  	}
  2202  
  2203  	// Wrappers that panic on error, to prove that our alloc-free
  2204  	// methods are returning successfully.
  2205  	panicIP := func(ip Addr, err error) Addr {
  2206  		if err != nil {
  2207  			panic(err)
  2208  		}
  2209  		return ip
  2210  	}
  2211  	panicPfx := func(pfx Prefix, err error) Prefix {
  2212  		if err != nil {
  2213  			panic(err)
  2214  		}
  2215  		return pfx
  2216  	}
  2217  	panicIPP := func(ipp AddrPort, err error) AddrPort {
  2218  		if err != nil {
  2219  			panic(err)
  2220  		}
  2221  		return ipp
  2222  	}
  2223  	test := func(name string, f func()) {
  2224  		t.Run(name, func(t *testing.T) {
  2225  			n := testing.AllocsPerRun(1000, f)
  2226  			if n != 0 {
  2227  				t.Fatalf("allocs = %d; want 0", int(n))
  2228  			}
  2229  		})
  2230  	}
  2231  
  2232  	// Addr constructors
  2233  	test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
  2234  	test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
  2235  	test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
  2236  	test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
  2237  	test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
  2238  	test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
  2239  	test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
  2240  	test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
  2241  	test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
  2242  	test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
  2243  
  2244  	// Addr methods
  2245  	test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
  2246  	test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
  2247  	test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
  2248  	test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
  2249  	test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
  2250  	test("Addr.Compare", func() {
  2251  		a := MustParseAddr("1.2.3.4")
  2252  		b := MustParseAddr("2.3.4.5")
  2253  		sinkBool = a.Compare(b) == 0
  2254  	})
  2255  	test("Addr.Less", func() {
  2256  		a := MustParseAddr("1.2.3.4")
  2257  		b := MustParseAddr("2.3.4.5")
  2258  		sinkBool = a.Less(b)
  2259  	})
  2260  	test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
  2261  	test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
  2262  	test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
  2263  	test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
  2264  	test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
  2265  	test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
  2266  	test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
  2267  	test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
  2268  	test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
  2269  	test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
  2270  	test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
  2271  	test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
  2272  	test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
  2273  	test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
  2274  	test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
  2275  	test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
  2276  	test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
  2277  	test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
  2278  	test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
  2279  
  2280  	// AddrPort constructors
  2281  	test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
  2282  	test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
  2283  	test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
  2284  
  2285  	// Prefix constructors
  2286  	test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
  2287  	test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
  2288  	test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
  2289  	test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
  2290  
  2291  	// Prefix methods
  2292  	test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
  2293  	test("Prefix.Overlaps", func() {
  2294  		a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
  2295  		sinkBool = a.Overlaps(b)
  2296  	})
  2297  	test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
  2298  	test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
  2299  	test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
  2300  }
  2301  
  2302  func TestAddrStringAllocs(t *testing.T) {
  2303  	tests := []struct {
  2304  		name       string
  2305  		ip         Addr
  2306  		wantAllocs int
  2307  	}{
  2308  		{"zero", Addr{}, 0},
  2309  		{"ipv4", MustParseAddr("192.168.1.1"), 1},
  2310  		{"ipv6", MustParseAddr("2001:db8::1"), 1},
  2311  		{"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
  2312  		{"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
  2313  		{"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
  2314  	}
  2315  	optimizationOff := testenv.OptimizationOff()
  2316  	for _, tc := range tests {
  2317  		t.Run(tc.name, func(t *testing.T) {
  2318  			if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
  2319  				// Optimizations are required to remove some allocs.
  2320  				t.Skipf("skipping on %v", testenv.Builder())
  2321  			}
  2322  			allocs := int(testing.AllocsPerRun(1000, func() {
  2323  				sinkString = tc.ip.String()
  2324  			}))
  2325  			if allocs != tc.wantAllocs {
  2326  				t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
  2327  			}
  2328  		})
  2329  	}
  2330  }
  2331  
  2332  func TestPrefixString(t *testing.T) {
  2333  	tests := []struct {
  2334  		ipp  Prefix
  2335  		want string
  2336  	}{
  2337  		{Prefix{}, "invalid Prefix"},
  2338  		{PrefixFrom(Addr{}, 8), "invalid Prefix"},
  2339  		{PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
  2340  	}
  2341  
  2342  	for _, tt := range tests {
  2343  		if got := tt.ipp.String(); got != tt.want {
  2344  			t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
  2345  		}
  2346  	}
  2347  }
  2348  
  2349  func TestAddrPortString(t *testing.T) {
  2350  	tests := []struct {
  2351  		ipp  AddrPort
  2352  		want string
  2353  	}{
  2354  		{MustParseAddrPort("127.0.0.1:80"), "127.0.0.1:80"},
  2355  		{MustParseAddrPort("[0000::0]:8080"), "[::]:8080"},
  2356  		{MustParseAddrPort("[FFFF::1]:8080"), "[ffff::1]:8080"},
  2357  		{AddrPort{}, "invalid AddrPort"},
  2358  		{AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
  2359  	}
  2360  
  2361  	for _, tt := range tests {
  2362  		if got := tt.ipp.String(); got != tt.want {
  2363  			t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
  2364  		}
  2365  	}
  2366  }
  2367  
  2368  func TestAsSlice(t *testing.T) {
  2369  	tests := []struct {
  2370  		in   Addr
  2371  		want []byte
  2372  	}{
  2373  		{in: Addr{}, want: nil},
  2374  		{in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
  2375  		{in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
  2376  	}
  2377  
  2378  	for _, test := range tests {
  2379  		got := test.in.AsSlice()
  2380  		if !bytes.Equal(got, test.want) {
  2381  			t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
  2382  		}
  2383  	}
  2384  }
  2385  
  2386  var sink16 [16]byte
  2387  
  2388  func BenchmarkAs16(b *testing.B) {
  2389  	addr := MustParseAddr("1::10")
  2390  	for i := 0; i < b.N; i++ {
  2391  		sink16 = addr.As16()
  2392  	}
  2393  }
  2394  

View as plain text