slog: support go 1.19
To allow slog to work on Go 1.19, restore the 1.19-compatible code we
were using previously for string and slice headers.
slogtest never supported 1.19, but slog depends on it. So replace
errors.Join with a simple workaround.
Change-Id: I30ca95b9b023a839a8a2e566c3566418ebd07392
Reviewed-on: https://go-review.googlesource.com/c/exp/+/495975
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/slog/slogtest/slogtest.go b/slog/slogtest/slogtest.go
index e2f09e9..24b3513 100644
--- a/slog/slogtest/slogtest.go
+++ b/slog/slogtest/slogtest.go
@@ -2,7 +2,6 @@
import (
"context"
- "errors"
"fmt"
"reflect"
"runtime"
@@ -225,7 +224,7 @@
}
}
}
- return errors.Join(errs...)
+ return errorsJoin(errs...)
}
type check func(map[string]any) string
diff --git a/slog/slogtest/slogtest_119.go b/slog/slogtest/slogtest_119.go
new file mode 100644
index 0000000..21cd8b9
--- /dev/null
+++ b/slog/slogtest/slogtest_119.go
@@ -0,0 +1,27 @@
+// Copyright 2023 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 go1.19 && !go1.20
+
+package slogtest
+
+import (
+ "errors"
+ "strings"
+)
+
+func errorsJoin(errs ...error) error {
+ var b strings.Builder
+ for _, err := range errs {
+ if err != nil {
+ b.WriteString(err.Error())
+ b.WriteByte('\n')
+ }
+ }
+ s := b.String()
+ if len(s) == 0 {
+ return nil
+ }
+ return errors.New(s)
+}
diff --git a/slog/slogtest/slogtest_120.go b/slog/slogtest/slogtest_120.go
new file mode 100644
index 0000000..dfb3e41
--- /dev/null
+++ b/slog/slogtest/slogtest_120.go
@@ -0,0 +1,11 @@
+// Copyright 2023 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 go1.20
+
+package slogtest
+
+import "errors"
+
+var errorsJoin = errors.Join
diff --git a/slog/value.go b/slog/value.go
index 0903a7c..df9b047 100644
--- a/slog/value.go
+++ b/slog/value.go
@@ -2,8 +2,6 @@
// 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 (
@@ -38,11 +36,6 @@
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
@@ -108,11 +101,6 @@
//////////////// 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))
@@ -164,12 +152,6 @@
return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
}
-// 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 Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))}
-}
-
// AnyValue returns a Value for the supplied value.
//
// If the supplied value is of type Value, it is returned
@@ -265,21 +247,6 @@
}
}
-// 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 {
diff --git a/slog/value_119.go b/slog/value_119.go
new file mode 100644
index 0000000..29b0d73
--- /dev/null
+++ b/slog/value_119.go
@@ -0,0 +1,53 @@
+// 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 go1.19 && !go1.20
+
+package slog
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+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
+)
+
+// 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
+ }
+ return string(v.append(nil))
+}
+
+// GroupValue returns a new Value for a list of Attrs.
+// The caller must not subsequently mutate the argument slice.
+func GroupValue(as ...Attr) Value {
+ hdr := (*reflect.SliceHeader)(unsafe.Pointer(&as))
+ return Value{num: uint64(hdr.Len), any: groupptr(hdr.Data)}
+}
diff --git a/slog/value_120.go b/slog/value_120.go
new file mode 100644
index 0000000..f7d4c09
--- /dev/null
+++ b/slog/value_120.go
@@ -0,0 +1,39 @@
+// 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 go1.20
+
+package slog
+
+import "unsafe"
+
+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
+)
+
+// StringValue returns a new Value for a string.
+func StringValue(value string) Value {
+ return Value{num: uint64(len(value)), any: stringptr(unsafe.StringData(value))}
+}
+
+// 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 Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))}
+}
+
+// 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)
+ }
+ return string(v.append(nil))
+}
+
+func (v Value) str() string {
+ return unsafe.String(v.any.(stringptr), v.num)
+}