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)
-}