blob: 6fab1e82dd6235276c9b2cf6e73ef0ef2bbf509a [file] [log] [blame]
// Copyright 2019 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.
package event
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"unsafe"
)
// Value holds any value in an efficient way that avoids allocations for
// most types.
type Value struct {
packed uint64
untyped interface{}
}
// Label is a named value.
type Label struct {
Name string
Value Value
}
// stringptr is used in untyped when the Value is a string
type stringptr unsafe.Pointer
// int64Kind is used in untyped when the Value is a signed integer
type int64Kind struct{}
// uint64Kind is used in untyped when the Value is an unsigned integer
type uint64Kind struct{}
// float64Kind is used in untyped when the Value is a floating point number
type float64Kind struct{}
// boolKind is used in untyped when the Value is a boolean
type boolKind struct{}
// Format prints the label in a standard form.
func (l *Label) Format(f fmt.State, verb rune) {
buf := bufPool.Get().(*buffer)
l.format(f.(writer), buf.data[:0])
bufPool.Put(buf)
}
func (l *Label) format(w writer, buf []byte) {
w.Write(strconv.AppendQuote(buf[:0], l.Name))
w.WriteString(":")
l.Value.format(w, buf)
}
// Format prints the value in a standard form.
func (v *Value) Format(f fmt.State, verb rune) {
buf := bufPool.Get().(*buffer)
v.format(f.(writer), buf.data[:0])
bufPool.Put(buf)
}
func (v *Value) format(w writer, buf []byte) {
switch {
case v.IsString():
w.Write(strconv.AppendQuote(buf[:0], v.String()))
case v.IsInt64():
w.Write(strconv.AppendInt(buf[:0], v.Int64(), 10))
case v.IsUint64():
w.Write(strconv.AppendUint(buf[:0], v.Uint64(), 10))
case v.IsFloat64():
w.Write(strconv.AppendFloat(buf[:0], v.Float64(), 'g', -1, 64))
case v.IsBool():
if v.Bool() {
w.WriteString("true")
} else {
w.WriteString("false")
}
default:
fmt.Fprint(w, v.Interface())
}
}
// HasValue returns true if the value is set to any type.
func (v *Value) HasValue() bool { return v.untyped != nil }
// ValueOf returns a Value for the supplied value.
func ValueOf(value interface{}) Value {
return Value{untyped: value}
}
// Interface returns the value.
// This will never panic, things that were not set using SetInterface will be
// unpacked and returned anyway.
func (v Value) Interface() interface{} {
switch {
case v.IsString():
return v.String()
case v.IsInt64():
return v.Int64()
case v.IsUint64():
return v.Uint64()
case v.IsFloat64():
return v.Float64()
case v.IsBool():
return v.Bool()
default:
return v.untyped
}
}
// StringOf returns a new Value for a string.
func StringOf(s string) Value {
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
return Value{packed: uint64(hdr.Len), untyped: stringptr(hdr.Data)}
}
// String returns the value as a string.
// This does not panic if v's Kind is not String, instead, it returns a string
// representation of the value in all cases.
func (v Value) String() string {
if sp, ok := v.untyped.(stringptr); ok {
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(sp)
hdr.Len = int(v.packed)
return s
}
// not a string, so invoke the formatter to build one
w := &strings.Builder{}
buf := bufPool.Get().(*buffer)
v.format(w, buf.data[:0])
bufPool.Put(buf)
return w.String()
}
// IsString returns true if the value was built with SetString.
func (v Value) IsString() bool {
_, ok := v.untyped.(stringptr)
return ok
}
// Int64Of returns a new Value for a signed integer.
func Int64Of(u int64) Value {
return Value{packed: uint64(u), untyped: int64Kind{}}
}
// Int64 returns the int64 from a value that was set with SetInt64.
// It will panic for any value for which IsInt64 is not true.
func (v Value) Int64() int64 {
if !v.IsInt64() {
panic("Int64 called on non int64 value")
}
return int64(v.packed)
}
// IsInt64 returns true if the value was built with SetInt64.
func (v Value) IsInt64() bool {
_, ok := v.untyped.(int64Kind)
return ok
}
// Uint64Of returns a new Value for an unsigned integer.
func Uint64Of(u uint64) Value {
return Value{packed: u, untyped: uint64Kind{}}
}
// Uint64 returns the uint64 from a value that was set with SetUint64.
// It will panic for any value for which IsUint64 is not true.
func (v Value) Uint64() uint64 {
if !v.IsUint64() {
panic("Uint64 called on non uint64 value")
}
return v.packed
}
// IsUint64 returns true if the value was built with SetUint64.
func (v Value) IsUint64() bool {
_, ok := v.untyped.(uint64Kind)
return ok
}
// Float64Of returns a new Value for a floating point number.
func Float64Of(f float64) Value {
return Value{packed: math.Float64bits(f), untyped: float64Kind{}}
}
// Float64 returns the float64 from a value that was set with SetFloat64.
// It will panic for any value for which IsFloat64 is not true.
func (v Value) Float64() float64 {
if !v.IsFloat64() {
panic("Float64 called on non float64 value")
}
return math.Float64frombits(v.packed)
}
// IsFloat64 returns true if the value was built with SetFloat64.
func (v Value) IsFloat64() bool {
_, ok := v.untyped.(float64Kind)
return ok
}
// BoolOf returns a new Value for a bool.
func BoolOf(b bool) Value {
if b {
return Value{packed: 1, untyped: boolKind{}}
}
return Value{packed: 0, untyped: boolKind{}}
}
// Bool returns the bool from a value that was set with SetBool.
// It will panic for any value for which IsBool is not true.
func (v Value) Bool() bool {
if !v.IsBool() {
panic("Bool called on non bool value")
}
if v.packed != 0 {
return true
}
return false
}
// IsBool returns true if the value was built with SetBool.
func (v Value) IsBool() bool {
_, ok := v.untyped.(boolKind)
return ok
}