1// Copyright 2022 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package slog
6
7import (
8	"fmt"
9	"math"
10	"runtime"
11	"slices"
12	"strconv"
13	"strings"
14	"time"
15	"unsafe"
16)
17
18// A Value can represent any Go value, but unlike type any,
19// it can represent most small values without an allocation.
20// The zero Value corresponds to nil.
21type Value struct {
22	_ [0]func() // disallow ==
23	// num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
24	// the string length for KindString, and nanoseconds since the epoch for KindTime.
25	num uint64
26	// If any is of type Kind, then the value is in num as described above.
27	// If any is of type *time.Location, then the Kind is Time and time.Time value
28	// can be constructed from the Unix nanos in num and the location (monotonic time
29	// is not preserved).
30	// If any is of type stringptr, then the Kind is String and the string value
31	// consists of the length in num and the pointer in any.
32	// Otherwise, the Kind is Any and any is the value.
33	// (This implies that Attrs cannot store values of type Kind, *time.Location
34	// or stringptr.)
35	any any
36}
37
38type (
39	stringptr *byte // used in Value.any when the Value is a string
40	groupptr  *Attr // used in Value.any when the Value is a []Attr
41)
42
43// Kind is the kind of a [Value].
44type Kind int
45
46// The following list is sorted alphabetically, but it's also important that
47// KindAny is 0 so that a zero Value represents nil.
48
49const (
50	KindAny Kind = iota
51	KindBool
52	KindDuration
53	KindFloat64
54	KindInt64
55	KindString
56	KindTime
57	KindUint64
58	KindGroup
59	KindLogValuer
60)
61
62var kindStrings = []string{
63	"Any",
64	"Bool",
65	"Duration",
66	"Float64",
67	"Int64",
68	"String",
69	"Time",
70	"Uint64",
71	"Group",
72	"LogValuer",
73}
74
75func (k Kind) String() string {
76	if k >= 0 && int(k) < len(kindStrings) {
77		return kindStrings[k]
78	}
79	return "<unknown slog.Kind>"
80}
81
82// Unexported version of Kind, just so we can store Kinds in Values.
83// (No user-provided value has this type.)
84type kind Kind
85
86// Kind returns v's Kind.
87func (v Value) Kind() Kind {
88	switch x := v.any.(type) {
89	case Kind:
90		return x
91	case stringptr:
92		return KindString
93	case timeLocation, timeTime:
94		return KindTime
95	case groupptr:
96		return KindGroup
97	case LogValuer:
98		return KindLogValuer
99	case kind: // a kind is just a wrapper for a Kind
100		return KindAny
101	default:
102		return KindAny
103	}
104}
105
106//////////////// Constructors
107
108// StringValue returns a new [Value] for a string.
109func StringValue(value string) Value {
110	return Value{num: uint64(len(value)), any: stringptr(unsafe.StringData(value))}
111}
112
113// IntValue returns a [Value] for an int.
114func IntValue(v int) Value {
115	return Int64Value(int64(v))
116}
117
118// Int64Value returns a [Value] for an int64.
119func Int64Value(v int64) Value {
120	return Value{num: uint64(v), any: KindInt64}
121}
122
123// Uint64Value returns a [Value] for a uint64.
124func Uint64Value(v uint64) Value {
125	return Value{num: v, any: KindUint64}
126}
127
128// Float64Value returns a [Value] for a floating-point number.
129func Float64Value(v float64) Value {
130	return Value{num: math.Float64bits(v), any: KindFloat64}
131}
132
133// BoolValue returns a [Value] for a bool.
134func BoolValue(v bool) Value {
135	u := uint64(0)
136	if v {
137		u = 1
138	}
139	return Value{num: u, any: KindBool}
140}
141
142type (
143	// Unexported version of *time.Location, just so we can store *time.Locations in
144	// Values. (No user-provided value has this type.)
145	timeLocation *time.Location
146
147	// timeTime is for times where UnixNano is undefined.
148	timeTime time.Time
149)
150
151// TimeValue returns a [Value] for a [time.Time].
152// It discards the monotonic portion.
153func TimeValue(v time.Time) Value {
154	if v.IsZero() {
155		// UnixNano on the zero time is undefined, so represent the zero time
156		// with a nil *time.Location instead. time.Time.Location method never
157		// returns nil, so a Value with any == timeLocation(nil) cannot be
158		// mistaken for any other Value, time.Time or otherwise.
159		return Value{any: timeLocation(nil)}
160	}
161	nsec := v.UnixNano()
162	t := time.Unix(0, nsec)
163	if v.Equal(t) {
164		// UnixNano correctly represents the time, so use a zero-alloc representation.
165		return Value{num: uint64(nsec), any: timeLocation(v.Location())}
166	}
167	// Fall back to the general form.
168	// Strip the monotonic portion to match the other representation.
169	return Value{any: timeTime(v.Round(0))}
170}
171
172// DurationValue returns a [Value] for a [time.Duration].
173func DurationValue(v time.Duration) Value {
174	return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
175}
176
177// GroupValue returns a new [Value] for a list of Attrs.
178// The caller must not subsequently mutate the argument slice.
179func GroupValue(as ...Attr) Value {
180	// Remove empty groups.
181	// It is simpler overall to do this at construction than
182	// to check each Group recursively for emptiness.
183	if n := countEmptyGroups(as); n > 0 {
184		as2 := make([]Attr, 0, len(as)-n)
185		for _, a := range as {
186			if !a.Value.isEmptyGroup() {
187				as2 = append(as2, a)
188			}
189		}
190		as = as2
191	}
192	return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))}
193}
194
195// countEmptyGroups returns the number of empty group values in its argument.
196func countEmptyGroups(as []Attr) int {
197	n := 0
198	for _, a := range as {
199		if a.Value.isEmptyGroup() {
200			n++
201		}
202	}
203	return n
204}
205
206// AnyValue returns a [Value] for the supplied value.
207//
208// If the supplied value is of type Value, it is returned
209// unmodified.
210//
211// Given a value of one of Go's predeclared string, bool, or
212// (non-complex) numeric types, AnyValue returns a Value of kind
213// [KindString], [KindBool], [KindUint64], [KindInt64], or [KindFloat64].
214// The width of the original numeric type is not preserved.
215//
216// Given a [time.Time] or [time.Duration] value, AnyValue returns a Value of kind
217// [KindTime] or [KindDuration]. The monotonic time is not preserved.
218//
219// For nil, or values of all other types, including named types whose
220// underlying type is numeric, AnyValue returns a value of kind [KindAny].
221func AnyValue(v any) Value {
222	switch v := v.(type) {
223	case string:
224		return StringValue(v)
225	case int:
226		return Int64Value(int64(v))
227	case uint:
228		return Uint64Value(uint64(v))
229	case int64:
230		return Int64Value(v)
231	case uint64:
232		return Uint64Value(v)
233	case bool:
234		return BoolValue(v)
235	case time.Duration:
236		return DurationValue(v)
237	case time.Time:
238		return TimeValue(v)
239	case uint8:
240		return Uint64Value(uint64(v))
241	case uint16:
242		return Uint64Value(uint64(v))
243	case uint32:
244		return Uint64Value(uint64(v))
245	case uintptr:
246		return Uint64Value(uint64(v))
247	case int8:
248		return Int64Value(int64(v))
249	case int16:
250		return Int64Value(int64(v))
251	case int32:
252		return Int64Value(int64(v))
253	case float64:
254		return Float64Value(v)
255	case float32:
256		return Float64Value(float64(v))
257	case []Attr:
258		return GroupValue(v...)
259	case Kind:
260		return Value{any: kind(v)}
261	case Value:
262		return v
263	default:
264		return Value{any: v}
265	}
266}
267
268//////////////// Accessors
269
270// Any returns v's value as an any.
271func (v Value) Any() any {
272	switch v.Kind() {
273	case KindAny:
274		if k, ok := v.any.(kind); ok {
275			return Kind(k)
276		}
277		return v.any
278	case KindLogValuer:
279		return v.any
280	case KindGroup:
281		return v.group()
282	case KindInt64:
283		return int64(v.num)
284	case KindUint64:
285		return v.num
286	case KindFloat64:
287		return v.float()
288	case KindString:
289		return v.str()
290	case KindBool:
291		return v.bool()
292	case KindDuration:
293		return v.duration()
294	case KindTime:
295		return v.time()
296	default:
297		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
298	}
299}
300
301// String returns Value's value as a string, formatted like [fmt.Sprint]. Unlike
302// the methods Int64, Float64, and so on, which panic if v is of the
303// wrong kind, String never panics.
304func (v Value) String() string {
305	if sp, ok := v.any.(stringptr); ok {
306		return unsafe.String(sp, v.num)
307	}
308	var buf []byte
309	return string(v.append(buf))
310}
311
312func (v Value) str() string {
313	return unsafe.String(v.any.(stringptr), v.num)
314}
315
316// Int64 returns v's value as an int64. It panics
317// if v is not a signed integer.
318func (v Value) Int64() int64 {
319	if g, w := v.Kind(), KindInt64; g != w {
320		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
321	}
322	return int64(v.num)
323}
324
325// Uint64 returns v's value as a uint64. It panics
326// if v is not an unsigned integer.
327func (v Value) Uint64() uint64 {
328	if g, w := v.Kind(), KindUint64; g != w {
329		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
330	}
331	return v.num
332}
333
334// Bool returns v's value as a bool. It panics
335// if v is not a bool.
336func (v Value) Bool() bool {
337	if g, w := v.Kind(), KindBool; g != w {
338		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
339	}
340	return v.bool()
341}
342
343func (v Value) bool() bool {
344	return v.num == 1
345}
346
347// Duration returns v's value as a [time.Duration]. It panics
348// if v is not a time.Duration.
349func (v Value) Duration() time.Duration {
350	if g, w := v.Kind(), KindDuration; g != w {
351		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
352	}
353
354	return v.duration()
355}
356
357func (v Value) duration() time.Duration {
358	return time.Duration(int64(v.num))
359}
360
361// Float64 returns v's value as a float64. It panics
362// if v is not a float64.
363func (v Value) Float64() float64 {
364	if g, w := v.Kind(), KindFloat64; g != w {
365		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
366	}
367
368	return v.float()
369}
370
371func (v Value) float() float64 {
372	return math.Float64frombits(v.num)
373}
374
375// Time returns v's value as a [time.Time]. It panics
376// if v is not a time.Time.
377func (v Value) Time() time.Time {
378	if g, w := v.Kind(), KindTime; g != w {
379		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
380	}
381	return v.time()
382}
383
384// See TimeValue to understand how times are represented.
385func (v Value) time() time.Time {
386	switch a := v.any.(type) {
387	case timeLocation:
388		if a == nil {
389			return time.Time{}
390		}
391		return time.Unix(0, int64(v.num)).In(a)
392	case timeTime:
393		return time.Time(a)
394	default:
395		panic(fmt.Sprintf("bad time type %T", v.any))
396	}
397}
398
399// LogValuer returns v's value as a LogValuer. It panics
400// if v is not a LogValuer.
401func (v Value) LogValuer() LogValuer {
402	return v.any.(LogValuer)
403}
404
405// Group returns v's value as a []Attr.
406// It panics if v's [Kind] is not [KindGroup].
407func (v Value) Group() []Attr {
408	if sp, ok := v.any.(groupptr); ok {
409		return unsafe.Slice((*Attr)(sp), v.num)
410	}
411	panic("Group: bad kind")
412}
413
414func (v Value) group() []Attr {
415	return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num)
416}
417
418//////////////// Other
419
420// Equal reports whether v and w represent the same Go value.
421func (v Value) Equal(w Value) bool {
422	k1 := v.Kind()
423	k2 := w.Kind()
424	if k1 != k2 {
425		return false
426	}
427	switch k1 {
428	case KindInt64, KindUint64, KindBool, KindDuration:
429		return v.num == w.num
430	case KindString:
431		return v.str() == w.str()
432	case KindFloat64:
433		return v.float() == w.float()
434	case KindTime:
435		return v.time().Equal(w.time())
436	case KindAny, KindLogValuer:
437		return v.any == w.any // may panic if non-comparable
438	case KindGroup:
439		return slices.EqualFunc(v.group(), w.group(), Attr.Equal)
440	default:
441		panic(fmt.Sprintf("bad kind: %s", k1))
442	}
443}
444
445// isEmptyGroup reports whether v is a group that has no attributes.
446func (v Value) isEmptyGroup() bool {
447	if v.Kind() != KindGroup {
448		return false
449	}
450	// We do not need to recursively examine the group's Attrs for emptiness,
451	// because GroupValue removed them when the group was constructed, and
452	// groups are immutable.
453	return len(v.group()) == 0
454}
455
456// append appends a text representation of v to dst.
457// v is formatted as with fmt.Sprint.
458func (v Value) append(dst []byte) []byte {
459	switch v.Kind() {
460	case KindString:
461		return append(dst, v.str()...)
462	case KindInt64:
463		return strconv.AppendInt(dst, int64(v.num), 10)
464	case KindUint64:
465		return strconv.AppendUint(dst, v.num, 10)
466	case KindFloat64:
467		return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
468	case KindBool:
469		return strconv.AppendBool(dst, v.bool())
470	case KindDuration:
471		return append(dst, v.duration().String()...)
472	case KindTime:
473		return append(dst, v.time().String()...)
474	case KindGroup:
475		return fmt.Append(dst, v.group())
476	case KindAny, KindLogValuer:
477		return fmt.Append(dst, v.any)
478	default:
479		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
480	}
481}
482
483// A LogValuer is any Go value that can convert itself into a Value for logging.
484//
485// This mechanism may be used to defer expensive operations until they are
486// needed, or to expand a single value into a sequence of components.
487type LogValuer interface {
488	LogValue() Value
489}
490
491const maxLogValues = 100
492
493// Resolve repeatedly calls LogValue on v while it implements [LogValuer],
494// and returns the result.
495// If v resolves to a group, the group's attributes' values are not recursively
496// resolved.
497// If the number of LogValue calls exceeds a threshold, a Value containing an
498// error is returned.
499// Resolve's return value is guaranteed not to be of Kind [KindLogValuer].
500func (v Value) Resolve() (rv Value) {
501	orig := v
502	defer func() {
503		if r := recover(); r != nil {
504			rv = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5)))
505		}
506	}()
507
508	for i := 0; i < maxLogValues; i++ {
509		if v.Kind() != KindLogValuer {
510			return v
511		}
512		v = v.LogValuer().LogValue()
513	}
514	err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any())
515	return AnyValue(err)
516}
517
518func stack(skip, nFrames int) string {
519	pcs := make([]uintptr, nFrames+1)
520	n := runtime.Callers(skip+1, pcs)
521	if n == 0 {
522		return "(no stack)"
523	}
524	frames := runtime.CallersFrames(pcs[:n])
525	var b strings.Builder
526	i := 0
527	for {
528		frame, more := frames.Next()
529		fmt.Fprintf(&b, "called from %s (%s:%d)\n", frame.Function, frame.File, frame.Line)
530		if !more {
531			break
532		}
533		i++
534		if i >= nFrames {
535			fmt.Fprintf(&b, "(rest of stack elided)\n")
536			break
537		}
538	}
539	return b.String()
540}
541