github.com/AndrienkoAleksandr/go@v0.0.19/src/log/slog/value.go (about)

     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  
     5  package slog
     6  
     7  import (
     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.
    21  type 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  
    38  type (
    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.
    44  type 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  
    49  const (
    50  	KindAny Kind = iota
    51  	KindBool
    52  	KindDuration
    53  	KindFloat64
    54  	KindInt64
    55  	KindString
    56  	KindTime
    57  	KindUint64
    58  	KindGroup
    59  	KindLogValuer
    60  )
    61  
    62  var kindStrings = []string{
    63  	"Any",
    64  	"Bool",
    65  	"Duration",
    66  	"Float64",
    67  	"Int64",
    68  	"String",
    69  	"Time",
    70  	"Uint64",
    71  	"Group",
    72  	"LogValuer",
    73  }
    74  
    75  func (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.)
    84  type kind Kind
    85  
    86  // Kind returns v's Kind.
    87  func (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:
    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.
   109  func 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.
   114  func IntValue(v int) Value {
   115  	return Int64Value(int64(v))
   116  }
   117  
   118  // Int64Value returns a Value for an int64.
   119  func Int64Value(v int64) Value {
   120  	return Value{num: uint64(v), any: KindInt64}
   121  }
   122  
   123  // Uint64Value returns a Value for a uint64.
   124  func Uint64Value(v uint64) Value {
   125  	return Value{num: v, any: KindUint64}
   126  }
   127  
   128  // Float64Value returns a Value for a floating-point number.
   129  func Float64Value(v float64) Value {
   130  	return Value{num: math.Float64bits(v), any: KindFloat64}
   131  }
   132  
   133  // BoolValue returns a Value for a bool.
   134  func BoolValue(v bool) Value {
   135  	u := uint64(0)
   136  	if v {
   137  		u = 1
   138  	}
   139  	return Value{num: u, any: KindBool}
   140  }
   141  
   142  // Unexported version of *time.Location, just so we can store *time.Locations in
   143  // Values. (No user-provided value has this type.)
   144  type timeLocation *time.Location
   145  
   146  // TimeValue returns a Value for a time.Time.
   147  // It discards the monotonic portion.
   148  func TimeValue(v time.Time) Value {
   149  	if v.IsZero() {
   150  		// UnixNano on the zero time is undefined, so represent the zero time
   151  		// with a nil *time.Location instead. time.Time.Location method never
   152  		// returns nil, so a Value with any == timeLocation(nil) cannot be
   153  		// mistaken for any other Value, time.Time or otherwise.
   154  		return Value{any: timeLocation(nil)}
   155  	}
   156  	return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())}
   157  }
   158  
   159  // DurationValue returns a Value for a time.Duration.
   160  func DurationValue(v time.Duration) Value {
   161  	return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
   162  }
   163  
   164  // GroupValue returns a new Value for a list of Attrs.
   165  // The caller must not subsequently mutate the argument slice.
   166  func GroupValue(as ...Attr) Value {
   167  	return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))}
   168  }
   169  
   170  // AnyValue returns a Value for the supplied value.
   171  //
   172  // If the supplied value is of type Value, it is returned
   173  // unmodified.
   174  //
   175  // Given a value of one of Go's predeclared string, bool, or
   176  // (non-complex) numeric types, AnyValue returns a Value of kind
   177  // String, Bool, Uint64, Int64, or Float64. The width of the
   178  // original numeric type is not preserved.
   179  //
   180  // Given a time.Time or time.Duration value, AnyValue returns a Value of kind
   181  // KindTime or KindDuration. The monotonic time is not preserved.
   182  //
   183  // For nil, or values of all other types, including named types whose
   184  // underlying type is numeric, AnyValue returns a value of kind KindAny.
   185  func AnyValue(v any) Value {
   186  	switch v := v.(type) {
   187  	case string:
   188  		return StringValue(v)
   189  	case int:
   190  		return Int64Value(int64(v))
   191  	case uint:
   192  		return Uint64Value(uint64(v))
   193  	case int64:
   194  		return Int64Value(v)
   195  	case uint64:
   196  		return Uint64Value(v)
   197  	case bool:
   198  		return BoolValue(v)
   199  	case time.Duration:
   200  		return DurationValue(v)
   201  	case time.Time:
   202  		return TimeValue(v)
   203  	case uint8:
   204  		return Uint64Value(uint64(v))
   205  	case uint16:
   206  		return Uint64Value(uint64(v))
   207  	case uint32:
   208  		return Uint64Value(uint64(v))
   209  	case uintptr:
   210  		return Uint64Value(uint64(v))
   211  	case int8:
   212  		return Int64Value(int64(v))
   213  	case int16:
   214  		return Int64Value(int64(v))
   215  	case int32:
   216  		return Int64Value(int64(v))
   217  	case float64:
   218  		return Float64Value(v)
   219  	case float32:
   220  		return Float64Value(float64(v))
   221  	case []Attr:
   222  		return GroupValue(v...)
   223  	case Kind:
   224  		return Value{any: kind(v)}
   225  	case Value:
   226  		return v
   227  	default:
   228  		return Value{any: v}
   229  	}
   230  }
   231  
   232  //////////////// Accessors
   233  
   234  // Any returns v's value as an any.
   235  func (v Value) Any() any {
   236  	switch v.Kind() {
   237  	case KindAny:
   238  		if k, ok := v.any.(kind); ok {
   239  			return Kind(k)
   240  		}
   241  		return v.any
   242  	case KindLogValuer:
   243  		return v.any
   244  	case KindGroup:
   245  		return v.group()
   246  	case KindInt64:
   247  		return int64(v.num)
   248  	case KindUint64:
   249  		return v.num
   250  	case KindFloat64:
   251  		return v.float()
   252  	case KindString:
   253  		return v.str()
   254  	case KindBool:
   255  		return v.bool()
   256  	case KindDuration:
   257  		return v.duration()
   258  	case KindTime:
   259  		return v.time()
   260  	default:
   261  		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
   262  	}
   263  }
   264  
   265  // String returns Value's value as a string, formatted like fmt.Sprint. Unlike
   266  // the methods Int64, Float64, and so on, which panic if v is of the
   267  // wrong kind, String never panics.
   268  func (v Value) String() string {
   269  	if sp, ok := v.any.(stringptr); ok {
   270  		return unsafe.String(sp, v.num)
   271  	}
   272  	var buf []byte
   273  	return string(v.append(buf))
   274  }
   275  
   276  func (v Value) str() string {
   277  	return unsafe.String(v.any.(stringptr), v.num)
   278  }
   279  
   280  // Int64 returns v's value as an int64. It panics
   281  // if v is not a signed integer.
   282  func (v Value) Int64() int64 {
   283  	if g, w := v.Kind(), KindInt64; g != w {
   284  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   285  	}
   286  	return int64(v.num)
   287  }
   288  
   289  // Uint64 returns v's value as a uint64. It panics
   290  // if v is not an unsigned integer.
   291  func (v Value) Uint64() uint64 {
   292  	if g, w := v.Kind(), KindUint64; g != w {
   293  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   294  	}
   295  	return v.num
   296  }
   297  
   298  // Bool returns v's value as a bool. It panics
   299  // if v is not a bool.
   300  func (v Value) Bool() bool {
   301  	if g, w := v.Kind(), KindBool; g != w {
   302  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   303  	}
   304  	return v.bool()
   305  }
   306  
   307  func (a Value) bool() bool {
   308  	return a.num == 1
   309  }
   310  
   311  // Duration returns v's value as a time.Duration. It panics
   312  // if v is not a time.Duration.
   313  func (a Value) Duration() time.Duration {
   314  	if g, w := a.Kind(), KindDuration; g != w {
   315  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   316  	}
   317  
   318  	return a.duration()
   319  }
   320  
   321  func (a Value) duration() time.Duration {
   322  	return time.Duration(int64(a.num))
   323  }
   324  
   325  // Float64 returns v's value as a float64. It panics
   326  // if v is not a float64.
   327  func (v Value) Float64() float64 {
   328  	if g, w := v.Kind(), KindFloat64; g != w {
   329  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   330  	}
   331  
   332  	return v.float()
   333  }
   334  
   335  func (a Value) float() float64 {
   336  	return math.Float64frombits(a.num)
   337  }
   338  
   339  // Time returns v's value as a time.Time. It panics
   340  // if v is not a time.Time.
   341  func (v Value) Time() time.Time {
   342  	if g, w := v.Kind(), KindTime; g != w {
   343  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   344  	}
   345  	return v.time()
   346  }
   347  
   348  func (v Value) time() time.Time {
   349  	loc := v.any.(timeLocation)
   350  	if loc == nil {
   351  		return time.Time{}
   352  	}
   353  	return time.Unix(0, int64(v.num)).In(loc)
   354  }
   355  
   356  // LogValuer returns v's value as a LogValuer. It panics
   357  // if v is not a LogValuer.
   358  func (v Value) LogValuer() LogValuer {
   359  	return v.any.(LogValuer)
   360  }
   361  
   362  // Group returns v's value as a []Attr.
   363  // It panics if v's Kind is not KindGroup.
   364  func (v Value) Group() []Attr {
   365  	if sp, ok := v.any.(groupptr); ok {
   366  		return unsafe.Slice((*Attr)(sp), v.num)
   367  	}
   368  	panic("Group: bad kind")
   369  }
   370  
   371  func (v Value) group() []Attr {
   372  	return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num)
   373  }
   374  
   375  //////////////// Other
   376  
   377  // Equal reports whether v and w represent the same Go value.
   378  func (v Value) Equal(w Value) bool {
   379  	k1 := v.Kind()
   380  	k2 := w.Kind()
   381  	if k1 != k2 {
   382  		return false
   383  	}
   384  	switch k1 {
   385  	case KindInt64, KindUint64, KindBool, KindDuration:
   386  		return v.num == w.num
   387  	case KindString:
   388  		return v.str() == w.str()
   389  	case KindFloat64:
   390  		return v.float() == w.float()
   391  	case KindTime:
   392  		return v.time().Equal(w.time())
   393  	case KindAny, KindLogValuer:
   394  		return v.any == w.any // may panic if non-comparable
   395  	case KindGroup:
   396  		return slices.EqualFunc(v.group(), w.group(), Attr.Equal)
   397  	default:
   398  		panic(fmt.Sprintf("bad kind: %s", k1))
   399  	}
   400  }
   401  
   402  // append appends a text representation of v to dst.
   403  // v is formatted as with fmt.Sprint.
   404  func (v Value) append(dst []byte) []byte {
   405  	switch v.Kind() {
   406  	case KindString:
   407  		return append(dst, v.str()...)
   408  	case KindInt64:
   409  		return strconv.AppendInt(dst, int64(v.num), 10)
   410  	case KindUint64:
   411  		return strconv.AppendUint(dst, v.num, 10)
   412  	case KindFloat64:
   413  		return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
   414  	case KindBool:
   415  		return strconv.AppendBool(dst, v.bool())
   416  	case KindDuration:
   417  		return append(dst, v.duration().String()...)
   418  	case KindTime:
   419  		return append(dst, v.time().String()...)
   420  	case KindGroup:
   421  		return fmt.Append(dst, v.group())
   422  	case KindAny, KindLogValuer:
   423  		return fmt.Append(dst, v.any)
   424  	default:
   425  		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
   426  	}
   427  }
   428  
   429  // A LogValuer is any Go value that can convert itself into a Value for logging.
   430  //
   431  // This mechanism may be used to defer expensive operations until they are
   432  // needed, or to expand a single value into a sequence of components.
   433  type LogValuer interface {
   434  	LogValue() Value
   435  }
   436  
   437  const maxLogValues = 100
   438  
   439  // Resolve repeatedly calls LogValue on v while it implements LogValuer,
   440  // and returns the result.
   441  // If v resolves to a group, the group's attributes' values are not recursively
   442  // resolved.
   443  // If the number of LogValue calls exceeds a threshold, a Value containing an
   444  // error is returned.
   445  // Resolve's return value is guaranteed not to be of Kind KindLogValuer.
   446  func (v Value) Resolve() (rv Value) {
   447  	orig := v
   448  	defer func() {
   449  		if r := recover(); r != nil {
   450  			rv = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5)))
   451  		}
   452  	}()
   453  
   454  	for i := 0; i < maxLogValues; i++ {
   455  		if v.Kind() != KindLogValuer {
   456  			return v
   457  		}
   458  		v = v.LogValuer().LogValue()
   459  	}
   460  	err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any())
   461  	return AnyValue(err)
   462  }
   463  
   464  func stack(skip, nFrames int) string {
   465  	pcs := make([]uintptr, nFrames+1)
   466  	n := runtime.Callers(skip+1, pcs)
   467  	if n == 0 {
   468  		return "(no stack)"
   469  	}
   470  	frames := runtime.CallersFrames(pcs[:n])
   471  	var b strings.Builder
   472  	i := 0
   473  	for {
   474  		frame, more := frames.Next()
   475  		fmt.Fprintf(&b, "called from %s (%s:%d)\n", frame.Function, frame.File, frame.Line)
   476  		if !more {
   477  			break
   478  		}
   479  		i++
   480  		if i >= nFrames {
   481  			fmt.Fprintf(&b, "(rest of stack elided)\n")
   482  			break
   483  		}
   484  	}
   485  	return b.String()
   486  }