mirror of
https://github.com/golang/go
synced 2024-10-06 08:00:07 +00:00
net/netip: add new IP address package
Co-authored-by: Alex Willmer <alex@moreati.org.uk> (GitHub @moreati) Co-authored-by: Alexander Yastrebov <yastrebov.alex@gmail.com> Co-authored-by: David Anderson <dave@natulte.net> (Tailscale CLA) Co-authored-by: David Crawshaw <crawshaw@tailscale.com> (Tailscale CLA) Co-authored-by: Dmytro Shynkevych <dmytro@tailscale.com> (Tailscale CLA) Co-authored-by: Elias Naur <mail@eliasnaur.com> Co-authored-by: Joe Tsai <joetsai@digital-static.net> (Tailscale CLA) Co-authored-by: Jonathan Yu <jawnsy@cpan.org> (GitHub @jawnsy) Co-authored-by: Josh Bleecher Snyder <josharian@gmail.com> (Tailscale CLA) Co-authored-by: Maisem Ali <maisem@tailscale.com> (Tailscale CLA) Co-authored-by: Manuel Mendez (Go AUTHORS mmendez534@...) Co-authored-by: Matt Layher <mdlayher@gmail.com> Co-authored-by: Noah Treuhaft <noah.treuhaft@gmail.com> (GitHub @nwt) Co-authored-by: Stefan Majer <stefan.majer@gmail.com> Co-authored-by: Terin Stock <terinjokes@gmail.com> (Cloudflare CLA) Co-authored-by: Tobias Klauser <tklauser@distanz.ch> Fixes #46518 Change-Id: I0041f9e1115d61fa6e95fcf32b01d9faee708712 Reviewed-on: https://go-review.googlesource.com/c/go/+/339309 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> Trust: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
81fea0b4fd
commit
a59e33224e
|
@ -10,11 +10,11 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
macOS "crypto/x509/internal/macos"
|
macOS "crypto/x509/internal/macos"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/godebug"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var debugDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")
|
var debugDarwinRoots = godebug.Get("x509roots") == "1"
|
||||||
|
|
||||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -173,7 +173,7 @@ var depsRules = `
|
||||||
io/fs
|
io/fs
|
||||||
< embed;
|
< embed;
|
||||||
|
|
||||||
unicode, fmt !< os, os/signal;
|
unicode, fmt !< net, os, os/signal;
|
||||||
|
|
||||||
os/signal, STR
|
os/signal, STR
|
||||||
< path/filepath
|
< path/filepath
|
||||||
|
@ -187,6 +187,8 @@ var depsRules = `
|
||||||
OS
|
OS
|
||||||
< golang.org/x/sys/cpu;
|
< golang.org/x/sys/cpu;
|
||||||
|
|
||||||
|
os < internal/godebug;
|
||||||
|
|
||||||
# FMT is OS (which includes string routines) plus reflect and fmt.
|
# FMT is OS (which includes string routines) plus reflect and fmt.
|
||||||
# It does not include package log, which should be avoided in core packages.
|
# It does not include package log, which should be avoided in core packages.
|
||||||
strconv, unicode
|
strconv, unicode
|
||||||
|
@ -352,6 +354,13 @@ var depsRules = `
|
||||||
golang.org/x/net/lif,
|
golang.org/x/net/lif,
|
||||||
golang.org/x/net/route;
|
golang.org/x/net/route;
|
||||||
|
|
||||||
|
os, runtime, strconv, sync, unsafe,
|
||||||
|
internal/godebug
|
||||||
|
< internal/intern;
|
||||||
|
|
||||||
|
internal/bytealg, internal/intern, internal/itoa, math/bits, sort, strconv
|
||||||
|
< net/netip;
|
||||||
|
|
||||||
# net is unavoidable when doing any networking,
|
# net is unavoidable when doing any networking,
|
||||||
# so large dependencies must be kept out.
|
# so large dependencies must be kept out.
|
||||||
# This is a long-looking list but most of these
|
# This is a long-looking list but most of these
|
||||||
|
@ -360,10 +369,12 @@ var depsRules = `
|
||||||
golang.org/x/net/dns/dnsmessage,
|
golang.org/x/net/dns/dnsmessage,
|
||||||
golang.org/x/net/lif,
|
golang.org/x/net/lif,
|
||||||
golang.org/x/net/route,
|
golang.org/x/net/route,
|
||||||
|
internal/godebug,
|
||||||
internal/nettrace,
|
internal/nettrace,
|
||||||
internal/poll,
|
internal/poll,
|
||||||
internal/singleflight,
|
internal/singleflight,
|
||||||
internal/race,
|
internal/race,
|
||||||
|
net/netip,
|
||||||
os
|
os
|
||||||
< net;
|
< net;
|
||||||
|
|
||||||
|
@ -515,7 +526,8 @@ var depsRules = `
|
||||||
FMT, DEBUG, flag, runtime/trace, internal/sysinfo, math/rand
|
FMT, DEBUG, flag, runtime/trace, internal/sysinfo, math/rand
|
||||||
< testing;
|
< testing;
|
||||||
|
|
||||||
FMT, crypto/sha256, encoding/json, go/ast, go/parser, go/token, math/rand, encoding/hex, crypto/sha256
|
FMT, crypto/sha256, encoding/json, go/ast, go/parser, go/token,
|
||||||
|
internal/godebug, math/rand, encoding/hex, crypto/sha256
|
||||||
< internal/fuzz;
|
< internal/fuzz;
|
||||||
|
|
||||||
internal/fuzz, internal/testlog, runtime/pprof, regexp
|
internal/fuzz, internal/testlog, runtime/pprof, regexp
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/godebug"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
@ -1063,13 +1064,7 @@ var (
|
||||||
|
|
||||||
func shouldPrintDebugInfo() bool {
|
func shouldPrintDebugInfo() bool {
|
||||||
debugInfoOnce.Do(func() {
|
debugInfoOnce.Do(func() {
|
||||||
debug := strings.Split(os.Getenv("GODEBUG"), ",")
|
debugInfo = godebug.Get("fuzzdebug") == "1"
|
||||||
for _, f := range debug {
|
|
||||||
if f == "fuzzdebug=1" {
|
|
||||||
debugInfo = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return debugInfo
|
return debugInfo
|
||||||
}
|
}
|
||||||
|
|
34
src/internal/godebug/godebug.go
Normal file
34
src/internal/godebug/godebug.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package godebug parses the GODEBUG environment variable.
|
||||||
|
package godebug
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// Get returns the value for the provided GODEBUG key.
|
||||||
|
func Get(key string) string {
|
||||||
|
return get(os.Getenv("GODEBUG"), key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get returns the value part of key=value in s (a GODEBUG value).
|
||||||
|
func get(s, key string) string {
|
||||||
|
for i := 0; i < len(s)-len(key)-1; i++ {
|
||||||
|
if i > 0 && s[i-1] != ',' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
afterKey := s[i+len(key):]
|
||||||
|
if afterKey[0] != '=' || s[i:i+len(key)] != key {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val := afterKey[1:]
|
||||||
|
for i, b := range val {
|
||||||
|
if b == ',' {
|
||||||
|
return val[:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
34
src/internal/godebug/godebug_test.go
Normal file
34
src/internal/godebug/godebug_test.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package godebug
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestGet(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
godebug string
|
||||||
|
key string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"", "", ""},
|
||||||
|
{"", "foo", ""},
|
||||||
|
{"foo=bar", "foo", "bar"},
|
||||||
|
{"foo=bar,after=x", "foo", "bar"},
|
||||||
|
{"before=x,foo=bar,after=x", "foo", "bar"},
|
||||||
|
{"before=x,foo=bar", "foo", "bar"},
|
||||||
|
{",,,foo=bar,,,", "foo", "bar"},
|
||||||
|
{"foodecoy=wrong,foo=bar", "foo", "bar"},
|
||||||
|
{"foo=", "foo", ""},
|
||||||
|
{"foo", "foo", ""},
|
||||||
|
{",foo", "foo", ""},
|
||||||
|
{"foo=bar,baz", "loooooooong", ""},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := get(tt.godebug, tt.key)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("get(%q, %q) = %q; want %q", tt.godebug, tt.key, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
178
src/internal/intern/intern.go
Normal file
178
src/internal/intern/intern.go
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package intern lets you make smaller comparable values by boxing
|
||||||
|
// a larger comparable value (such as a 16 byte string header) down
|
||||||
|
// into a globally unique 8 byte pointer.
|
||||||
|
//
|
||||||
|
// The globally unique pointers are garbage collected with weak
|
||||||
|
// references and finalizers. This package hides that.
|
||||||
|
package intern
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/godebug"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Value pointer is the handle to an underlying comparable value.
|
||||||
|
// See func Get for how Value pointers may be used.
|
||||||
|
type Value struct {
|
||||||
|
_ [0]func() // prevent people from accidentally using value type as comparable
|
||||||
|
cmpVal interface{}
|
||||||
|
// resurrected is guarded by mu (for all instances of Value).
|
||||||
|
// It is set true whenever v is synthesized from a uintptr.
|
||||||
|
resurrected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the comparable value passed to the Get func
|
||||||
|
// that returned v.
|
||||||
|
func (v *Value) Get() interface{} { return v.cmpVal }
|
||||||
|
|
||||||
|
// key is a key in our global value map.
|
||||||
|
// It contains type-specialized fields to avoid allocations
|
||||||
|
// when converting common types to empty interfaces.
|
||||||
|
type key struct {
|
||||||
|
s string
|
||||||
|
cmpVal interface{}
|
||||||
|
// isString reports whether key contains a string.
|
||||||
|
// Without it, the zero value of key is ambiguous.
|
||||||
|
isString bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyFor returns a key to use with cmpVal.
|
||||||
|
func keyFor(cmpVal interface{}) key {
|
||||||
|
if s, ok := cmpVal.(string); ok {
|
||||||
|
return key{s: s, isString: true}
|
||||||
|
}
|
||||||
|
return key{cmpVal: cmpVal}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns a *Value built from k.
|
||||||
|
func (k key) Value() *Value {
|
||||||
|
if k.isString {
|
||||||
|
return &Value{cmpVal: k.s}
|
||||||
|
}
|
||||||
|
return &Value{cmpVal: k.cmpVal}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// mu guards valMap, a weakref map of *Value by underlying value.
|
||||||
|
// It also guards the resurrected field of all *Values.
|
||||||
|
mu sync.Mutex
|
||||||
|
valMap = map[key]uintptr{} // to uintptr(*Value)
|
||||||
|
valSafe = safeMap() // non-nil in safe+leaky mode
|
||||||
|
)
|
||||||
|
|
||||||
|
// safeMap returns a non-nil map if we're in safe-but-leaky mode,
|
||||||
|
// as controlled by GODEBUG=intern=leaky
|
||||||
|
func safeMap() map[key]*Value {
|
||||||
|
if godebug.Get("intern") == "leaky" {
|
||||||
|
return map[key]*Value{}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a pointer representing the comparable value cmpVal.
|
||||||
|
//
|
||||||
|
// The returned pointer will be the same for Get(v) and Get(v2)
|
||||||
|
// if and only if v == v2, and can be used as a map key.
|
||||||
|
func Get(cmpVal interface{}) *Value {
|
||||||
|
return get(keyFor(cmpVal))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByString is identical to Get, except that it is specialized for strings.
|
||||||
|
// This avoids an allocation from putting a string into an interface{}
|
||||||
|
// to pass as an argument to Get.
|
||||||
|
func GetByString(s string) *Value {
|
||||||
|
return get(key{s: s, isString: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// We play unsafe games that violate Go's rules (and assume a non-moving
|
||||||
|
// collector). So we quiet Go here.
|
||||||
|
// See the comment below Get for more implementation details.
|
||||||
|
//go:nocheckptr
|
||||||
|
func get(k key) *Value {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
var v *Value
|
||||||
|
if valSafe != nil {
|
||||||
|
v = valSafe[k]
|
||||||
|
} else if addr, ok := valMap[k]; ok {
|
||||||
|
v = (*Value)(unsafe.Pointer(addr))
|
||||||
|
v.resurrected = true
|
||||||
|
}
|
||||||
|
if v != nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
v = k.Value()
|
||||||
|
if valSafe != nil {
|
||||||
|
valSafe[k] = v
|
||||||
|
} else {
|
||||||
|
// SetFinalizer before uintptr conversion (theoretical concern;
|
||||||
|
// see https://github.com/go4org/intern/issues/13)
|
||||||
|
runtime.SetFinalizer(v, finalize)
|
||||||
|
valMap[k] = uintptr(unsafe.Pointer(v))
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func finalize(v *Value) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
if v.resurrected {
|
||||||
|
// We lost the race. Somebody resurrected it while we
|
||||||
|
// were about to finalize it. Try again next round.
|
||||||
|
v.resurrected = false
|
||||||
|
runtime.SetFinalizer(v, finalize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(valMap, keyFor(v.cmpVal))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interning is simple if you don't require that unused values be
|
||||||
|
// garbage collectable. But we do require that; we don't want to be
|
||||||
|
// DOS vector. We do this by using a uintptr to hide the pointer from
|
||||||
|
// the garbage collector, and using a finalizer to eliminate the
|
||||||
|
// pointer when no other code is using it.
|
||||||
|
//
|
||||||
|
// The obvious implementation of this is to use a
|
||||||
|
// map[interface{}]uintptr-of-*interface{}, and set up a finalizer to
|
||||||
|
// delete from the map. Unfortunately, this is racy. Because pointers
|
||||||
|
// are being created in violation of Go's unsafety rules, it's
|
||||||
|
// possible to create a pointer to a value concurrently with the GC
|
||||||
|
// concluding that the value can be collected. There are other races
|
||||||
|
// that break the equality invariant as well, but the use-after-free
|
||||||
|
// will cause a runtime crash.
|
||||||
|
//
|
||||||
|
// To make this work, the finalizer needs to know that no references
|
||||||
|
// have been unsafely created since the finalizer was set up. To do
|
||||||
|
// this, values carry a "resurrected" sentinel, which gets set
|
||||||
|
// whenever a pointer is unsafely created. If the finalizer encounters
|
||||||
|
// the sentinel, it clears the sentinel and delays collection for one
|
||||||
|
// additional GC cycle, by re-installing itself as finalizer. This
|
||||||
|
// ensures that the unsafely created pointer is visible to the GC, and
|
||||||
|
// will correctly prevent collection.
|
||||||
|
//
|
||||||
|
// This technique does mean that interned values that get reused take
|
||||||
|
// at least 3 GC cycles to fully collect (1 to clear the sentinel, 1
|
||||||
|
// to clean up the unsafe map, 1 to be actually deleted).
|
||||||
|
//
|
||||||
|
// @ianlancetaylor commented in
|
||||||
|
// https://github.com/golang/go/issues/41303#issuecomment-717401656
|
||||||
|
// that it is possible to implement weak references in terms of
|
||||||
|
// finalizers without unsafe. Unfortunately, the approach he outlined
|
||||||
|
// does not work here, for two reasons. First, there is no way to
|
||||||
|
// construct a strong pointer out of a weak pointer; our map stores
|
||||||
|
// weak pointers, but we must return strong pointers to callers.
|
||||||
|
// Second, and more fundamentally, we must return not just _a_ strong
|
||||||
|
// pointer to callers, but _the same_ strong pointer to callers. In
|
||||||
|
// order to return _the same_ strong pointer to callers, we must track
|
||||||
|
// it, which is exactly what we cannot do with strong pointers.
|
||||||
|
//
|
||||||
|
// See https://github.com/inetaf/netaddr/issues/53 for more
|
||||||
|
// discussion, and https://github.com/go4org/intern/issues/2 for an
|
||||||
|
// illustration of the subtleties at play.
|
199
src/internal/intern/intern_test.go
Normal file
199
src/internal/intern/intern_test.go
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package intern
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBasics(t *testing.T) {
|
||||||
|
clearMap()
|
||||||
|
foo := Get("foo")
|
||||||
|
bar := Get("bar")
|
||||||
|
empty := Get("")
|
||||||
|
nilEface := Get(nil)
|
||||||
|
i := Get(0x7777777)
|
||||||
|
foo2 := Get("foo")
|
||||||
|
bar2 := Get("bar")
|
||||||
|
empty2 := Get("")
|
||||||
|
nilEface2 := Get(nil)
|
||||||
|
i2 := Get(0x7777777)
|
||||||
|
foo3 := GetByString("foo")
|
||||||
|
empty3 := GetByString("")
|
||||||
|
|
||||||
|
if foo.Get() != foo2.Get() {
|
||||||
|
t.Error("foo/foo2 values differ")
|
||||||
|
}
|
||||||
|
if foo.Get() != foo3.Get() {
|
||||||
|
t.Error("foo/foo3 values differ")
|
||||||
|
}
|
||||||
|
if foo.Get() != "foo" {
|
||||||
|
t.Error("foo.Get not foo")
|
||||||
|
}
|
||||||
|
if foo != foo2 {
|
||||||
|
t.Error("foo/foo2 pointers differ")
|
||||||
|
}
|
||||||
|
if foo != foo3 {
|
||||||
|
t.Error("foo/foo3 pointers differ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bar.Get() != bar2.Get() {
|
||||||
|
t.Error("bar values differ")
|
||||||
|
}
|
||||||
|
if bar.Get() != "bar" {
|
||||||
|
t.Error("bar.Get not bar")
|
||||||
|
}
|
||||||
|
if bar != bar2 {
|
||||||
|
t.Error("bar pointers differ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.Get() != i.Get() {
|
||||||
|
t.Error("i values differ")
|
||||||
|
}
|
||||||
|
if i.Get() != 0x7777777 {
|
||||||
|
t.Error("i.Get not 0x7777777")
|
||||||
|
}
|
||||||
|
if i != i2 {
|
||||||
|
t.Error("i pointers differ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if empty.Get() != empty2.Get() {
|
||||||
|
t.Error("empty/empty2 values differ")
|
||||||
|
}
|
||||||
|
if empty.Get() != empty.Get() {
|
||||||
|
t.Error("empty/empty3 values differ")
|
||||||
|
}
|
||||||
|
if empty.Get() != "" {
|
||||||
|
t.Error("empty.Get not empty string")
|
||||||
|
}
|
||||||
|
if empty != empty2 {
|
||||||
|
t.Error("empty/empty2 pointers differ")
|
||||||
|
}
|
||||||
|
if empty != empty3 {
|
||||||
|
t.Error("empty/empty3 pointers differ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if nilEface.Get() != nilEface2.Get() {
|
||||||
|
t.Error("nilEface values differ")
|
||||||
|
}
|
||||||
|
if nilEface.Get() != nil {
|
||||||
|
t.Error("nilEface.Get not nil")
|
||||||
|
}
|
||||||
|
if nilEface != nilEface2 {
|
||||||
|
t.Error("nilEface pointers differ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := mapLen(); n != 5 {
|
||||||
|
t.Errorf("map len = %d; want 4", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantEmpty(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wantEmpty(t testing.TB) {
|
||||||
|
t.Helper()
|
||||||
|
const gcTries = 5000
|
||||||
|
for try := 0; try < gcTries; try++ {
|
||||||
|
runtime.GC()
|
||||||
|
n := mapLen()
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if try == gcTries-1 {
|
||||||
|
t.Errorf("map len = %d after (%d GC tries); want 0, contents: %v", n, gcTries, mapKeys())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStress(t *testing.T) {
|
||||||
|
iters := 10000
|
||||||
|
if testing.Short() {
|
||||||
|
iters = 1000
|
||||||
|
}
|
||||||
|
var sink []byte
|
||||||
|
for i := 0; i < iters; i++ {
|
||||||
|
_ = Get("foo")
|
||||||
|
sink = make([]byte, 1<<20)
|
||||||
|
}
|
||||||
|
_ = sink
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStress(b *testing.B) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
runtime.GC()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
clearMap()
|
||||||
|
v1 := Get("foo")
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
v2 := Get("foo")
|
||||||
|
if v1 != v2 {
|
||||||
|
b.Fatal("wrong value")
|
||||||
|
}
|
||||||
|
// And also a key we don't retain:
|
||||||
|
_ = Get("bar")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
runtime.GC()
|
||||||
|
wantEmpty(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapLen() int {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
return len(valMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapKeys() (keys []string) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
for k := range valMap {
|
||||||
|
keys = append(keys, fmt.Sprint(k))
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearMap() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
for k := range valMap {
|
||||||
|
delete(valMap, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalString = "not a constant"
|
||||||
|
sink string
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetByStringAllocs(t *testing.T) {
|
||||||
|
allocs := int(testing.AllocsPerRun(100, func() {
|
||||||
|
GetByString(globalString)
|
||||||
|
}))
|
||||||
|
if allocs != 0 {
|
||||||
|
t.Errorf("GetString allocated %d objects, want 0", allocs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGetByString(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
v := GetByString(globalString)
|
||||||
|
sink = v.Get().(string)
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"internal/bytealg"
|
"internal/bytealg"
|
||||||
|
"internal/godebug"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -286,7 +287,7 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde
|
||||||
// cgo+2 // same, but debug level 2
|
// cgo+2 // same, but debug level 2
|
||||||
// etc.
|
// etc.
|
||||||
func goDebugNetDNS() (dnsMode string, debugLevel int) {
|
func goDebugNetDNS() (dnsMode string, debugLevel int) {
|
||||||
goDebug := goDebugString("netdns")
|
goDebug := godebug.Get("netdns")
|
||||||
parsePart := func(s string) {
|
parsePart := func(s string) {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return
|
return
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/godebug"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
@ -20,7 +21,6 @@ import (
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"net/url"
|
"net/url"
|
||||||
urlpkg "net/url"
|
urlpkg "net/url"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -3296,7 +3296,7 @@ func (srv *Server) onceSetNextProtoDefaults_Serve() {
|
||||||
// configured otherwise. (by setting srv.TLSNextProto non-nil)
|
// configured otherwise. (by setting srv.TLSNextProto non-nil)
|
||||||
// It must only be called via srv.nextProtoOnce (use srv.setupHTTP2_*).
|
// It must only be called via srv.nextProtoOnce (use srv.setupHTTP2_*).
|
||||||
func (srv *Server) onceSetNextProtoDefaults() {
|
func (srv *Server) onceSetNextProtoDefaults() {
|
||||||
if omitBundledHTTP2 || strings.Contains(os.Getenv("GODEBUG"), "http2server=0") {
|
if omitBundledHTTP2 || godebug.Get("http2server") == "0" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Enable HTTP/2 by default if the user hasn't otherwise
|
// Enable HTTP/2 by default if the user hasn't otherwise
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/godebug"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -24,7 +25,6 @@ import (
|
||||||
"net/http/internal/ascii"
|
"net/http/internal/ascii"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -360,7 +360,7 @@ func (t *Transport) hasCustomTLSDialer() bool {
|
||||||
// It must be called via t.nextProtoOnce.Do.
|
// It must be called via t.nextProtoOnce.Do.
|
||||||
func (t *Transport) onceSetNextProtoDefaults() {
|
func (t *Transport) onceSetNextProtoDefaults() {
|
||||||
t.tlsNextProtoWasNil = (t.TLSNextProto == nil)
|
t.tlsNextProtoWasNil = (t.TLSNextProto == nil)
|
||||||
if strings.Contains(os.Getenv("GODEBUG"), "http2client=0") {
|
if godebug.Get("http2client") == "0" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"internal/nettrace"
|
"internal/nettrace"
|
||||||
"internal/singleflight"
|
"internal/singleflight"
|
||||||
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -232,6 +233,28 @@ func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, er
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LookupNetIP looks up host using the local resolver.
|
||||||
|
// It returns a slice of that host's IP addresses of the type specified by
|
||||||
|
// network.
|
||||||
|
// The network must be one of "ip", "ip4" or "ip6".
|
||||||
|
func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
|
||||||
|
// TODO(bradfitz): make this efficient, making the internal net package
|
||||||
|
// type throughout be netip.Addr and only converting to the net.IP slice
|
||||||
|
// version at the edge. But for now (2021-10-20), this is a wrapper around
|
||||||
|
// the old way.
|
||||||
|
ips, err := r.LookupIP(ctx, network, host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret := make([]netip.Addr, 0, len(ips))
|
||||||
|
for _, ip := range ips {
|
||||||
|
if a, ok := netip.AddrFromSlice(ip); ok {
|
||||||
|
ret = append(ret, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
// onlyValuesCtx is a context that uses an underlying context
|
// onlyValuesCtx is a context that uses an underlying context
|
||||||
// for value lookup if the underlying context hasn't yet expired.
|
// for value lookup if the underlying context hasn't yet expired.
|
||||||
type onlyValuesCtx struct {
|
type onlyValuesCtx struct {
|
||||||
|
|
30
src/net/netip/export_test.go
Normal file
30
src/net/netip/export_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package netip
|
||||||
|
|
||||||
|
import "internal/intern"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Z0 = z0
|
||||||
|
Z4 = z4
|
||||||
|
Z6noz = z6noz
|
||||||
|
)
|
||||||
|
|
||||||
|
type Uint128 = uint128
|
||||||
|
|
||||||
|
func Mk128(hi, lo uint64) Uint128 {
|
||||||
|
return uint128{hi, lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MkAddr(u Uint128, z *intern.Value) Addr {
|
||||||
|
return Addr{u, z}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IPv4(a, b, c, d uint8) Addr { return AddrFrom4([4]byte{a, b, c, d}) }
|
||||||
|
|
||||||
|
var TestAppendToMarshal = testAppendToMarshal
|
||||||
|
|
||||||
|
func (a Addr) IsZero() bool { return a.isZero() }
|
||||||
|
func (p Prefix) IsZero() bool { return p.isZero() }
|
110
src/net/netip/inlining_test.go
Normal file
110
src/net/netip/inlining_test.go
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package netip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/testenv"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInlining(t *testing.T) {
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
t.Parallel()
|
||||||
|
var exe string
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
exe = ".exe"
|
||||||
|
}
|
||||||
|
out, err := exec.Command(
|
||||||
|
filepath.Join(runtime.GOROOT(), "bin", "go"+exe),
|
||||||
|
"build",
|
||||||
|
"--gcflags=-m",
|
||||||
|
"net/netip").CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("go build: %v, %s", err, out)
|
||||||
|
}
|
||||||
|
got := map[string]bool{}
|
||||||
|
regexp.MustCompile(` can inline (\S+)`).ReplaceAllFunc(out, func(match []byte) []byte {
|
||||||
|
got[strings.TrimPrefix(string(match), " can inline ")] = true
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
wantInlinable := []string{
|
||||||
|
"(*uint128).halves",
|
||||||
|
"Addr.BitLen",
|
||||||
|
"Addr.hasZone",
|
||||||
|
"Addr.Is4",
|
||||||
|
"Addr.Is4In6",
|
||||||
|
"Addr.Is6",
|
||||||
|
"Addr.IsLoopback",
|
||||||
|
"Addr.IsMulticast",
|
||||||
|
"Addr.IsInterfaceLocalMulticast",
|
||||||
|
"Addr.IsValid",
|
||||||
|
"Addr.IsUnspecified",
|
||||||
|
"Addr.Less",
|
||||||
|
"Addr.lessOrEq",
|
||||||
|
"Addr.Unmap",
|
||||||
|
"Addr.Zone",
|
||||||
|
"Addr.v4",
|
||||||
|
"Addr.v6",
|
||||||
|
"Addr.v6u16",
|
||||||
|
"Addr.withoutZone",
|
||||||
|
"AddrPortFrom",
|
||||||
|
"AddrPort.Addr",
|
||||||
|
"AddrPort.Port",
|
||||||
|
"AddrPort.IsValid",
|
||||||
|
"Prefix.IsSingleIP",
|
||||||
|
"Prefix.Masked",
|
||||||
|
"Prefix.IsValid",
|
||||||
|
"PrefixFrom",
|
||||||
|
"Prefix.Addr",
|
||||||
|
"Prefix.Bits",
|
||||||
|
"AddrFrom4",
|
||||||
|
"IPv6LinkLocalAllNodes",
|
||||||
|
"IPv6Unspecified",
|
||||||
|
"MustParseAddr",
|
||||||
|
"MustParseAddrPort",
|
||||||
|
"MustParsePrefix",
|
||||||
|
"appendDecimal",
|
||||||
|
"appendHex",
|
||||||
|
"uint128.addOne",
|
||||||
|
"uint128.and",
|
||||||
|
"uint128.bitsClearedFrom",
|
||||||
|
"uint128.bitsSetFrom",
|
||||||
|
"uint128.isZero",
|
||||||
|
"uint128.not",
|
||||||
|
"uint128.or",
|
||||||
|
"uint128.subOne",
|
||||||
|
"uint128.xor",
|
||||||
|
}
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "amd64", "arm64":
|
||||||
|
// These don't inline on 32-bit.
|
||||||
|
wantInlinable = append(wantInlinable,
|
||||||
|
"u64CommonPrefixLen",
|
||||||
|
"uint128.commonPrefixLen",
|
||||||
|
"Addr.Next",
|
||||||
|
"Addr.Prev",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, want := range wantInlinable {
|
||||||
|
if !got[want] {
|
||||||
|
t.Errorf("%q is no longer inlinable", want)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(got, want)
|
||||||
|
}
|
||||||
|
for sym := range got {
|
||||||
|
if strings.Contains(sym, ".func") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Logf("not in expected set, but also inlinable: %q", sym)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
43
src/net/netip/leaf_alts.go
Normal file
43
src/net/netip/leaf_alts.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Stuff that exists in std, but we can't use due to being a dependency
|
||||||
|
// of net, for go/build deps_test policy reasons.
|
||||||
|
|
||||||
|
package netip
|
||||||
|
|
||||||
|
func stringsLastIndexByte(s string, b byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] == b {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func beUint64(b []byte) uint64 {
|
||||||
|
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||||
|
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||||
|
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||||
|
}
|
||||||
|
|
||||||
|
func bePutUint64(b []byte, v uint64) {
|
||||||
|
_ = b[7] // early bounds check to guarantee safety of writes below
|
||||||
|
b[0] = byte(v >> 56)
|
||||||
|
b[1] = byte(v >> 48)
|
||||||
|
b[2] = byte(v >> 40)
|
||||||
|
b[3] = byte(v >> 32)
|
||||||
|
b[4] = byte(v >> 24)
|
||||||
|
b[5] = byte(v >> 16)
|
||||||
|
b[6] = byte(v >> 8)
|
||||||
|
b[7] = byte(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bePutUint32(b []byte, v uint32) {
|
||||||
|
_ = b[3] // early bounds check to guarantee safety of writes below
|
||||||
|
b[0] = byte(v >> 24)
|
||||||
|
b[1] = byte(v >> 16)
|
||||||
|
b[2] = byte(v >> 8)
|
||||||
|
b[3] = byte(v)
|
||||||
|
}
|
1414
src/net/netip/netip.go
Normal file
1414
src/net/netip/netip.go
Normal file
File diff suppressed because it is too large
Load diff
359
src/net/netip/netip_pkg_test.go
Normal file
359
src/net/netip/netip_pkg_test.go
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package netip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mustPrefix = MustParsePrefix
|
||||||
|
mustIP = MustParseAddr
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrefixValid(t *testing.T) {
|
||||||
|
v4 := MustParseAddr("1.2.3.4")
|
||||||
|
v6 := MustParseAddr("::1")
|
||||||
|
tests := []struct {
|
||||||
|
ipp Prefix
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{Prefix{v4, -2}, false},
|
||||||
|
{Prefix{v4, -1}, false},
|
||||||
|
{Prefix{v4, 0}, true},
|
||||||
|
{Prefix{v4, 32}, true},
|
||||||
|
{Prefix{v4, 33}, false},
|
||||||
|
|
||||||
|
{Prefix{v6, -2}, false},
|
||||||
|
{Prefix{v6, -1}, false},
|
||||||
|
{Prefix{v6, 0}, true},
|
||||||
|
{Prefix{v6, 32}, true},
|
||||||
|
{Prefix{v6, 128}, true},
|
||||||
|
{Prefix{v6, 129}, false},
|
||||||
|
|
||||||
|
{Prefix{Addr{}, -2}, false},
|
||||||
|
{Prefix{Addr{}, -1}, false},
|
||||||
|
{Prefix{Addr{}, 0}, false},
|
||||||
|
{Prefix{Addr{}, 32}, false},
|
||||||
|
{Prefix{Addr{}, 128}, false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := tt.ipp.IsValid()
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("(%v).IsValid() = %v want %v", tt.ipp, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextPrevTests = []struct {
|
||||||
|
ip Addr
|
||||||
|
next Addr
|
||||||
|
prev Addr
|
||||||
|
}{
|
||||||
|
{mustIP("10.0.0.1"), mustIP("10.0.0.2"), mustIP("10.0.0.0")},
|
||||||
|
{mustIP("10.0.0.255"), mustIP("10.0.1.0"), mustIP("10.0.0.254")},
|
||||||
|
{mustIP("127.0.0.1"), mustIP("127.0.0.2"), mustIP("127.0.0.0")},
|
||||||
|
{mustIP("254.255.255.255"), mustIP("255.0.0.0"), mustIP("254.255.255.254")},
|
||||||
|
{mustIP("255.255.255.255"), Addr{}, mustIP("255.255.255.254")},
|
||||||
|
{mustIP("0.0.0.0"), mustIP("0.0.0.1"), Addr{}},
|
||||||
|
{mustIP("::"), mustIP("::1"), Addr{}},
|
||||||
|
{mustIP("::%x"), mustIP("::1%x"), Addr{}},
|
||||||
|
{mustIP("::1"), mustIP("::2"), mustIP("::")},
|
||||||
|
{mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), Addr{}, mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPNextPrev(t *testing.T) {
|
||||||
|
doNextPrev(t)
|
||||||
|
|
||||||
|
for _, ip := range []Addr{
|
||||||
|
mustIP("0.0.0.0"),
|
||||||
|
mustIP("::"),
|
||||||
|
} {
|
||||||
|
got := ip.Prev()
|
||||||
|
if !got.isZero() {
|
||||||
|
t.Errorf("IP(%v).Prev = %v; want zero", ip, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var allFF [16]byte
|
||||||
|
for i := range allFF {
|
||||||
|
allFF[i] = 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range []Addr{
|
||||||
|
mustIP("255.255.255.255"),
|
||||||
|
AddrFrom16(allFF),
|
||||||
|
} {
|
||||||
|
got := ip.Next()
|
||||||
|
if !got.isZero() {
|
||||||
|
t.Errorf("IP(%v).Next = %v; want zero", ip, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIPNextPrev(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
doNextPrev(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doNextPrev(t testing.TB) {
|
||||||
|
for _, tt := range nextPrevTests {
|
||||||
|
gnext, gprev := tt.ip.Next(), tt.ip.Prev()
|
||||||
|
if gnext != tt.next {
|
||||||
|
t.Errorf("IP(%v).Next = %v; want %v", tt.ip, gnext, tt.next)
|
||||||
|
}
|
||||||
|
if gprev != tt.prev {
|
||||||
|
t.Errorf("IP(%v).Prev = %v; want %v", tt.ip, gprev, tt.prev)
|
||||||
|
}
|
||||||
|
if !tt.ip.Next().isZero() && tt.ip.Next().Prev() != tt.ip {
|
||||||
|
t.Errorf("IP(%v).Next.Prev = %v; want %v", tt.ip, tt.ip.Next().Prev(), tt.ip)
|
||||||
|
}
|
||||||
|
if !tt.ip.Prev().isZero() && tt.ip.Prev().Next() != tt.ip {
|
||||||
|
t.Errorf("IP(%v).Prev.Next = %v; want %v", tt.ip, tt.ip.Prev().Next(), tt.ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPBitLen(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
ip Addr
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{Addr{}, 0},
|
||||||
|
{mustIP("0.0.0.0"), 32},
|
||||||
|
{mustIP("10.0.0.1"), 32},
|
||||||
|
{mustIP("::"), 128},
|
||||||
|
{mustIP("fed0::1"), 128},
|
||||||
|
{mustIP("::ffff:10.0.0.1"), 128},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := tt.ip.BitLen()
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("BitLen(%v) = %d; want %d", tt.ip, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrefixContains(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
ipp Prefix
|
||||||
|
ip Addr
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{mustPrefix("9.8.7.6/0"), mustIP("9.8.7.6"), true},
|
||||||
|
{mustPrefix("9.8.7.6/16"), mustIP("9.8.7.6"), true},
|
||||||
|
{mustPrefix("9.8.7.6/16"), mustIP("9.8.6.4"), true},
|
||||||
|
{mustPrefix("9.8.7.6/16"), mustIP("9.9.7.6"), false},
|
||||||
|
{mustPrefix("9.8.7.6/32"), mustIP("9.8.7.6"), true},
|
||||||
|
{mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false},
|
||||||
|
{mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false},
|
||||||
|
{mustPrefix("::1/0"), mustIP("::1"), true},
|
||||||
|
{mustPrefix("::1/0"), mustIP("::2"), true},
|
||||||
|
{mustPrefix("::1/127"), mustIP("::1"), true},
|
||||||
|
{mustPrefix("::1/127"), mustIP("::2"), false},
|
||||||
|
{mustPrefix("::1/128"), mustIP("::1"), true},
|
||||||
|
{mustPrefix("::1/127"), mustIP("::2"), false},
|
||||||
|
// zones support
|
||||||
|
{mustPrefix("::1%a/128"), mustIP("::1"), true}, // prefix zones are stripped...
|
||||||
|
{mustPrefix("::1%a/128"), mustIP("::1%a"), false}, // but ip zones are not
|
||||||
|
// invalid IP
|
||||||
|
{mustPrefix("::1/0"), Addr{}, false},
|
||||||
|
{mustPrefix("1.2.3.4/0"), Addr{}, false},
|
||||||
|
// invalid Prefix
|
||||||
|
{Prefix{mustIP("::1"), 129}, mustIP("::1"), false},
|
||||||
|
{Prefix{mustIP("1.2.3.4"), 33}, mustIP("1.2.3.4"), false},
|
||||||
|
{Prefix{Addr{}, 0}, mustIP("1.2.3.4"), false},
|
||||||
|
{Prefix{Addr{}, 32}, mustIP("1.2.3.4"), false},
|
||||||
|
{Prefix{Addr{}, 128}, mustIP("::1"), false},
|
||||||
|
// wrong IP family
|
||||||
|
{mustPrefix("::1/0"), mustIP("1.2.3.4"), false},
|
||||||
|
{mustPrefix("1.2.3.4/0"), mustIP("::1"), false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := tt.ipp.Contains(tt.ip)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("(%v).Contains(%v) = %v want %v", tt.ipp, tt.ip, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseIPError(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
ip string
|
||||||
|
errstr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ip: "localhost",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "500.0.0.1",
|
||||||
|
errstr: "field has value >255",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "::gggg%eth0",
|
||||||
|
errstr: "must have at least one digit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "fe80::1cc0:3e8c:119f:c2e1%",
|
||||||
|
errstr: "zone must be a non-empty string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "%eth0",
|
||||||
|
errstr: "missing IPv6 address",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.ip, func(t *testing.T) {
|
||||||
|
_, err := ParseAddr(test.ip)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("no error")
|
||||||
|
}
|
||||||
|
if _, ok := err.(parseAddrError); !ok {
|
||||||
|
t.Errorf("error type is %T, want parseIPError", err)
|
||||||
|
}
|
||||||
|
if test.errstr == "" {
|
||||||
|
test.errstr = "unable to parse IP"
|
||||||
|
}
|
||||||
|
if got := err.Error(); !strings.Contains(got, test.errstr) {
|
||||||
|
t.Errorf("error is missing substring %q: %s", test.errstr, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAddrPort(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in string
|
||||||
|
want AddrPort
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{in: "1.2.3.4:1234", want: AddrPort{mustIP("1.2.3.4"), 1234}},
|
||||||
|
{in: "1.1.1.1:123456", wantErr: true},
|
||||||
|
{in: "1.1.1.1:-123", wantErr: true},
|
||||||
|
{in: "[::1]:1234", want: AddrPort{mustIP("::1"), 1234}},
|
||||||
|
{in: "[1.2.3.4]:1234", wantErr: true},
|
||||||
|
{in: "fe80::1:1234", wantErr: true},
|
||||||
|
{in: ":0", wantErr: true}, // if we need to parse this form, there should be a separate function that explicitly allows it
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.in, func(t *testing.T) {
|
||||||
|
got, err := ParseAddrPort(test.in)
|
||||||
|
if err != nil {
|
||||||
|
if test.wantErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("got %v; want %v", got, test.want)
|
||||||
|
}
|
||||||
|
if got.String() != test.in {
|
||||||
|
t.Errorf("String = %q; want %q", got.String(), test.in)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run(test.in+"/AppendTo", func(t *testing.T) {
|
||||||
|
got, err := ParseAddrPort(test.in)
|
||||||
|
if err == nil {
|
||||||
|
testAppendToMarshal(t, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// TextMarshal and TextUnmarshal mostly behave like
|
||||||
|
// ParseAddrPort and String. Divergent behavior are handled in
|
||||||
|
// TestAddrPortMarshalUnmarshal.
|
||||||
|
t.Run(test.in+"/Marshal", func(t *testing.T) {
|
||||||
|
var got AddrPort
|
||||||
|
jsin := `"` + test.in + `"`
|
||||||
|
err := json.Unmarshal([]byte(jsin), &got)
|
||||||
|
if err != nil {
|
||||||
|
if test.wantErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("got %v; want %v", got, test.want)
|
||||||
|
}
|
||||||
|
gotb, err := json.Marshal(got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(gotb) != jsin {
|
||||||
|
t.Errorf("Marshal = %q; want %q", string(gotb), jsin)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddrPortMarshalUnmarshal(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in string
|
||||||
|
want AddrPort
|
||||||
|
}{
|
||||||
|
{"", AddrPort{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.in, func(t *testing.T) {
|
||||||
|
orig := `"` + test.in + `"`
|
||||||
|
|
||||||
|
var ipp AddrPort
|
||||||
|
if err := json.Unmarshal([]byte(orig), &ipp); err != nil {
|
||||||
|
t.Fatalf("failed to unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ippb, err := json.Marshal(ipp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
back := string(ippb)
|
||||||
|
if orig != back {
|
||||||
|
t.Errorf("Marshal = %q; want %q", back, orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
testAppendToMarshal(t, ipp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type appendMarshaler interface {
|
||||||
|
encoding.TextMarshaler
|
||||||
|
AppendTo([]byte) []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// testAppendToMarshal tests that x's AppendTo and MarshalText methods yield the same results.
|
||||||
|
// x's MarshalText method must not return an error.
|
||||||
|
func testAppendToMarshal(t *testing.T, x appendMarshaler) {
|
||||||
|
t.Helper()
|
||||||
|
m, err := x.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("(%v).MarshalText: %v", x, err)
|
||||||
|
}
|
||||||
|
a := make([]byte, 0, len(m))
|
||||||
|
a = x.AppendTo(a)
|
||||||
|
if !bytes.Equal(m, a) {
|
||||||
|
t.Errorf("(%v).MarshalText = %q, (%v).AppendTo = %q", x, m, x, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPv6Accessor(t *testing.T) {
|
||||||
|
var a [16]byte
|
||||||
|
for i := range a {
|
||||||
|
a[i] = uint8(i) + 1
|
||||||
|
}
|
||||||
|
ip := AddrFrom16(a)
|
||||||
|
for i := range a {
|
||||||
|
if got, want := ip.v6(uint8(i)), uint8(i)+1; got != want {
|
||||||
|
t.Errorf("v6(%v) = %v; want %v", i, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1798
src/net/netip/netip_test.go
Normal file
1798
src/net/netip/netip_test.go
Normal file
File diff suppressed because it is too large
Load diff
190
src/net/netip/slow_test.go
Normal file
190
src/net/netip/slow_test.go
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package netip_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
. "net/netip"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// zeros is a slice of eight stringified zeros. It's used in
|
||||||
|
// parseIPSlow to construct slices of specific amounts of zero fields,
|
||||||
|
// from 1 to 8.
|
||||||
|
var zeros = []string{"0", "0", "0", "0", "0", "0", "0", "0"}
|
||||||
|
|
||||||
|
// parseIPSlow is like ParseIP, but aims for readability above
|
||||||
|
// speed. It's the reference implementation for correctness checking
|
||||||
|
// and against which we measure optimized parsers.
|
||||||
|
//
|
||||||
|
// parseIPSlow understands the following forms of IP addresses:
|
||||||
|
// - Regular IPv4: 1.2.3.4
|
||||||
|
// - IPv4 with many leading zeros: 0000001.0000002.0000003.0000004
|
||||||
|
// - Regular IPv6: 1111:2222:3333:4444:5555:6666:7777:8888
|
||||||
|
// - IPv6 with many leading zeros: 00000001:0000002:0000003:0000004:0000005:0000006:0000007:0000008
|
||||||
|
// - IPv6 with zero blocks elided: 1111:2222::7777:8888
|
||||||
|
// - IPv6 with trailing 32 bits expressed as IPv4: 1111:2222:3333:4444:5555:6666:77.77.88.88
|
||||||
|
//
|
||||||
|
// It does not process the following IP address forms, which have been
|
||||||
|
// varyingly accepted by some programs due to an under-specification
|
||||||
|
// of the shapes of IPv4 addresses:
|
||||||
|
//
|
||||||
|
// - IPv4 as a single 32-bit uint: 4660 (same as "1.2.3.4")
|
||||||
|
// - IPv4 with octal numbers: 0300.0250.0.01 (same as "192.168.0.1")
|
||||||
|
// - IPv4 with hex numbers: 0xc0.0xa8.0x0.0x1 (same as "192.168.0.1")
|
||||||
|
// - IPv4 in "class-B style": 1.2.52 (same as "1.2.3.4")
|
||||||
|
// - IPv4 in "class-A style": 1.564 (same as "1.2.3.4")
|
||||||
|
func parseIPSlow(s string) (Addr, error) {
|
||||||
|
// Identify and strip out the zone, if any. There should be 0 or 1
|
||||||
|
// '%' in the string.
|
||||||
|
var zone string
|
||||||
|
fs := strings.Split(s, "%")
|
||||||
|
switch len(fs) {
|
||||||
|
case 1:
|
||||||
|
// No zone, that's fine.
|
||||||
|
case 2:
|
||||||
|
s, zone = fs[0], fs[1]
|
||||||
|
if zone == "" {
|
||||||
|
return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): no zone after zone specifier", s)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): too many zone specifiers", s) // TODO: less specific?
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4 by itself is easy to do in a helper.
|
||||||
|
if strings.Count(s, ":") == 0 {
|
||||||
|
if zone != "" {
|
||||||
|
return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): IPv4 addresses cannot have a zone", s)
|
||||||
|
}
|
||||||
|
return parseIPv4Slow(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
normal, err := normalizeIPv6Slow(s)
|
||||||
|
if err != nil {
|
||||||
|
return Addr{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we've normalized the address back into 8 hex
|
||||||
|
// fields of 16 bits each. Parse that.
|
||||||
|
fs = strings.Split(normal, ":")
|
||||||
|
if len(fs) != 8 {
|
||||||
|
return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): wrong size address", s)
|
||||||
|
}
|
||||||
|
var ret [16]byte
|
||||||
|
for i, f := range fs {
|
||||||
|
a, b, err := parseWord(f)
|
||||||
|
if err != nil {
|
||||||
|
return Addr{}, err
|
||||||
|
}
|
||||||
|
ret[i*2] = a
|
||||||
|
ret[i*2+1] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddrFrom16(ret).WithZone(zone), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeIPv6Slow expands s, which is assumed to be an IPv6
|
||||||
|
// address, to its canonical text form.
|
||||||
|
//
|
||||||
|
// The canonical form of an IPv6 address is 8 colon-separated fields,
|
||||||
|
// where each field should be a hex value from 0 to ffff. This
|
||||||
|
// function does not verify the contents of each field.
|
||||||
|
//
|
||||||
|
// This function performs two transformations:
|
||||||
|
// - The last 32 bits of an IPv6 address may be represented in
|
||||||
|
// IPv4-style dotted quad form, as in 1:2:3:4:5:6:7.8.9.10. That
|
||||||
|
// address is transformed to its hex equivalent,
|
||||||
|
// e.g. 1:2:3:4:5:6:708:90a.
|
||||||
|
// - An address may contain one "::", which expands into as many
|
||||||
|
// 16-bit blocks of zeros as needed to make the address its correct
|
||||||
|
// full size. For example, fe80::1:2 expands to fe80:0:0:0:0:0:1:2.
|
||||||
|
//
|
||||||
|
// Both short forms may be present in a single address,
|
||||||
|
// e.g. fe80::1.2.3.4.
|
||||||
|
func normalizeIPv6Slow(orig string) (string, error) {
|
||||||
|
s := orig
|
||||||
|
|
||||||
|
// Find and convert an IPv4 address in the final field, if any.
|
||||||
|
i := strings.LastIndex(s, ":")
|
||||||
|
if i == -1 {
|
||||||
|
return "", fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", orig)
|
||||||
|
}
|
||||||
|
if strings.Contains(s[i+1:], ".") {
|
||||||
|
ip, err := parseIPv4Slow(s[i+1:])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
a4 := ip.As4()
|
||||||
|
s = fmt.Sprintf("%s:%02x%02x:%02x%02x", s[:i], a4[0], a4[1], a4[2], a4[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and expand a ::, if any.
|
||||||
|
fs := strings.Split(s, "::")
|
||||||
|
switch len(fs) {
|
||||||
|
case 1:
|
||||||
|
// No ::, nothing to do.
|
||||||
|
case 2:
|
||||||
|
lhs, rhs := fs[0], fs[1]
|
||||||
|
// Found a ::, figure out how many zero blocks need to be
|
||||||
|
// inserted.
|
||||||
|
nblocks := strings.Count(lhs, ":") + strings.Count(rhs, ":")
|
||||||
|
if lhs != "" {
|
||||||
|
nblocks++
|
||||||
|
}
|
||||||
|
if rhs != "" {
|
||||||
|
nblocks++
|
||||||
|
}
|
||||||
|
if nblocks > 7 {
|
||||||
|
return "", fmt.Errorf("netaddr.ParseIP(%q): address too long", orig)
|
||||||
|
}
|
||||||
|
fs = nil
|
||||||
|
// Either side of the :: can be empty. We don't want empty
|
||||||
|
// fields to feature in the final normalized address.
|
||||||
|
if lhs != "" {
|
||||||
|
fs = append(fs, lhs)
|
||||||
|
}
|
||||||
|
fs = append(fs, zeros[:8-nblocks]...)
|
||||||
|
if rhs != "" {
|
||||||
|
fs = append(fs, rhs)
|
||||||
|
}
|
||||||
|
s = strings.Join(fs, ":")
|
||||||
|
default:
|
||||||
|
// Too many ::
|
||||||
|
return "", fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseIPv4Slow parses and returns an IPv4 address in dotted quad
|
||||||
|
// form, e.g. "192.168.0.1". It is slow but easy to read, and the
|
||||||
|
// reference implementation against which we compare faster
|
||||||
|
// implementations for correctness.
|
||||||
|
func parseIPv4Slow(s string) (Addr, error) {
|
||||||
|
fs := strings.Split(s, ".")
|
||||||
|
if len(fs) != 4 {
|
||||||
|
return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", s)
|
||||||
|
}
|
||||||
|
var ret [4]byte
|
||||||
|
for i := range ret {
|
||||||
|
val, err := strconv.ParseUint(fs[i], 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
return Addr{}, err
|
||||||
|
}
|
||||||
|
ret[i] = uint8(val)
|
||||||
|
}
|
||||||
|
return AddrFrom4([4]byte{ret[0], ret[1], ret[2], ret[3]}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseWord converts a 16-bit hex string into its corresponding
|
||||||
|
// two-byte value.
|
||||||
|
func parseWord(s string) (byte, byte, error) {
|
||||||
|
ret, err := strconv.ParseUint(s, 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return uint8(ret >> 8), uint8(ret), nil
|
||||||
|
}
|
92
src/net/netip/uint128.go
Normal file
92
src/net/netip/uint128.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package netip
|
||||||
|
|
||||||
|
import "math/bits"
|
||||||
|
|
||||||
|
// uint128 represents a uint128 using two uint64s.
|
||||||
|
//
|
||||||
|
// When the methods below mention a bit number, bit 0 is the most
|
||||||
|
// significant bit (in hi) and bit 127 is the lowest (lo&1).
|
||||||
|
type uint128 struct {
|
||||||
|
hi uint64
|
||||||
|
lo uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// mask6 returns a uint128 bitmask with the topmost n bits of a
|
||||||
|
// 128-bit number.
|
||||||
|
func mask6(n int) uint128 {
|
||||||
|
return uint128{^(^uint64(0) >> n), ^uint64(0) << (128 - n)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isZero reports whether u == 0.
|
||||||
|
//
|
||||||
|
// It's faster than u == (uint128{}) because the compiler (as of Go
|
||||||
|
// 1.15/1.16b1) doesn't do this trick and instead inserts a branch in
|
||||||
|
// its eq alg's generated code.
|
||||||
|
func (u uint128) isZero() bool { return u.hi|u.lo == 0 }
|
||||||
|
|
||||||
|
// and returns the bitwise AND of u and m (u&m).
|
||||||
|
func (u uint128) and(m uint128) uint128 {
|
||||||
|
return uint128{u.hi & m.hi, u.lo & m.lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// xor returns the bitwise XOR of u and m (u^m).
|
||||||
|
func (u uint128) xor(m uint128) uint128 {
|
||||||
|
return uint128{u.hi ^ m.hi, u.lo ^ m.lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// or returns the bitwise OR of u and m (u|m).
|
||||||
|
func (u uint128) or(m uint128) uint128 {
|
||||||
|
return uint128{u.hi | m.hi, u.lo | m.lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not returns the bitwise NOT of u.
|
||||||
|
func (u uint128) not() uint128 {
|
||||||
|
return uint128{^u.hi, ^u.lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// subOne returns u - 1.
|
||||||
|
func (u uint128) subOne() uint128 {
|
||||||
|
lo, borrow := bits.Sub64(u.lo, 1, 0)
|
||||||
|
return uint128{u.hi - borrow, lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addOne returns u + 1.
|
||||||
|
func (u uint128) addOne() uint128 {
|
||||||
|
lo, carry := bits.Add64(u.lo, 1, 0)
|
||||||
|
return uint128{u.hi + carry, lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func u64CommonPrefixLen(a, b uint64) uint8 {
|
||||||
|
return uint8(bits.LeadingZeros64(a ^ b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u uint128) commonPrefixLen(v uint128) (n uint8) {
|
||||||
|
if n = u64CommonPrefixLen(u.hi, v.hi); n == 64 {
|
||||||
|
n += u64CommonPrefixLen(u.lo, v.lo)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// halves returns the two uint64 halves of the uint128.
|
||||||
|
//
|
||||||
|
// Logically, think of it as returning two uint64s.
|
||||||
|
// It only returns pointers for inlining reasons on 32-bit platforms.
|
||||||
|
func (u *uint128) halves() [2]*uint64 {
|
||||||
|
return [2]*uint64{&u.hi, &u.lo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitsSetFrom returns a copy of u with the given bit
|
||||||
|
// and all subsequent ones set.
|
||||||
|
func (u uint128) bitsSetFrom(bit uint8) uint128 {
|
||||||
|
return u.or(mask6(int(bit)).not())
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitsClearedFrom returns a copy of u with the given bit
|
||||||
|
// and all subsequent ones cleared.
|
||||||
|
func (u uint128) bitsClearedFrom(bit uint8) uint128 {
|
||||||
|
return u.and(mask6(int(bit)))
|
||||||
|
}
|
89
src/net/netip/uint128_test.go
Normal file
89
src/net/netip/uint128_test.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package netip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUint128AddSub(t *testing.T) {
|
||||||
|
const add1 = 1
|
||||||
|
const sub1 = -1
|
||||||
|
tests := []struct {
|
||||||
|
in uint128
|
||||||
|
op int // +1 or -1 to add vs subtract
|
||||||
|
want uint128
|
||||||
|
}{
|
||||||
|
{uint128{0, 0}, add1, uint128{0, 1}},
|
||||||
|
{uint128{0, 1}, add1, uint128{0, 2}},
|
||||||
|
{uint128{1, 0}, add1, uint128{1, 1}},
|
||||||
|
{uint128{0, ^uint64(0)}, add1, uint128{1, 0}},
|
||||||
|
{uint128{^uint64(0), ^uint64(0)}, add1, uint128{0, 0}},
|
||||||
|
|
||||||
|
{uint128{0, 0}, sub1, uint128{^uint64(0), ^uint64(0)}},
|
||||||
|
{uint128{0, 1}, sub1, uint128{0, 0}},
|
||||||
|
{uint128{0, 2}, sub1, uint128{0, 1}},
|
||||||
|
{uint128{1, 0}, sub1, uint128{0, ^uint64(0)}},
|
||||||
|
{uint128{1, 1}, sub1, uint128{1, 0}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
var got uint128
|
||||||
|
switch tt.op {
|
||||||
|
case add1:
|
||||||
|
got = tt.in.addOne()
|
||||||
|
case sub1:
|
||||||
|
got = tt.in.subOne()
|
||||||
|
default:
|
||||||
|
panic("bogus op")
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("%v add %d = %v; want %v", tt.in, tt.op, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBitsSetFrom(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
bit uint8
|
||||||
|
want uint128
|
||||||
|
}{
|
||||||
|
{0, uint128{^uint64(0), ^uint64(0)}},
|
||||||
|
{1, uint128{^uint64(0) >> 1, ^uint64(0)}},
|
||||||
|
{63, uint128{1, ^uint64(0)}},
|
||||||
|
{64, uint128{0, ^uint64(0)}},
|
||||||
|
{65, uint128{0, ^uint64(0) >> 1}},
|
||||||
|
{127, uint128{0, 1}},
|
||||||
|
{128, uint128{0, 0}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
var zero uint128
|
||||||
|
got := zero.bitsSetFrom(tt.bit)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("0.bitsSetFrom(%d) = %064b want %064b", tt.bit, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBitsClearedFrom(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
bit uint8
|
||||||
|
want uint128
|
||||||
|
}{
|
||||||
|
{0, uint128{0, 0}},
|
||||||
|
{1, uint128{1 << 63, 0}},
|
||||||
|
{63, uint128{^uint64(0) &^ 1, 0}},
|
||||||
|
{64, uint128{^uint64(0), 0}},
|
||||||
|
{65, uint128{^uint64(0), 1 << 63}},
|
||||||
|
{127, uint128{^uint64(0), ^uint64(0) &^ 1}},
|
||||||
|
{128, uint128{^uint64(0), ^uint64(0)}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
ones := uint128{^uint64(0), ^uint64(0)}
|
||||||
|
got := ones.bitsClearedFrom(tt.bit)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("ones.bitsClearedFrom(%d) = %064b want %064b", tt.bit, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -341,26 +341,3 @@ func readFull(r io.Reader) (all []byte, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// goDebugString returns the value of the named GODEBUG key.
|
|
||||||
// GODEBUG is of the form "key=val,key2=val2"
|
|
||||||
func goDebugString(key string) string {
|
|
||||||
s := os.Getenv("GODEBUG")
|
|
||||||
for i := 0; i < len(s)-len(key)-1; i++ {
|
|
||||||
if i > 0 && s[i-1] != ',' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
afterKey := s[i+len(key):]
|
|
||||||
if afterKey[0] != '=' || s[i:i+len(key)] != key {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val := afterKey[1:]
|
|
||||||
for i, b := range val {
|
|
||||||
if b == ',' {
|
|
||||||
return val[:i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,33 +51,6 @@ func TestReadLine(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGoDebugString(t *testing.T) {
|
|
||||||
defer os.Setenv("GODEBUG", os.Getenv("GODEBUG"))
|
|
||||||
tests := []struct {
|
|
||||||
godebug string
|
|
||||||
key string
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"", "foo", ""},
|
|
||||||
{"foo=", "foo", ""},
|
|
||||||
{"foo=bar", "foo", "bar"},
|
|
||||||
{"foo=bar,", "foo", "bar"},
|
|
||||||
{"foo,foo=bar,", "foo", "bar"},
|
|
||||||
{"foo1=bar,foo=bar,", "foo", "bar"},
|
|
||||||
{"foo=bar,foo=bar,", "foo", "bar"},
|
|
||||||
{"foo=", "foo", ""},
|
|
||||||
{"foo", "foo", ""},
|
|
||||||
{",foo", "foo", ""},
|
|
||||||
{"foo=bar,baz", "loooooooong", ""},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
os.Setenv("GODEBUG", tt.godebug)
|
|
||||||
if got := goDebugString(tt.key); got != tt.want {
|
|
||||||
t.Errorf("for %q, goDebugString(%q) = %q; want %q", tt.godebug, tt.key, got, tt.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDtoi(t *testing.T) {
|
func TestDtoi(t *testing.T) {
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
in string
|
in string
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"internal/itoa"
|
"internal/itoa"
|
||||||
"io"
|
"io"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -23,6 +24,20 @@ type TCPAddr struct {
|
||||||
Zone string // IPv6 scoped addressing zone
|
Zone string // IPv6 scoped addressing zone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddrPort returns the TCPAddr a as a netip.AddrPort.
|
||||||
|
//
|
||||||
|
// If a.Port does not fit in a uint16, it's silently truncated.
|
||||||
|
//
|
||||||
|
// If a is nil, a zero value is returned.
|
||||||
|
func (a *TCPAddr) AddrPort() netip.AddrPort {
|
||||||
|
if a == nil {
|
||||||
|
return netip.AddrPort{}
|
||||||
|
}
|
||||||
|
na, _ := netip.AddrFromSlice(a.IP)
|
||||||
|
na = na.WithZone(a.Zone)
|
||||||
|
return netip.AddrPortFrom(na, uint16(a.Port))
|
||||||
|
}
|
||||||
|
|
||||||
// Network returns the address's network name, "tcp".
|
// Network returns the address's network name, "tcp".
|
||||||
func (a *TCPAddr) Network() string { return "tcp" }
|
func (a *TCPAddr) Network() string { return "tcp" }
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ package net
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"internal/itoa"
|
"internal/itoa"
|
||||||
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,6 +27,20 @@ type UDPAddr struct {
|
||||||
Zone string // IPv6 scoped addressing zone
|
Zone string // IPv6 scoped addressing zone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddrPort returns the UDPAddr a as a netip.AddrPort.
|
||||||
|
//
|
||||||
|
// If a.Port does not fit in a uint16, it's silently truncated.
|
||||||
|
//
|
||||||
|
// If a is nil, a zero value is returned.
|
||||||
|
func (a *UDPAddr) AddrPort() netip.AddrPort {
|
||||||
|
if a == nil {
|
||||||
|
return netip.AddrPort{}
|
||||||
|
}
|
||||||
|
na, _ := netip.AddrFromSlice(a.IP)
|
||||||
|
na = na.WithZone(a.Zone)
|
||||||
|
return netip.AddrPortFrom(na, uint16(a.Port))
|
||||||
|
}
|
||||||
|
|
||||||
// Network returns the address's network name, "udp".
|
// Network returns the address's network name, "udp".
|
||||||
func (a *UDPAddr) Network() string { return "udp" }
|
func (a *UDPAddr) Network() string { return "udp" }
|
||||||
|
|
||||||
|
@ -84,6 +99,21 @@ func ResolveUDPAddr(network, address string) (*UDPAddr, error) {
|
||||||
return addrs.forResolve(network, address).(*UDPAddr), nil
|
return addrs.forResolve(network, address).(*UDPAddr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UDPAddrFromAddrPort returns addr as a UDPAddr.
|
||||||
|
//
|
||||||
|
// If addr is not valid, it returns nil.
|
||||||
|
func UDPAddrFromAddrPort(addr netip.AddrPort) *UDPAddr {
|
||||||
|
if !addr.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ip16 := addr.Addr().As16()
|
||||||
|
return &UDPAddr{
|
||||||
|
IP: IP(ip16[:]),
|
||||||
|
Zone: addr.Addr().Zone(),
|
||||||
|
Port: int(addr.Port()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UDPConn is the implementation of the Conn and PacketConn interfaces
|
// UDPConn is the implementation of the Conn and PacketConn interfaces
|
||||||
// for UDP network connections.
|
// for UDP network connections.
|
||||||
type UDPConn struct {
|
type UDPConn struct {
|
||||||
|
@ -148,6 +178,18 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadMsgUDPAddrPort is like ReadMsgUDP but returns an netip.AddrPort instead of a UDPAddr.
|
||||||
|
func (c *UDPConn) ReadMsgUDPAddrPort(b, oob []byte) (n, oobn, flags int, addr netip.AddrPort, err error) {
|
||||||
|
// TODO(bradfitz): make this efficient, making the internal net package
|
||||||
|
// type throughout be netip.Addr and only converting to the net.IP slice
|
||||||
|
// version at the edge. But for now (2021-10-20), this is a wrapper around
|
||||||
|
// the old way.
|
||||||
|
var ua *UDPAddr
|
||||||
|
n, oobn, flags, ua, err = c.ReadMsgUDP(b, oob)
|
||||||
|
addr = ua.AddrPort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// WriteToUDP acts like WriteTo but takes a UDPAddr.
|
// WriteToUDP acts like WriteTo but takes a UDPAddr.
|
||||||
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
|
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
|
||||||
if !c.ok() {
|
if !c.ok() {
|
||||||
|
@ -160,6 +202,15 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteToUDPAddrPort acts like WriteTo but takes a netip.AddrPort.
|
||||||
|
func (c *UDPConn) WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (int, error) {
|
||||||
|
// TODO(bradfitz): make this efficient, making the internal net package
|
||||||
|
// type throughout be netip.Addr and only converting to the net.IP slice
|
||||||
|
// version at the edge. But for now (2021-10-20), this is a wrapper around
|
||||||
|
// the old way.
|
||||||
|
return c.WriteToUDP(b, UDPAddrFromAddrPort(addr))
|
||||||
|
}
|
||||||
|
|
||||||
// WriteTo implements the PacketConn WriteTo method.
|
// WriteTo implements the PacketConn WriteTo method.
|
||||||
func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
|
func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
|
||||||
if !c.ok() {
|
if !c.ok() {
|
||||||
|
@ -195,6 +246,15 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteMsgUDPAddrPort is like WriteMsgUDP but takes a netip.AddrPort instead of a UDPAddr.
|
||||||
|
func (c *UDPConn) WriteMsgUDPAddrPort(b, oob []byte, addr netip.AddrPort) (n, oobn int, err error) {
|
||||||
|
// TODO(bradfitz): make this efficient, making the internal net package
|
||||||
|
// type throughout be netip.Addr and only converting to the net.IP slice
|
||||||
|
// version at the edge. But for now (2021-10-20), this is a wrapper around
|
||||||
|
// the old way.
|
||||||
|
return c.WriteMsgUDP(b, oob, UDPAddrFromAddrPort(addr))
|
||||||
|
}
|
||||||
|
|
||||||
func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
|
func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
|
||||||
|
|
||||||
// DialUDP acts like Dial for UDP networks.
|
// DialUDP acts like Dial for UDP networks.
|
||||||
|
|
Loading…
Reference in a new issue