|  | // 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" | 
|  | "time" | 
|  | "unsafe" | 
|  | ) | 
|  |  | 
|  | // Label is a named value. | 
|  | type Label struct { | 
|  | Name string | 
|  |  | 
|  | packed  uint64 | 
|  | untyped interface{} | 
|  | } | 
|  |  | 
|  | // stringptr is used in untyped when the Value is a string | 
|  | type stringptr unsafe.Pointer | 
|  |  | 
|  | // bytesptr is used in untyped when the Value is a byte slice | 
|  | type bytesptr 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{} | 
|  |  | 
|  | // durationKind is used in untyped when the Value is a time.Duration | 
|  | type durationKind struct{} | 
|  |  | 
|  | // HasValue returns true if the value is set to any type. | 
|  | func (l Label) HasValue() bool { return l.untyped != nil } | 
|  |  | 
|  | // Equal reports whether two labels are equal. | 
|  | func (l Label) Equal(l2 Label) bool { | 
|  | if l.Name != l2.Name { | 
|  | return false | 
|  | } | 
|  | if !l.HasValue() { | 
|  | return !l2.HasValue() | 
|  | } | 
|  | if !l2.HasValue() { | 
|  | return false | 
|  | } | 
|  | switch { | 
|  | case l.IsString(): | 
|  | return l2.IsString() && l.String() == l2.String() | 
|  | case l.IsInt64(): | 
|  | return l2.IsInt64() && l.packed == l2.packed | 
|  | case l.IsUint64(): | 
|  | return l2.IsUint64() && l.packed == l2.packed | 
|  | case l.IsFloat64(): | 
|  | return l2.IsFloat64() && l.Float64() == l2.Float64() | 
|  | case l.IsBool(): | 
|  | return l2.IsBool() && l.packed == l2.packed | 
|  | case l.IsDuration(): | 
|  | return l2.IsDuration() && l.packed == l2.packed | 
|  | default: | 
|  | return l.untyped == l2.untyped | 
|  | } | 
|  | } | 
|  |  | 
|  | // Value returns a Label for the supplied value. | 
|  | func Value(name string, value interface{}) Label { | 
|  | return Label{Name: name, 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 Label) 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() | 
|  | case v.IsDuration(): | 
|  | return v.Duration() | 
|  | default: | 
|  | return v.untyped | 
|  | } | 
|  | } | 
|  |  | 
|  | // String returns a new Value for a string. | 
|  | func String(name string, s string) Label { | 
|  | hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) | 
|  | return Label{Name: name, 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 Label) 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, convert to one | 
|  | switch { | 
|  | case v.IsInt64(): | 
|  | return strconv.FormatInt(v.Int64(), 10) | 
|  | case v.IsUint64(): | 
|  | return strconv.FormatUint(v.Uint64(), 10) | 
|  | case v.IsFloat64(): | 
|  | return strconv.FormatFloat(v.Float64(), 'g', -1, 64) | 
|  | case v.IsBool(): | 
|  | if v.Bool() { | 
|  | return "true" | 
|  | } else { | 
|  | return "false" | 
|  | } | 
|  | default: | 
|  | return fmt.Sprint(v.Interface()) | 
|  | } | 
|  | } | 
|  |  | 
|  | // IsString returns true if the value was built with StringOf. | 
|  | func (v Label) IsString() bool { | 
|  | _, ok := v.untyped.(stringptr) | 
|  | return ok | 
|  | } | 
|  |  | 
|  | // Bytes returns a new Value for a string. | 
|  | func Bytes(name string, data []byte) Label { | 
|  | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data)) | 
|  | return Label{Name: name, packed: uint64(hdr.Len), untyped: bytesptr(hdr.Data)} | 
|  | } | 
|  |  | 
|  | // Bytes returns the value as a bytes array. | 
|  | func (v Label) Bytes() []byte { | 
|  | bp, ok := v.untyped.(bytesptr) | 
|  | if !ok { | 
|  | panic("Bytes called on non []byte value") | 
|  | } | 
|  | var buf []byte | 
|  | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) | 
|  | hdr.Data = uintptr(bp) | 
|  | hdr.Len = int(v.packed) | 
|  | hdr.Cap = hdr.Len | 
|  | return buf | 
|  | } | 
|  |  | 
|  | // IsBytes returns true if the value was built with BytesOf. | 
|  | func (v Label) IsBytes() bool { | 
|  | _, ok := v.untyped.(bytesptr) | 
|  | return ok | 
|  | } | 
|  |  | 
|  | // Int64 returns a new Value for a signed integer. | 
|  | func Int64(name string, u int64) Label { | 
|  | return Label{Name: name, 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 Label) 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 Label) IsInt64() bool { | 
|  | _, ok := v.untyped.(int64Kind) | 
|  | return ok | 
|  | } | 
|  |  | 
|  | // Uint64 returns a new Value for an unsigned integer. | 
|  | func Uint64(name string, u uint64) Label { | 
|  | return Label{Name: name, 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 Label) 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 Label) IsUint64() bool { | 
|  | _, ok := v.untyped.(uint64Kind) | 
|  | return ok | 
|  | } | 
|  |  | 
|  | // Float64 returns a new Value for a floating point number. | 
|  | func Float64(name string, f float64) Label { | 
|  | return Label{Name: name, 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 Label) 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 Label) IsFloat64() bool { | 
|  | _, ok := v.untyped.(float64Kind) | 
|  | return ok | 
|  | } | 
|  |  | 
|  | // Bool returns a new Value for a bool. | 
|  | func Bool(name string, b bool) Label { | 
|  | if b { | 
|  | return Label{Name: name, packed: 1, untyped: boolKind{}} | 
|  | } | 
|  | return Label{Name: name, 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 Label) 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 Label) IsBool() bool { | 
|  | _, ok := v.untyped.(boolKind) | 
|  | return ok | 
|  | } | 
|  |  | 
|  | func Duration(name string, d time.Duration) Label { | 
|  | return Label{Name: name, packed: uint64(d), untyped: durationKind{}} | 
|  | } | 
|  |  | 
|  | func (v Label) Duration() time.Duration { | 
|  | if !v.IsDuration() { | 
|  | panic("Duration called on non-Duration value") | 
|  | } | 
|  | return time.Duration(v.packed) | 
|  | } | 
|  |  | 
|  | func (v Label) IsDuration() bool { | 
|  | _, ok := v.untyped.(durationKind) | 
|  | return ok | 
|  | } |