Extend tcpip.Address.String to ipv6 addresses

PiperOrigin-RevId: 214039349
diff --git a/tcpip/tcpip.go b/tcpip/tcpip.go
index 01cd908..ec6c5af 100644
--- a/tcpip/tcpip.go
+++ b/tcpip/tcpip.go
@@ -666,6 +666,43 @@
 	switch len(a) {
 	case 4:
 		return fmt.Sprintf("%d.%d.%d.%d", int(a[0]), int(a[1]), int(a[2]), int(a[3]))
+	case 16:
+		// Find the longest subsequence of hexadecimal zeros.
+		start, end := -1, -1
+		for i := 0; i < len(a); i += 2 {
+			j := i
+			for j < len(a) && a[j] == 0 && a[j+1] == 0 {
+				j += 2
+			}
+			if j > i+2 && j-i > end-start {
+				start, end = i, j
+			}
+		}
+
+		var b strings.Builder
+		for i := 0; i < len(a); i += 2 {
+			if i == start {
+				b.WriteString("::")
+				i = end
+				if end >= len(a) {
+					break
+				}
+			} else if i > 0 {
+				b.WriteByte(':')
+			}
+			v := uint16(a[i+0])<<8 | uint16(a[i+1])
+			if v == 0 {
+				b.WriteByte('0')
+			} else {
+				const digits = "0123456789abcdef"
+				for i := uint(3); i < 4; i-- {
+					if v := v >> (i * 4); v != 0 {
+						b.WriteByte(digits[v&0xf])
+					}
+				}
+			}
+		}
+		return b.String()
 	default:
 		return fmt.Sprintf("%x", []byte(a))
 	}
diff --git a/tcpip/tcpip_test.go b/tcpip/tcpip_test.go
index 6bf82d6..9b20c74 100644
--- a/tcpip/tcpip_test.go
+++ b/tcpip/tcpip_test.go
@@ -15,6 +15,7 @@
 package tcpip
 
 import (
+	"net"
 	"testing"
 )
 
@@ -138,3 +139,57 @@
 		}
 	}
 }
+
+func TestAddressString(t *testing.T) {
+	for _, want := range []string{
+		// Taken from stdlib.
+		"2001:db8::123:12:1",
+		"2001:db8::1",
+		"2001:db8:0:1:0:1:0:1",
+		"2001:db8:1:0:1:0:1:0",
+		"2001::1:0:0:1",
+		"2001:db8:0:0:1::",
+		"2001:db8::1:0:0:1",
+		"2001:db8::a:b:c:d",
+
+		// Leading zeros.
+		"::1",
+		// Trailing zeros.
+		"8::",
+		// No zeros.
+		"1:1:1:1:1:1:1:1",
+		// Longer sequence is after other zeros, but not at the end.
+		"1:0:0:1::1",
+		// Longer sequence is at the beginning, shorter sequence is at
+		// the end.
+		"::1:1:1:0:0",
+		// Longer sequence is not at the beginning, shorter sequence is
+		// at the end.
+		"1::1:1:0:0",
+		// Longer sequence is at the beginning, shorter sequence is not
+		// at the end.
+		"::1:1:0:0:1",
+		// Neither sequence is at an end, longer is after shorter.
+		"1:0:0:1::1",
+		// Shorter sequence is at the beginning, longer sequence is not
+		// at the end.
+		"0:0:1:1::1",
+		// Shorter sequence is at the beginning, longer sequence is at
+		// the end.
+		"0:0:1:1:1::",
+		// Short sequences at both ends, longer one in the middle.
+		"0:1:1::1:1:0",
+		// Short sequences at both ends, longer one in the middle.
+		"0:1::1:0:0",
+		// Short sequences at both ends, longer one in the middle.
+		"0:0:1::1:0",
+		// Longer sequence surrounded by shorter sequences, but none at
+		// the end.
+		"1:0:1::1:0:1",
+	} {
+		addr := Address(net.ParseIP(want))
+		if got := addr.String(); got != want {
+			t.Errorf("Address(%x).String() = '%s', want = '%s'", addr, got, want)
+		}
+	}
+}