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  	for _, tt := range tests {
  1128  		got := tt.a.Compare(tt.b)
  1129  		if got != tt.want {
  1130  			t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
  1131  		}
  1132  
  1133  		// Also check inverse.
  1134  		if got == tt.want {
  1135  			got2 := tt.b.Compare(tt.a)
  1136  			if want2 := -1 * tt.want; got2 != want2 {
  1137  				t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
  1138  			}
  1139  		}
  1140  	}
  1141  
  1142  	// And just sort.
  1143  	values := []Prefix{
  1144  		mustPrefix("1.2.3.0/24"),
  1145  		mustPrefix("fe90::/64"),
  1146  		mustPrefix("fe80::/64"),
  1147  		mustPrefix("1.2.0.0/16"),
  1148  		Prefix{},
  1149  		mustPrefix("fe80::/48"),
  1150  		mustPrefix("1.2.0.0/24"),
  1151  	}
  1152  	slices.SortFunc(values, Prefix.Compare)
  1153  	got := fmt.Sprintf("%s", values)
  1154  	want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 fe80::/48 fe80::/64 fe90::/64]`
  1155  	if got != want {
  1156  		t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
  1157  	}
  1158  }
  1159  
  1160  func TestIPStringExpanded(t *testing.T) {
  1161  	tests := []struct {
  1162  		ip Addr
  1163  		s  string
  1164  	}{
  1165  		{
  1166  			ip: Addr{},
  1167  			s:  "invalid IP",
  1168  		},
  1169  		{
  1170  			ip: mustIP("192.0.2.1"),
  1171  			s:  "192.0.2.1",
  1172  		},
  1173  		{
  1174  			ip: mustIP("::ffff:192.0.2.1"),
  1175  			s:  "0000:0000:0000:0000:0000:ffff:c000:0201",
  1176  		},
  1177  		{
  1178  			ip: mustIP("2001:db8::1"),
  1179  			s:  "2001:0db8:0000:0000:0000:0000:0000:0001",
  1180  		},
  1181  		{
  1182  			ip: mustIP("2001:db8::1%eth0"),
  1183  			s:  "2001:0db8:0000:0000:0000:0000:0000:0001%eth0",
  1184  		},
  1185  	}
  1186  
  1187  	for _, tt := range tests {
  1188  		t.Run(tt.ip.String(), func(t *testing.T) {
  1189  			want := tt.s
  1190  			got := tt.ip.StringExpanded()
  1191  
  1192  			if got != want {
  1193  				t.Fatalf("got %s, want %s", got, want)
  1194  			}
  1195  		})
  1196  	}
  1197  }
  1198  
  1199  func TestPrefixMasking(t *testing.T) {
  1200  	type subtest struct {
  1201  		ip   Addr
  1202  		bits uint8
  1203  		p    Prefix
  1204  		ok   bool
  1205  	}
  1206  
  1207  	// makeIPv6 produces a set of IPv6 subtests with an optional zone identifier.
  1208  	makeIPv6 := func(zone string) []subtest {
  1209  		if zone != "" {
  1210  			zone = "%" + zone
  1211  		}
  1212  
  1213  		return []subtest{
  1214  			{
  1215  				ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
  1216  				bits: 255,
  1217  			},
  1218  			{
  1219  				ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
  1220  				bits: 32,
  1221  				p:    mustPrefix("2001:db8::/32"),
  1222  				ok:   true,
  1223  			},
  1224  			{
  1225  				ip:   mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
  1226  				bits: 96,
  1227  				p:    mustPrefix("fe80::dead:beef:0:0/96"),
  1228  				ok:   true,
  1229  			},
  1230  			{
  1231  				ip:   mustIP(fmt.Sprintf("aaaa::%s", zone)),
  1232  				bits: 4,
  1233  				p:    mustPrefix("a000::/4"),
  1234  				ok:   true,
  1235  			},
  1236  			{
  1237  				ip:   mustIP(fmt.Sprintf("::%s", zone)),
  1238  				bits: 63,
  1239  				p:    mustPrefix("::/63"),
  1240  				ok:   true,
  1241  			},
  1242  		}
  1243  	}
  1244  
  1245  	tests := []struct {
  1246  		family   string
  1247  		subtests []subtest
  1248  	}{
  1249  		{
  1250  			family: "nil",
  1251  			subtests: []subtest{
  1252  				{
  1253  					bits: 255,
  1254  					ok:   true,
  1255  				},
  1256  				{
  1257  					bits: 16,
  1258  					ok:   true,
  1259  				},
  1260  			},
  1261  		},
  1262  		{
  1263  			family: "IPv4",
  1264  			subtests: []subtest{
  1265  				{
  1266  					ip:   mustIP("192.0.2.0"),
  1267  					bits: 255,
  1268  				},
  1269  				{
  1270  					ip:   mustIP("192.0.2.0"),
  1271  					bits: 16,
  1272  					p:    mustPrefix("192.0.0.0/16"),
  1273  					ok:   true,
  1274  				},
  1275  				{
  1276  					ip:   mustIP("255.255.255.255"),
  1277  					bits: 20,
  1278  					p:    mustPrefix("255.255.240.0/20"),
  1279  					ok:   true,
  1280  				},
  1281  				{
  1282  					// Partially masking one byte that contains both
  1283  					// 1s and 0s on either side of the mask limit.
  1284  					ip:   mustIP("100.98.156.66"),
  1285  					bits: 10,
  1286  					p:    mustPrefix("100.64.0.0/10"),
  1287  					ok:   true,
  1288  				},
  1289  			},
  1290  		},
  1291  		{
  1292  			family:   "IPv6",
  1293  			subtests: makeIPv6(""),
  1294  		},
  1295  		{
  1296  			family:   "IPv6 zone",
  1297  			subtests: makeIPv6("eth0"),
  1298  		},
  1299  	}
  1300  
  1301  	for _, tt := range tests {
  1302  		t.Run(tt.family, func(t *testing.T) {
  1303  			for _, st := range tt.subtests {
  1304  				t.Run(st.p.String(), func(t *testing.T) {
  1305  					// Ensure st.ip is not mutated.
  1306  					orig := st.ip.String()
  1307  
  1308  					p, err := st.ip.Prefix(int(st.bits))
  1309  					if st.ok && err != nil {
  1310  						t.Fatalf("failed to produce prefix: %v", err)
  1311  					}
  1312  					if !st.ok && err == nil {
  1313  						t.Fatal("expected an error, but none occurred")
  1314  					}
  1315  					if err != nil {
  1316  						t.Logf("err: %v", err)
  1317  						return
  1318  					}
  1319  
  1320  					if !reflect.DeepEqual(p, st.p) {
  1321  						t.Errorf("prefix = %q, want %q", p, st.p)
  1322  					}
  1323  
  1324  					if got := st.ip.String(); got != orig {
  1325  						t.Errorf("IP was mutated: %q, want %q", got, orig)
  1326  					}
  1327  				})
  1328  			}
  1329  		})
  1330  	}
  1331  }
  1332  
  1333  func TestPrefixMarshalUnmarshal(t *testing.T) {
  1334  	tests := []string{
  1335  		"",
  1336  		"1.2.3.4/32",
  1337  		"0.0.0.0/0",
  1338  		"::/0",
  1339  		"::1/128",
  1340  		"2001:db8::/32",
  1341  	}
  1342  
  1343  	for _, s := range tests {
  1344  		t.Run(s, func(t *testing.T) {
  1345  			// Ensure that JSON  (and by extension, text) marshaling is
  1346  			// sane by entering quoted input.
  1347  			orig := `"` + s + `"`
  1348  
  1349  			var p Prefix
  1350  			if err := json.Unmarshal([]byte(orig), &p); err != nil {
  1351  				t.Fatalf("failed to unmarshal: %v", err)
  1352  			}
  1353  
  1354  			pb, err := json.Marshal(p)
  1355  			if err != nil {
  1356  				t.Fatalf("failed to marshal: %v", err)
  1357  			}
  1358  
  1359  			back := string(pb)
  1360  			if orig != back {
  1361  				t.Errorf("Marshal = %q; want %q", back, orig)
  1362  			}
  1363  		})
  1364  	}
  1365  }
  1366  
  1367  func TestPrefixUnmarshalTextNonZero(t *testing.T) {
  1368  	ip := mustPrefix("fe80::/64")
  1369  	if err := ip.UnmarshalText([]byte("xxx")); err == nil {
  1370  		t.Fatal("unmarshaled into non-empty Prefix")
  1371  	}
  1372  }
  1373  
  1374  func TestIs4AndIs6(t *testing.T) {
  1375  	tests := []struct {
  1376  		ip  Addr
  1377  		is4 bool
  1378  		is6 bool
  1379  	}{
  1380  		{Addr{}, false, false},
  1381  		{mustIP("1.2.3.4"), true, false},
  1382  		{mustIP("127.0.0.2"), true, false},
  1383  		{mustIP("::1"), false, true},
  1384  		{mustIP("::ffff:192.0.2.128"), false, true},
  1385  		{mustIP("::fffe:c000:0280"), false, true},
  1386  		{mustIP("::1%eth0"), false, true},
  1387  	}
  1388  	for _, tt := range tests {
  1389  		got4 := tt.ip.Is4()
  1390  		if got4 != tt.is4 {
  1391  			t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4)
  1392  		}
  1393  
  1394  		got6 := tt.ip.Is6()
  1395  		if got6 != tt.is6 {
  1396  			t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6)
  1397  		}
  1398  	}
  1399  }
  1400  
  1401  func TestIs4In6(t *testing.T) {
  1402  	tests := []struct {
  1403  		ip        Addr
  1404  		want      bool
  1405  		wantUnmap Addr
  1406  	}{
  1407  		{Addr{}, false, Addr{}},
  1408  		{mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")},
  1409  		{mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")},
  1410  		{mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")},
  1411  		{mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")},
  1412  		{mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
  1413  		{mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")},
  1414  		{mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
  1415  		{mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
  1416  		{mustIP("::1"), false, mustIP("::1")},
  1417  		{mustIP("1.2.3.4"), false, mustIP("1.2.3.4")},
  1418  	}
  1419  	for _, tt := range tests {
  1420  		got := tt.ip.Is4In6()
  1421  		if got != tt.want {
  1422  			t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want)
  1423  		}
  1424  		u := tt.ip.Unmap()
  1425  		if u != tt.wantUnmap {
  1426  			t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap)
  1427  		}
  1428  	}
  1429  }
  1430  
  1431  func TestPrefixMasked(t *testing.T) {
  1432  	tests := []struct {
  1433  		prefix Prefix
  1434  		masked Prefix
  1435  	}{
  1436  		{
  1437  			prefix: mustPrefix("192.168.0.255/24"),
  1438  			masked: mustPrefix("192.168.0.0/24"),
  1439  		},
  1440  		{
  1441  			prefix: mustPrefix("2100::/3"),
  1442  			masked: mustPrefix("2000::/3"),
  1443  		},
  1444  		{
  1445  			prefix: PrefixFrom(mustIP("2000::"), 129),
  1446  			masked: Prefix{},
  1447  		},
  1448  		{
  1449  			prefix: PrefixFrom(mustIP("1.2.3.4"), 33),
  1450  			masked: Prefix{},
  1451  		},
  1452  	}
  1453  	for _, test := range tests {
  1454  		t.Run(test.prefix.String(), func(t *testing.T) {
  1455  			got := test.prefix.Masked()
  1456  			if got != test.masked {
  1457  				t.Errorf("Masked=%s, want %s", got, test.masked)
  1458  			}
  1459  		})
  1460  	}
  1461  }
  1462  
  1463  func TestPrefix(t *testing.T) {
  1464  	tests := []struct {
  1465  		prefix      string
  1466  		ip          Addr
  1467  		bits        int
  1468  		str         string
  1469  		contains    []Addr
  1470  		notContains []Addr
  1471  	}{
  1472  		{
  1473  			prefix:      "192.168.0.0/24",
  1474  			ip:          mustIP("192.168.0.0"),
  1475  			bits:        24,
  1476  			contains:    mustIPs("192.168.0.1", "192.168.0.55"),
  1477  			notContains: mustIPs("192.168.1.1", "1.1.1.1"),
  1478  		},
  1479  		{
  1480  			prefix:      "192.168.1.1/32",
  1481  			ip:          mustIP("192.168.1.1"),
  1482  			bits:        32,
  1483  			contains:    mustIPs("192.168.1.1"),
  1484  			notContains: mustIPs("192.168.1.2"),
  1485  		},
  1486  		{
  1487  			prefix:      "100.64.0.0/10", // CGNAT range; prefix not multiple of 8
  1488  			ip:          mustIP("100.64.0.0"),
  1489  			bits:        10,
  1490  			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"),
  1491  			notContains: mustIPs("100.63.255.255", "100.128.0.0"),
  1492  		},
  1493  		{
  1494  			prefix:      "2001:db8::/96",
  1495  			ip:          mustIP("2001:db8::"),
  1496  			bits:        96,
  1497  			contains:    mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"),
  1498  			notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"),
  1499  		},
  1500  		{
  1501  			prefix:      "0.0.0.0/0",
  1502  			ip:          mustIP("0.0.0.0"),
  1503  			bits:        0,
  1504  			contains:    mustIPs("192.168.0.1", "1.1.1.1"),
  1505  			notContains: append(mustIPs("2001:db8::1"), Addr{}),
  1506  		},
  1507  		{
  1508  			prefix:      "::/0",
  1509  			ip:          mustIP("::"),
  1510  			bits:        0,
  1511  			contains:    mustIPs("::1", "2001:db8::1"),
  1512  			notContains: mustIPs("192.0.2.1"),
  1513  		},
  1514  		{
  1515  			prefix:      "2000::/3",
  1516  			ip:          mustIP("2000::"),
  1517  			bits:        3,
  1518  			contains:    mustIPs("2001:db8::1"),
  1519  			notContains: mustIPs("fe80::1"),
  1520  		},
  1521  	}
  1522  	for _, test := range tests {
  1523  		t.Run(test.prefix, func(t *testing.T) {
  1524  			prefix, err := ParsePrefix(test.prefix)
  1525  			if err != nil {
  1526  				t.Fatal(err)
  1527  			}
  1528  			if prefix.Addr() != test.ip {
  1529  				t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip)
  1530  			}
  1531  			if prefix.Bits() != test.bits {
  1532  				t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits)
  1533  			}
  1534  			for _, ip := range test.contains {
  1535  				if !prefix.Contains(ip) {
  1536  					t.Errorf("does not contain %s", ip)
  1537  				}
  1538  			}
  1539  			for _, ip := range test.notContains {
  1540  				if prefix.Contains(ip) {
  1541  					t.Errorf("contains %s", ip)
  1542  				}
  1543  			}
  1544  			want := test.str
  1545  			if want == "" {
  1546  				want = test.prefix
  1547  			}
  1548  			if got := prefix.String(); got != want {
  1549  				t.Errorf("prefix.String()=%q, want %q", got, want)
  1550  			}
  1551  
  1552  			TestAppendToMarshal(t, prefix)
  1553  		})
  1554  	}
  1555  }
  1556  
  1557  func TestPrefixFromInvalidBits(t *testing.T) {
  1558  	v4 := MustParseAddr("1.2.3.4")
  1559  	v6 := MustParseAddr("66::66")
  1560  	tests := []struct {
  1561  		ip       Addr
  1562  		in, want int
  1563  	}{
  1564  		{v4, 0, 0},
  1565  		{v6, 0, 0},
  1566  		{v4, 1, 1},
  1567  		{v4, 33, -1},
  1568  		{v6, 33, 33},
  1569  		{v6, 127, 127},
  1570  		{v6, 128, 128},
  1571  		{v4, 254, -1},
  1572  		{v4, 255, -1},
  1573  		{v4, -1, -1},
  1574  		{v6, -1, -1},
  1575  		{v4, -5, -1},
  1576  		{v6, -5, -1},
  1577  	}
  1578  	for _, tt := range tests {
  1579  		p := PrefixFrom(tt.ip, tt.in)
  1580  		if got := p.Bits(); got != tt.want {
  1581  			t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want)
  1582  		}
  1583  	}
  1584  }
  1585  
  1586  func TestParsePrefixAllocs(t *testing.T) {
  1587  	tests := []struct {
  1588  		ip    string
  1589  		slash string
  1590  	}{
  1591  		{"192.168.1.0", "/24"},
  1592  		{"aaaa:bbbb:cccc::", "/24"},
  1593  	}
  1594  	for _, test := range tests {
  1595  		prefix := test.ip + test.slash
  1596  		t.Run(prefix, func(t *testing.T) {
  1597  			ipAllocs := int(testing.AllocsPerRun(5, func() {
  1598  				ParseAddr(test.ip)
  1599  			}))
  1600  			prefixAllocs := int(testing.AllocsPerRun(5, func() {
  1601  				ParsePrefix(prefix)
  1602  			}))
  1603  			if got := prefixAllocs - ipAllocs; got != 0 {
  1604  				t.Errorf("allocs=%d, want 0", got)
  1605  			}
  1606  		})
  1607  	}
  1608  }
  1609  
  1610  func TestParsePrefixError(t *testing.T) {
  1611  	tests := []struct {
  1612  		prefix string
  1613  		errstr string
  1614  	}{
  1615  		{
  1616  			prefix: "192.168.0.0",
  1617  			errstr: "no '/'",
  1618  		},
  1619  		{
  1620  			prefix: "1.257.1.1/24",
  1621  			errstr: "value >255",
  1622  		},
  1623  		{
  1624  			prefix: "1.1.1.0/q",
  1625  			errstr: "bad bits",
  1626  		},
  1627  		{
  1628  			prefix: "1.1.1.0/-1",
  1629  			errstr: "bad bits",
  1630  		},
  1631  		{
  1632  			prefix: "1.1.1.0/33",
  1633  			errstr: "out of range",
  1634  		},
  1635  		{
  1636  			prefix: "2001::/129",
  1637  			errstr: "out of range",
  1638  		},
  1639  		// Zones are not allowed: https://go.dev/issue/51899
  1640  		{
  1641  			prefix: "1.1.1.0%a/24",
  1642  			errstr: "unexpected character",
  1643  		},
  1644  		{
  1645  			prefix: "2001:db8::%a/32",
  1646  			errstr: "zones cannot be present",
  1647  		},
  1648  		{
  1649  			prefix: "1.1.1.0/+32",
  1650  			errstr: "bad bits",
  1651  		},
  1652  		{
  1653  			prefix: "1.1.1.0/-32",
  1654  			errstr: "bad bits",
  1655  		},
  1656  		{
  1657  			prefix: "1.1.1.0/032",
  1658  			errstr: "bad bits",
  1659  		},
  1660  		{
  1661  			prefix: "1.1.1.0/0032",
  1662  			errstr: "bad bits",
  1663  		},
  1664  	}
  1665  	for _, test := range tests {
  1666  		t.Run(test.prefix, func(t *testing.T) {
  1667  			_, err := ParsePrefix(test.prefix)
  1668  			if err == nil {
  1669  				t.Fatal("no error")
  1670  			}
  1671  			if got := err.Error(); !strings.Contains(got, test.errstr) {
  1672  				t.Errorf("error is missing substring %q: %s", test.errstr, got)
  1673  			}
  1674  		})
  1675  	}
  1676  }
  1677  
  1678  func TestPrefixIsSingleIP(t *testing.T) {
  1679  	tests := []struct {
  1680  		ipp  Prefix
  1681  		want bool
  1682  	}{
  1683  		{ipp: mustPrefix("127.0.0.1/32"), want: true},
  1684  		{ipp: mustPrefix("127.0.0.1/31"), want: false},
  1685  		{ipp: mustPrefix("127.0.0.1/0"), want: false},
  1686  		{ipp: mustPrefix("::1/128"), want: true},
  1687  		{ipp: mustPrefix("::1/127"), want: false},
  1688  		{ipp: mustPrefix("::1/0"), want: false},
  1689  		{ipp: Prefix{}, want: false},
  1690  	}
  1691  	for _, tt := range tests {
  1692  		got := tt.ipp.IsSingleIP()
  1693  		if got != tt.want {
  1694  			t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want)
  1695  		}
  1696  	}
  1697  }
  1698  
  1699  func mustIPs(strs ...string) []Addr {
  1700  	var res []Addr
  1701  	for _, s := range strs {
  1702  		res = append(res, mustIP(s))
  1703  	}
  1704  	return res
  1705  }
  1706  
  1707  func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
  1708  	b.ReportAllocs()
  1709  	tests := []struct {
  1710  		name string
  1711  		ip   string
  1712  	}{
  1713  		{"ipv4", "1.2.3.4"},
  1714  		{"ipv6", "2001:db8::1"},
  1715  		{"ipv6+zone", "2001:db8::1%eth0"},
  1716  	}
  1717  	for _, tc := range tests {
  1718  		b.Run(tc.name, func(b *testing.B) {
  1719  			ip := mustIP(tc.ip)
  1720  			for i := 0; i < b.N; i++ {
  1721  				bt, err := ip.MarshalBinary()
  1722  				if err != nil {
  1723  					b.Fatal(err)
  1724  				}
  1725  				var ip2 Addr
  1726  				if err := ip2.UnmarshalBinary(bt); err != nil {
  1727  					b.Fatal(err)
  1728  				}
  1729  			}
  1730  		})
  1731  	}
  1732  }
  1733  
  1734  func BenchmarkStdIPv4(b *testing.B) {
  1735  	b.ReportAllocs()
  1736  	ips := []net.IP{}
  1737  	for i := 0; i < b.N; i++ {
  1738  		ip := net.IPv4(8, 8, 8, 8)
  1739  		ips = ips[:0]
  1740  		for i := 0; i < 100; i++ {
  1741  			ips = append(ips, ip)
  1742  		}
  1743  	}
  1744  }
  1745  
  1746  func BenchmarkIPv4(b *testing.B) {
  1747  	b.ReportAllocs()
  1748  	ips := []Addr{}
  1749  	for i := 0; i < b.N; i++ {
  1750  		ip := IPv4(8, 8, 8, 8)
  1751  		ips = ips[:0]
  1752  		for i := 0; i < 100; i++ {
  1753  			ips = append(ips, ip)
  1754  		}
  1755  	}
  1756  }
  1757  
  1758  // ip4i was one of the possible representations of IP that came up in
  1759  // discussions, inlining IPv4 addresses, but having an "overflow"
  1760  // interface for IPv6 or IPv6 + zone. This is here for benchmarking.
  1761  type ip4i struct {
  1762  	ip4    [4]byte
  1763  	flags1 byte
  1764  	flags2 byte
  1765  	flags3 byte
  1766  	flags4 byte
  1767  	ipv6   any
  1768  }
  1769  
  1770  func newip4i_v4(a, b, c, d byte) ip4i {
  1771  	return ip4i{ip4: [4]byte{a, b, c, d}}
  1772  }
  1773  
  1774  // BenchmarkIPv4_inline benchmarks the candidate representation, ip4i.
  1775  func BenchmarkIPv4_inline(b *testing.B) {
  1776  	b.ReportAllocs()
  1777  	ips := []ip4i{}
  1778  	for i := 0; i < b.N; i++ {
  1779  		ip := newip4i_v4(8, 8, 8, 8)
  1780  		ips = ips[:0]
  1781  		for i := 0; i < 100; i++ {
  1782  			ips = append(ips, ip)
  1783  		}
  1784  	}
  1785  }
  1786  
  1787  func BenchmarkStdIPv6(b *testing.B) {
  1788  	b.ReportAllocs()
  1789  	ips := []net.IP{}
  1790  	for i := 0; i < b.N; i++ {
  1791  		ip := net.ParseIP("2001:db8::1")
  1792  		ips = ips[:0]
  1793  		for i := 0; i < 100; i++ {
  1794  			ips = append(ips, ip)
  1795  		}
  1796  	}
  1797  }
  1798  
  1799  func BenchmarkIPv6(b *testing.B) {
  1800  	b.ReportAllocs()
  1801  	ips := []Addr{}
  1802  	for i := 0; i < b.N; i++ {
  1803  		ip := mustIP("2001:db8::1")
  1804  		ips = ips[:0]
  1805  		for i := 0; i < 100; i++ {
  1806  			ips = append(ips, ip)
  1807  		}
  1808  	}
  1809  }
  1810  
  1811  func BenchmarkIPv4Contains(b *testing.B) {
  1812  	b.ReportAllocs()
  1813  	prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24)
  1814  	ip := IPv4(192, 168, 1, 1)
  1815  	for i := 0; i < b.N; i++ {
  1816  		prefix.Contains(ip)
  1817  	}
  1818  }
  1819  
  1820  func BenchmarkIPv6Contains(b *testing.B) {
  1821  	b.ReportAllocs()
  1822  	prefix := MustParsePrefix("::1/128")
  1823  	ip := MustParseAddr("::1")
  1824  	for i := 0; i < b.N; i++ {
  1825  		prefix.Contains(ip)
  1826  	}
  1827  }
  1828  
  1829  var parseBenchInputs = []struct {
  1830  	name string
  1831  	ip   string
  1832  }{
  1833  	{"v4", "192.168.1.1"},
  1834  	{"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
  1835  	{"v6_ellipsis", "fd7a:115c::626b:430b"},
  1836  	{"v6_v4", "::ffff:192.168.140.255"},
  1837  	{"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
  1838  }
  1839  
  1840  func BenchmarkParseAddr(b *testing.B) {
  1841  	sinkInternValue = unique.Make(MakeAddrDetail(true, "eth1")) // Pin to not benchmark the intern package
  1842  	for _, test := range parseBenchInputs {
  1843  		b.Run(test.name, func(b *testing.B) {
  1844  			b.ReportAllocs()
  1845  			for i := 0; i < b.N; i++ {
  1846  				sinkIP, _ = ParseAddr(test.ip)
  1847  			}
  1848  		})
  1849  	}
  1850  }
  1851  
  1852  func BenchmarkStdParseIP(b *testing.B) {
  1853  	for _, test := range parseBenchInputs {
  1854  		b.Run(test.name, func(b *testing.B) {
  1855  			b.ReportAllocs()
  1856  			for i := 0; i < b.N; i++ {
  1857  				sinkStdIP = net.ParseIP(test.ip)
  1858  			}
  1859  		})
  1860  	}
  1861  }
  1862  
  1863  func BenchmarkAddrString(b *testing.B) {
  1864  	for _, test := range parseBenchInputs {
  1865  		ip := MustParseAddr(test.ip)
  1866  		b.Run(test.name, func(b *testing.B) {
  1867  			b.ReportAllocs()
  1868  			for i := 0; i < b.N; i++ {
  1869  				sinkString = ip.String()
  1870  			}
  1871  		})
  1872  	}
  1873  }
  1874  
  1875  func BenchmarkIPStringExpanded(b *testing.B) {
  1876  	for _, test := range parseBenchInputs {
  1877  		ip := MustParseAddr(test.ip)
  1878  		b.Run(test.name, func(b *testing.B) {
  1879  			b.ReportAllocs()
  1880  			for i := 0; i < b.N; i++ {
  1881  				sinkString = ip.StringExpanded()
  1882  			}
  1883  		})
  1884  	}
  1885  }
  1886  
  1887  func BenchmarkAddrMarshalText(b *testing.B) {
  1888  	for _, test := range parseBenchInputs {
  1889  		ip := MustParseAddr(test.ip)
  1890  		b.Run(test.name, func(b *testing.B) {
  1891  			b.ReportAllocs()
  1892  			for i := 0; i < b.N; i++ {
  1893  				sinkBytes, _ = ip.MarshalText()
  1894  			}
  1895  		})
  1896  	}
  1897  }
  1898  
  1899  func BenchmarkAddrPortString(b *testing.B) {
  1900  	for _, test := range parseBenchInputs {
  1901  		ip := MustParseAddr(test.ip)
  1902  		ipp := AddrPortFrom(ip, 60000)
  1903  		b.Run(test.name, func(b *testing.B) {
  1904  			b.ReportAllocs()
  1905  			for i := 0; i < b.N; i++ {
  1906  				sinkString = ipp.String()
  1907  			}
  1908  		})
  1909  	}
  1910  }
  1911  
  1912  func BenchmarkAddrPortMarshalText(b *testing.B) {
  1913  	for _, test := range parseBenchInputs {
  1914  		ip := MustParseAddr(test.ip)
  1915  		ipp := AddrPortFrom(ip, 60000)
  1916  		b.Run(test.name, func(b *testing.B) {
  1917  			b.ReportAllocs()
  1918  			for i := 0; i < b.N; i++ {
  1919  				sinkBytes, _ = ipp.MarshalText()
  1920  			}
  1921  		})
  1922  	}
  1923  }
  1924  
  1925  func BenchmarkPrefixMasking(b *testing.B) {
  1926  	tests := []struct {
  1927  		name string
  1928  		ip   Addr
  1929  		bits int
  1930  	}{
  1931  		{
  1932  			name: "IPv4 /32",
  1933  			ip:   IPv4(192, 0, 2, 0),
  1934  			bits: 32,
  1935  		},
  1936  		{
  1937  			name: "IPv4 /17",
  1938  			ip:   IPv4(192, 0, 2, 0),
  1939  			bits: 17,
  1940  		},
  1941  		{
  1942  			name: "IPv4 /0",
  1943  			ip:   IPv4(192, 0, 2, 0),
  1944  			bits: 0,
  1945  		},
  1946  		{
  1947  			name: "IPv6 /128",
  1948  			ip:   mustIP("2001:db8::1"),
  1949  			bits: 128,
  1950  		},
  1951  		{
  1952  			name: "IPv6 /65",
  1953  			ip:   mustIP("2001:db8::1"),
  1954  			bits: 65,
  1955  		},
  1956  		{
  1957  			name: "IPv6 /0",
  1958  			ip:   mustIP("2001:db8::1"),
  1959  			bits: 0,
  1960  		},
  1961  		{
  1962  			name: "IPv6 zone /128",
  1963  			ip:   mustIP("2001:db8::1%eth0"),
  1964  			bits: 128,
  1965  		},
  1966  		{
  1967  			name: "IPv6 zone /65",
  1968  			ip:   mustIP("2001:db8::1%eth0"),
  1969  			bits: 65,
  1970  		},
  1971  		{
  1972  			name: "IPv6 zone /0",
  1973  			ip:   mustIP("2001:db8::1%eth0"),
  1974  			bits: 0,
  1975  		},
  1976  	}
  1977  
  1978  	for _, tt := range tests {
  1979  		b.Run(tt.name, func(b *testing.B) {
  1980  			b.ReportAllocs()
  1981  
  1982  			for i := 0; i < b.N; i++ {
  1983  				sinkPrefix, _ = tt.ip.Prefix(tt.bits)
  1984  			}
  1985  		})
  1986  	}
  1987  }
  1988  
  1989  func BenchmarkPrefixMarshalText(b *testing.B) {
  1990  	b.ReportAllocs()
  1991  	ipp := MustParsePrefix("66.55.44.33/22")
  1992  	for i := 0; i < b.N; i++ {
  1993  		sinkBytes, _ = ipp.MarshalText()
  1994  	}
  1995  }
  1996  
  1997  func BenchmarkParseAddrPort(b *testing.B) {
  1998  	for _, test := range parseBenchInputs {
  1999  		var ipp string
  2000  		if strings.HasPrefix(test.name, "v6") {
  2001  			ipp = fmt.Sprintf("[%s]:1234", test.ip)
  2002  		} else {
  2003  			ipp = fmt.Sprintf("%s:1234", test.ip)
  2004  		}
  2005  		b.Run(test.name, func(b *testing.B) {
  2006  			b.ReportAllocs()
  2007  
  2008  			for i := 0; i < b.N; i++ {
  2009  				sinkAddrPort, _ = ParseAddrPort(ipp)
  2010  			}
  2011  		})
  2012  	}
  2013  }
  2014  
  2015  func TestAs4(t *testing.T) {
  2016  	tests := []struct {
  2017  		ip        Addr
  2018  		want      [4]byte
  2019  		wantPanic bool
  2020  	}{
  2021  		{
  2022  			ip:   mustIP("1.2.3.4"),
  2023  			want: [4]byte{1, 2, 3, 4},
  2024  		},
  2025  		{
  2026  			ip:   AddrFrom16(mustIP("1.2.3.4").As16()), // IPv4-in-IPv6
  2027  			want: [4]byte{1, 2, 3, 4},
  2028  		},
  2029  		{
  2030  			ip:   mustIP("0.0.0.0"),
  2031  			want: [4]byte{0, 0, 0, 0},
  2032  		},
  2033  		{
  2034  			ip:        Addr{},
  2035  			wantPanic: true,
  2036  		},
  2037  		{
  2038  			ip:        mustIP("::1"),
  2039  			wantPanic: true,
  2040  		},
  2041  	}
  2042  	as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
  2043  		defer func() {
  2044  			if recover() != nil {
  2045  				gotPanic = true
  2046  				return
  2047  			}
  2048  		}()
  2049  		v = ip.As4()
  2050  		return
  2051  	}
  2052  	for i, tt := range tests {
  2053  		got, gotPanic := as4(tt.ip)
  2054  		if gotPanic != tt.wantPanic {
  2055  			t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
  2056  			continue
  2057  		}
  2058  		if got != tt.want {
  2059  			t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
  2060  		}
  2061  	}
  2062  }
  2063  
  2064  func TestPrefixOverlaps(t *testing.T) {
  2065  	pfx := mustPrefix
  2066  	tests := []struct {
  2067  		a, b Prefix
  2068  		want bool
  2069  	}{
  2070  		{Prefix{}, pfx("1.2.0.0/16"), false},    // first zero
  2071  		{pfx("1.2.0.0/16"), Prefix{}, false},    // second zero
  2072  		{pfx("::0/3"), pfx("0.0.0.0/3"), false}, // different families
  2073  
  2074  		{pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true}, // equal
  2075  
  2076  		{pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
  2077  		{pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
  2078  
  2079  		{pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
  2080  		{pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
  2081  
  2082  		// Match /0 either order
  2083  		{pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
  2084  		{pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
  2085  
  2086  		{pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true}, // normalization not required; /0 means true
  2087  
  2088  		// IPv6 overlapping
  2089  		{pfx("5::1/128"), pfx("5::0/8"), true},
  2090  		{pfx("5::0/8"), pfx("5::1/128"), true},
  2091  
  2092  		// IPv6 not overlapping
  2093  		{pfx("1::1/128"), pfx("2::2/128"), false},
  2094  		{pfx("0100::0/8"), pfx("::1/128"), false},
  2095  
  2096  		// IPv4-mapped IPv6 addresses should not overlap with IPv4.
  2097  		{PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
  2098  
  2099  		// Invalid prefixes
  2100  		{PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
  2101  		{PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
  2102  	}
  2103  	for i, tt := range tests {
  2104  		if got := tt.a.Overlaps(tt.b); got != tt.want {
  2105  			t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
  2106  		}
  2107  		// Overlaps is commutative
  2108  		if got := tt.b.Overlaps(tt.a); got != tt.want {
  2109  			t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
  2110  		}
  2111  	}
  2112  }
  2113  
  2114  // Sink variables are here to force the compiler to not elide
  2115  // seemingly useless work in benchmarks and allocation tests. If you
  2116  // were to just `_ = foo()` within a test function, the compiler could
  2117  // correctly deduce that foo() does nothing and doesn't need to be
  2118  // called. By writing results to a global variable, we hide that fact
  2119  // from the compiler and force it to keep the code under test.
  2120  var (
  2121  	sinkIP          Addr
  2122  	sinkStdIP       net.IP
  2123  	sinkAddrPort    AddrPort
  2124  	sinkPrefix      Prefix
  2125  	sinkPrefixSlice []Prefix
  2126  	sinkInternValue unique.Handle[AddrDetail]
  2127  	sinkIP16        [16]byte
  2128  	sinkIP4         [4]byte
  2129  	sinkBool        bool
  2130  	sinkString      string
  2131  	sinkBytes       []byte
  2132  	sinkUDPAddr     = &net.UDPAddr{IP: make(net.IP, 0, 16)}
  2133  )
  2134  
  2135  func TestNoAllocs(t *testing.T) {
  2136  	if asan.Enabled {
  2137  		t.Skip("test allocates more with -asan; see #70079")
  2138  	}
  2139  
  2140  	// Wrappers that panic on error, to prove that our alloc-free
  2141  	// methods are returning successfully.
  2142  	panicIP := func(ip Addr, err error) Addr {
  2143  		if err != nil {
  2144  			panic(err)
  2145  		}
  2146  		return ip
  2147  	}
  2148  	panicPfx := func(pfx Prefix, err error) Prefix {
  2149  		if err != nil {
  2150  			panic(err)
  2151  		}
  2152  		return pfx
  2153  	}
  2154  	panicIPP := func(ipp AddrPort, err error) AddrPort {
  2155  		if err != nil {
  2156  			panic(err)
  2157  		}
  2158  		return ipp
  2159  	}
  2160  	test := func(name string, f func()) {
  2161  		t.Run(name, func(t *testing.T) {
  2162  			n := testing.AllocsPerRun(1000, f)
  2163  			if n != 0 {
  2164  				t.Fatalf("allocs = %d; want 0", int(n))
  2165  			}
  2166  		})
  2167  	}
  2168  
  2169  	// Addr constructors
  2170  	test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
  2171  	test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
  2172  	test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
  2173  	test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
  2174  	test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
  2175  	test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
  2176  	test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
  2177  	test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
  2178  	test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
  2179  	test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
  2180  
  2181  	// Addr methods
  2182  	test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
  2183  	test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
  2184  	test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
  2185  	test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
  2186  	test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
  2187  	test("Addr.Compare", func() {
  2188  		a := MustParseAddr("1.2.3.4")
  2189  		b := MustParseAddr("2.3.4.5")
  2190  		sinkBool = a.Compare(b) == 0
  2191  	})
  2192  	test("Addr.Less", func() {
  2193  		a := MustParseAddr("1.2.3.4")
  2194  		b := MustParseAddr("2.3.4.5")
  2195  		sinkBool = a.Less(b)
  2196  	})
  2197  	test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
  2198  	test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
  2199  	test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
  2200  	test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
  2201  	test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
  2202  	test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
  2203  	test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
  2204  	test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
  2205  	test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
  2206  	test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
  2207  	test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
  2208  	test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
  2209  	test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
  2210  	test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
  2211  	test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
  2212  	test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
  2213  	test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
  2214  	test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
  2215  	test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
  2216  
  2217  	// AddrPort constructors
  2218  	test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
  2219  	test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
  2220  	test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
  2221  
  2222  	// Prefix constructors
  2223  	test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
  2224  	test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
  2225  	test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
  2226  	test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
  2227  
  2228  	// Prefix methods
  2229  	test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
  2230  	test("Prefix.Overlaps", func() {
  2231  		a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
  2232  		sinkBool = a.Overlaps(b)
  2233  	})
  2234  	test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
  2235  	test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
  2236  	test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
  2237  }
  2238  
  2239  func TestAddrStringAllocs(t *testing.T) {
  2240  	tests := []struct {
  2241  		name       string
  2242  		ip         Addr
  2243  		wantAllocs int
  2244  	}{
  2245  		{"zero", Addr{}, 0},
  2246  		{"ipv4", MustParseAddr("192.168.1.1"), 1},
  2247  		{"ipv6", MustParseAddr("2001:db8::1"), 1},
  2248  		{"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
  2249  		{"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
  2250  		{"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
  2251  	}
  2252  	optimizationOff := testenv.OptimizationOff()
  2253  	for _, tc := range tests {
  2254  		t.Run(tc.name, func(t *testing.T) {
  2255  			if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
  2256  				// Optimizations are required to remove some allocs.
  2257  				t.Skipf("skipping on %v", testenv.Builder())
  2258  			}
  2259  			allocs := int(testing.AllocsPerRun(1000, func() {
  2260  				sinkString = tc.ip.String()
  2261  			}))
  2262  			if allocs != tc.wantAllocs {
  2263  				t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
  2264  			}
  2265  		})
  2266  	}
  2267  }
  2268  
  2269  func TestPrefixString(t *testing.T) {
  2270  	tests := []struct {
  2271  		ipp  Prefix
  2272  		want string
  2273  	}{
  2274  		{Prefix{}, "invalid Prefix"},
  2275  		{PrefixFrom(Addr{}, 8), "invalid Prefix"},
  2276  		{PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
  2277  	}
  2278  
  2279  	for _, tt := range tests {
  2280  		if got := tt.ipp.String(); got != tt.want {
  2281  			t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
  2282  		}
  2283  	}
  2284  }
  2285  
  2286  func TestAddrPortString(t *testing.T) {
  2287  	tests := []struct {
  2288  		ipp  AddrPort
  2289  		want string
  2290  	}{
  2291  		{MustParseAddrPort("127.0.0.1:80"), "127.0.0.1:80"},
  2292  		{MustParseAddrPort("[0000::0]:8080"), "[::]:8080"},
  2293  		{MustParseAddrPort("[FFFF::1]:8080"), "[ffff::1]:8080"},
  2294  		{AddrPort{}, "invalid AddrPort"},
  2295  		{AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
  2296  	}
  2297  
  2298  	for _, tt := range tests {
  2299  		if got := tt.ipp.String(); got != tt.want {
  2300  			t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
  2301  		}
  2302  	}
  2303  }
  2304  
  2305  func TestAsSlice(t *testing.T) {
  2306  	tests := []struct {
  2307  		in   Addr
  2308  		want []byte
  2309  	}{
  2310  		{in: Addr{}, want: nil},
  2311  		{in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
  2312  		{in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
  2313  	}
  2314  
  2315  	for _, test := range tests {
  2316  		got := test.in.AsSlice()
  2317  		if !bytes.Equal(got, test.want) {
  2318  			t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
  2319  		}
  2320  	}
  2321  }
  2322  
  2323  var sink16 [16]byte
  2324  
  2325  func BenchmarkAs16(b *testing.B) {
  2326  	addr := MustParseAddr("1::10")
  2327  	for i := 0; i < b.N; i++ {
  2328  		sink16 = addr.As16()
  2329  	}
  2330  }
  2331  

View as plain text