unix,windows,plan9: add Byte{Slice,Ptr}ToString functions

We already provide ByteSliceFromString and BytePtrFromString, and on
Windows we provide UTF16FromString, UTF16PtrFromString, UTF16ToString,
and UTF16PtrToString. So this commit fills in the remaining two
Byte-oriented functions: ByteSliceToString and BytePtrToString. Since
the existing two are available on windows, unix, and plan9, we add the
remaining two to the same places. This helps eliminate unsafe pointer
options in addition to triggering checkptr in Go 1.15, by eliminating
the use of prior idioms for these types of casts.

Change-Id: I85ae49f2756e142c2462fe8b2428216b6992e089
Reviewed-on: https://go-review.googlesource.com/c/sys/+/263757
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Brad Fitzpatrick <bradfitz@golang.org>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/plan9/syscall.go b/plan9/syscall.go
index 163254c..e7363a2 100644
--- a/plan9/syscall.go
+++ b/plan9/syscall.go
@@ -24,16 +24,20 @@
 // holds a value of type syscall.ErrorString.
 package plan9 // import "golang.org/x/sys/plan9"
 
-import "unsafe"
+import (
+	"bytes"
+	"strings"
+	"unsafe"
+
+	"golang.org/x/sys/internal/unsafeheader"
+)
 
 // ByteSliceFromString returns a NUL-terminated slice of bytes
 // containing the text of s. If s contains a NUL byte at any
 // location, it returns (nil, EINVAL).
 func ByteSliceFromString(s string) ([]byte, error) {
-	for i := 0; i < len(s); i++ {
-		if s[i] == 0 {
-			return nil, EINVAL
-		}
+	if strings.IndexByte(s, 0) != -1 {
+		return nil, EINVAL
 	}
 	a := make([]byte, len(s)+1)
 	copy(a, s)
@@ -51,6 +55,41 @@
 	return &a[0], nil
 }
 
+// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any
+// bytes after the NUL removed.
+func ByteSliceToString(s []byte) string {
+	if i := bytes.IndexByte(s, 0); i != -1 {
+		s = s[:i]
+	}
+	return string(s)
+}
+
+// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
+// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated
+// at a zero byte; if the zero byte is not present, the program may crash.
+func BytePtrToString(p *byte) string {
+	if p == nil {
+		return ""
+	}
+	if *p == 0 {
+		return ""
+	}
+
+	// Find NUL terminator.
+	n := 0
+	for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
+		ptr = unsafe.Pointer(uintptr(ptr) + 1)
+	}
+
+	var s []byte
+	h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
+	h.Data = unsafe.Pointer(p)
+	h.Len = n
+	h.Cap = n
+
+	return string(s)
+}
+
 // Single-word zero for use when we need a valid pointer to 0 bytes.
 // See mksyscall.pl.
 var _zero uintptr
diff --git a/unix/syscall.go b/unix/syscall.go
index fd4ee8e..ab75ef9 100644
--- a/unix/syscall.go
+++ b/unix/syscall.go
@@ -24,7 +24,13 @@
 // holds a value of type syscall.Errno.
 package unix // import "golang.org/x/sys/unix"
 
-import "strings"
+import (
+	"bytes"
+	"strings"
+	"unsafe"
+
+	"golang.org/x/sys/internal/unsafeheader"
+)
 
 // ByteSliceFromString returns a NUL-terminated slice of bytes
 // containing the text of s. If s contains a NUL byte at any
@@ -49,5 +55,40 @@
 	return &a[0], nil
 }
 
+// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any
+// bytes after the NUL removed.
+func ByteSliceToString(s []byte) string {
+	if i := bytes.IndexByte(s, 0); i != -1 {
+		s = s[:i]
+	}
+	return string(s)
+}
+
+// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
+// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated
+// at a zero byte; if the zero byte is not present, the program may crash.
+func BytePtrToString(p *byte) string {
+	if p == nil {
+		return ""
+	}
+	if *p == 0 {
+		return ""
+	}
+
+	// Find NUL terminator.
+	n := 0
+	for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
+		ptr = unsafe.Pointer(uintptr(ptr) + 1)
+	}
+
+	var s []byte
+	h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
+	h.Data = unsafe.Pointer(p)
+	h.Len = n
+	h.Cap = n
+
+	return string(s)
+}
+
 // Single-word zero for use when we need a valid pointer to 0 bytes.
 var _zero uintptr
diff --git a/windows/syscall.go b/windows/syscall.go
index af828a9..6122f55 100644
--- a/windows/syscall.go
+++ b/windows/syscall.go
@@ -25,17 +25,20 @@
 package windows // import "golang.org/x/sys/windows"
 
 import (
+	"bytes"
+	"strings"
 	"syscall"
+	"unsafe"
+
+	"golang.org/x/sys/internal/unsafeheader"
 )
 
 // ByteSliceFromString returns a NUL-terminated slice of bytes
 // containing the text of s. If s contains a NUL byte at any
 // location, it returns (nil, syscall.EINVAL).
 func ByteSliceFromString(s string) ([]byte, error) {
-	for i := 0; i < len(s); i++ {
-		if s[i] == 0 {
-			return nil, syscall.EINVAL
-		}
+	if strings.IndexByte(s, 0) != -1 {
+		return nil, syscall.EINVAL
 	}
 	a := make([]byte, len(s)+1)
 	copy(a, s)
@@ -53,6 +56,41 @@
 	return &a[0], nil
 }
 
+// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any
+// bytes after the NUL removed.
+func ByteSliceToString(s []byte) string {
+	if i := bytes.IndexByte(s, 0); i != -1 {
+		s = s[:i]
+	}
+	return string(s)
+}
+
+// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
+// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated
+// at a zero byte; if the zero byte is not present, the program may crash.
+func BytePtrToString(p *byte) string {
+	if p == nil {
+		return ""
+	}
+	if *p == 0 {
+		return ""
+	}
+
+	// Find NUL terminator.
+	n := 0
+	for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
+		ptr = unsafe.Pointer(uintptr(ptr) + 1)
+	}
+
+	var s []byte
+	h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
+	h.Data = unsafe.Pointer(p)
+	h.Len = n
+	h.Cap = n
+
+	return string(s)
+}
+
 // Single-word zero for use when we need a valid pointer to 0 bytes.
 // See mksyscall.pl.
 var _zero uintptr
diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go
index bbd075d..cd71f57 100644
--- a/windows/syscall_windows.go
+++ b/windows/syscall_windows.go
@@ -92,11 +92,11 @@
 }
 
 // UTF16ToString returns the UTF-8 encoding of the UTF-16 sequence s,
-// with a terminating NUL removed.
+// with a terminating NUL and any bytes after the NUL removed.
 func UTF16ToString(s []uint16) string {
 	for i, v := range s {
 		if v == 0 {
-			s = s[0:i]
+			s = s[:i]
 			break
 		}
 	}
@@ -120,7 +120,7 @@
 }
 
 // UTF16PtrToString takes a pointer to a UTF-16 sequence and returns the corresponding UTF-8 encoded string.
-// If the pointer is nil, this returns the empty string. This assumes that the UTF-16 sequence is terminated
+// If the pointer is nil, it returns the empty string. It assumes that the UTF-16 sequence is terminated
 // at a zero word; if the zero word is not present, the program may crash.
 func UTF16PtrToString(p *uint16) string {
 	if p == nil {