slog: update value to go 1.20 unsafe functions; remove safe version

Bring value.go in line with the standard library version:

- Use the new unsafe functions for string and slice data introduced
  in Go 1.20.

- Remove the version of Value that avoids unsafe code.

As of this commit, golang.org/x/exp/slog cannot be compiled
with Go versions before 1.20.

Change-Id: I45218aace8a498e94e90f87a337d3a09cae34318
Reviewed-on: https://go-review.googlesource.com/c/exp/+/486801
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/slog/value.go b/slog/value.go
index 5719976..0903a7c 100644
--- a/slog/value.go
+++ b/slog/value.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.20
+
 package slog
 
 import (
@@ -11,12 +13,35 @@
 	"strconv"
 	"strings"
 	"time"
+	"unsafe"
 
 	"golang.org/x/exp/slices"
 )
 
-// Definitions for Value.
-// The Value type itself can be found in value_{safe,unsafe}.go.
+// A Value can represent any Go value, but unlike type any,
+// it can represent most small values without an allocation.
+// The zero Value corresponds to nil.
+type Value struct {
+	_ [0]func() // disallow ==
+	// num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
+	// the string length for KindString, and nanoseconds since the epoch for KindTime.
+	num uint64
+	// If any is of type Kind, then the value is in num as described above.
+	// If any is of type *time.Location, then the Kind is Time and time.Time value
+	// can be constructed from the Unix nanos in num and the location (monotonic time
+	// is not preserved).
+	// If any is of type stringptr, then the Kind is String and the string value
+	// consists of the length in num and the pointer in any.
+	// Otherwise, the Kind is Any and any is the value.
+	// (This implies that Attrs cannot store values of type Kind, *time.Location
+	// or stringptr.)
+	any any
+}
+
+type (
+	stringptr *byte // used in Value.any when the Value is a string
+	groupptr  *Attr // used in Value.any when the Value is a []Attr
+)
 
 // Kind is the kind of a Value.
 type Kind int
@@ -61,8 +86,33 @@
 // (No user-provided value has this type.)
 type kind Kind
 
+// Kind returns v's Kind.
+func (v Value) Kind() Kind {
+	switch x := v.any.(type) {
+	case Kind:
+		return x
+	case stringptr:
+		return KindString
+	case timeLocation:
+		return KindTime
+	case groupptr:
+		return KindGroup
+	case LogValuer:
+		return KindLogValuer
+	case kind: // a kind is just a wrapper for a Kind
+		return KindAny
+	default:
+		return KindAny
+	}
+}
+
 //////////////// Constructors
 
+// StringValue returns a new Value for a string.
+func StringValue(value string) Value {
+	return Value{num: uint64(len(value)), any: stringptr(unsafe.StringData(value))}
+}
+
 // IntValue returns a Value for an int.
 func IntValue(v int) Value {
 	return Int64Value(int64(v))
@@ -117,7 +167,7 @@
 // GroupValue returns a new Value for a list of Attrs.
 // The caller must not subsequently mutate the argument slice.
 func GroupValue(as ...Attr) Value {
-	return groupValue(as)
+	return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))}
 }
 
 // AnyValue returns a Value for the supplied value.
@@ -195,7 +245,7 @@
 	case KindLogValuer:
 		return v.any
 	case KindGroup:
-		return v.uncheckedGroup()
+		return v.group()
 	case KindInt64:
 		return int64(v.num)
 	case KindUint64:
@@ -215,6 +265,21 @@
 	}
 }
 
+// String returns Value's value as a string, formatted like fmt.Sprint. Unlike
+// the methods Int64, Float64, and so on, which panic if v is of the
+// wrong kind, String never panics.
+func (v Value) String() string {
+	if sp, ok := v.any.(stringptr); ok {
+		return unsafe.String(sp, v.num)
+	}
+	var buf []byte
+	return string(v.append(buf))
+}
+
+func (v Value) str() string {
+	return unsafe.String(v.any.(stringptr), v.num)
+}
+
 // Int64 returns v's value as an int64. It panics
 // if v is not a signed integer.
 func (v Value) Int64() int64 {
@@ -300,12 +365,19 @@
 // Group returns v's value as a []Attr.
 // It panics if v's Kind is not KindGroup.
 func (v Value) Group() []Attr {
-	return v.group()
+	if sp, ok := v.any.(groupptr); ok {
+		return unsafe.Slice((*Attr)(sp), v.num)
+	}
+	panic("Group: bad kind")
+}
+
+func (v Value) group() []Attr {
+	return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num)
 }
 
 //////////////// Other
 
-// Equal reports whether v and w have equal keys and values.
+// Equal reports whether v and w represent the same Go value.
 func (v Value) Equal(w Value) bool {
 	k1 := v.Kind()
 	k2 := w.Kind()
@@ -324,7 +396,7 @@
 	case KindAny, KindLogValuer:
 		return v.any == w.any // may panic if non-comparable
 	case KindGroup:
-		return slices.EqualFunc(v.uncheckedGroup(), w.uncheckedGroup(), Attr.Equal)
+		return slices.EqualFunc(v.group(), w.group(), Attr.Equal)
 	default:
 		panic(fmt.Sprintf("bad kind: %s", k1))
 	}
diff --git a/slog/value_safe.go b/slog/value_safe.go
deleted file mode 100644
index 8b0a92d..0000000
--- a/slog/value_safe.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2022 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.
-
-//go:build safe_values
-
-package slog
-
-// This file defines the most portable representation of Value.
-
-// A Value can represent any Go value, but unlike type any,
-// it can represent most small values without an allocation.
-// The zero Value corresponds to nil.
-type Value struct {
-	// num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
-	// and nanoseconds since the epoch for KindTime.
-	num uint64
-	// s holds the value for KindString.
-	s string
-	// If any is of type Kind, then the value is in num or s as described above.
-	// If any is of type *time.Location, then the Kind is Time and time.Time
-	// value can be constructed from the Unix nanos in num and the location
-	// (monotonic time is not preserved).
-	// Otherwise, the Kind is Any and any is the value.
-	// (This implies that Values cannot store Kinds or *time.Locations.)
-	any any
-}
-
-// Kind returns v's Kind.
-func (v Value) Kind() Kind {
-	switch k := v.any.(type) {
-	case Kind:
-		return k
-	case timeLocation:
-		return KindTime
-	case []Attr:
-		return KindGroup
-	case LogValuer:
-		return KindLogValuer
-	case kind: // a kind is just a wrapper for a Kind
-		return KindAny
-	default:
-		return KindAny
-	}
-}
-
-func (v Value) str() string {
-	return v.s
-}
-
-// StringValue returns a new Value for a string.
-func StringValue(value string) Value {
-	return Value{s: value, any: KindString}
-}
-
-// String returns Value's value as a string, formatted like fmt.Sprint. Unlike
-// the methods Int64, Float64, and so on, which panic if v is of the
-// wrong kind, String never panics.
-func (v Value) String() string {
-	if v.Kind() == KindString {
-		return v.str()
-	}
-	var buf []byte
-	return string(v.append(buf))
-}
-
-func groupValue(as []Attr) Value {
-	return Value{any: as}
-}
-
-func (v Value) group() []Attr {
-	return v.any.([]Attr)
-}
-
-func (v Value) uncheckedGroup() []Attr { return v.group() }
diff --git a/slog/value_unsafe.go b/slog/value_unsafe.go
deleted file mode 100644
index f4276f9..0000000
--- a/slog/value_unsafe.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2022 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.
-
-//go:build !safe_values
-
-package slog
-
-// This file defines the most compact representation of Value.
-
-import (
-	"reflect"
-	"unsafe"
-)
-
-// A Value can represent any Go value, but unlike type any,
-// it can represent most small values without an allocation.
-// The zero Value corresponds to nil.
-type Value struct {
-	// num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
-	// the string length for KindString, and nanoseconds since the epoch for KindTime.
-	num uint64
-	// If any is of type Kind, then the value is in num as described above.
-	// If any is of type *time.Location, then the Kind is Time and time.Time value
-	// can be constructed from the Unix nanos in num and the location (monotonic time
-	// is not preserved).
-	// If any is of type stringptr, then the Kind is String and the string value
-	// consists of the length in num and the pointer in any.
-	// Otherwise, the Kind is Any and any is the value.
-	// (This implies that Attrs cannot store values of type Kind, *time.Location
-	// or stringptr.)
-	any any
-}
-
-type (
-	stringptr unsafe.Pointer // used in Value.any when the Value is a string
-	groupptr  unsafe.Pointer // used in Value.any when the Value is a []Attr
-)
-
-// Kind returns v's Kind.
-func (v Value) Kind() Kind {
-	switch x := v.any.(type) {
-	case Kind:
-		return x
-	case stringptr:
-		return KindString
-	case timeLocation:
-		return KindTime
-	case groupptr:
-		return KindGroup
-	case LogValuer:
-		return KindLogValuer
-	case kind: // a kind is just a wrapper for a Kind
-		return KindAny
-	default:
-		return KindAny
-	}
-}
-
-// StringValue returns a new Value for a string.
-func StringValue(value string) Value {
-	hdr := (*reflect.StringHeader)(unsafe.Pointer(&value))
-	return Value{num: uint64(hdr.Len), any: stringptr(hdr.Data)}
-}
-
-func (v Value) str() string {
-	var s string
-	hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
-	hdr.Data = uintptr(v.any.(stringptr))
-	hdr.Len = int(v.num)
-	return s
-}
-
-// String returns Value's value as a string, formatted like fmt.Sprint. Unlike
-// the methods Int64, Float64, and so on, which panic if v is of the
-// wrong kind, String never panics.
-func (v Value) String() string {
-	if sp, ok := v.any.(stringptr); ok {
-		// Inlining this code makes a huge difference.
-		var s string
-		hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
-		hdr.Data = uintptr(sp)
-		hdr.Len = int(v.num)
-		return s
-	}
-	var buf []byte
-	return string(v.append(buf))
-}
-
-func groupValue(as []Attr) Value {
-	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&as))
-	return Value{num: uint64(hdr.Len), any: groupptr(hdr.Data)}
-}
-
-// group returns the Value's value as a []Attr.
-// It panics if the Value's Kind is not KindGroup.
-func (v Value) group() []Attr {
-	if sp, ok := v.any.(groupptr); ok {
-		return unsafe.Slice((*Attr)(sp), v.num)
-	}
-	panic("Group: bad kind")
-}
-
-func (v Value) uncheckedGroup() []Attr {
-	return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num)
-}