package log
import (
"bytes"
"cmp"
"errors"
"fmt"
"math"
"slices"
"strconv"
"unsafe"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/internal/global"
)
var errKind = errors .New ("invalid Kind" )
type Kind int
const (
KindEmpty Kind = iota
KindBool
KindFloat64
KindInt64
KindString
KindBytes
KindSlice
KindMap
)
type Value struct {
noCmp [0 ]func ()
num uint64
any any
}
type (
stringptr *byte
bytesptr *byte
sliceptr *Value
mapptr *KeyValue
)
func StringValue (v string ) Value {
return Value {
num : uint64 (len (v )),
any : stringptr (unsafe .StringData (v )),
}
}
func IntValue (v int ) Value { return Int64Value (int64 (v )) }
func Int64Value (v int64 ) Value {
return Value {num : uint64 (v ), any : KindInt64 }
}
func Float64Value (v float64 ) Value {
return Value {num : math .Float64bits (v ), any : KindFloat64 }
}
func BoolValue (v bool ) Value {
var n uint64
if v {
n = 1
}
return Value {num : n , any : KindBool }
}
func BytesValue (v []byte ) Value {
return Value {
num : uint64 (len (v )),
any : bytesptr (unsafe .SliceData (v )),
}
}
func SliceValue (vs ...Value ) Value {
return Value {
num : uint64 (len (vs )),
any : sliceptr (unsafe .SliceData (vs )),
}
}
func MapValue (kvs ...KeyValue ) Value {
return Value {
num : uint64 (len (kvs )),
any : mapptr (unsafe .SliceData (kvs )),
}
}
func (v Value ) AsString () string {
if sp , ok := v .any .(stringptr ); ok {
return unsafe .String (sp , v .num )
}
global .Error (errKind , "AsString" , "Kind" , v .Kind ())
return ""
}
func (v Value ) asString () string {
return unsafe .String (v .any .(stringptr ), v .num )
}
func (v Value ) AsInt64 () int64 {
if v .Kind () != KindInt64 {
global .Error (errKind , "AsInt64" , "Kind" , v .Kind ())
return 0
}
return v .asInt64 ()
}
func (v Value ) asInt64 () int64 {
return int64 (v .num )
}
func (v Value ) AsBool () bool {
if v .Kind () != KindBool {
global .Error (errKind , "AsBool" , "Kind" , v .Kind ())
return false
}
return v .asBool ()
}
func (v Value ) asBool () bool { return v .num == 1 }
func (v Value ) AsFloat64 () float64 {
if v .Kind () != KindFloat64 {
global .Error (errKind , "AsFloat64" , "Kind" , v .Kind ())
return 0
}
return v .asFloat64 ()
}
func (v Value ) asFloat64 () float64 { return math .Float64frombits (v .num ) }
func (v Value ) AsBytes () []byte {
if sp , ok := v .any .(bytesptr ); ok {
return unsafe .Slice ((*byte )(sp ), v .num )
}
global .Error (errKind , "AsBytes" , "Kind" , v .Kind ())
return nil
}
func (v Value ) asBytes () []byte {
return unsafe .Slice ((*byte )(v .any .(bytesptr )), v .num )
}
func (v Value ) AsSlice () []Value {
if sp , ok := v .any .(sliceptr ); ok {
return unsafe .Slice ((*Value )(sp ), v .num )
}
global .Error (errKind , "AsSlice" , "Kind" , v .Kind ())
return nil
}
func (v Value ) asSlice () []Value {
return unsafe .Slice ((*Value )(v .any .(sliceptr )), v .num )
}
func (v Value ) AsMap () []KeyValue {
if sp , ok := v .any .(mapptr ); ok {
return unsafe .Slice ((*KeyValue )(sp ), v .num )
}
global .Error (errKind , "AsMap" , "Kind" , v .Kind ())
return nil
}
func (v Value ) asMap () []KeyValue {
return unsafe .Slice ((*KeyValue )(v .any .(mapptr )), v .num )
}
func (v Value ) Kind () Kind {
switch x := v .any .(type ) {
case Kind :
return x
case stringptr :
return KindString
case bytesptr :
return KindBytes
case sliceptr :
return KindSlice
case mapptr :
return KindMap
default :
return KindEmpty
}
}
func (v Value ) Empty () bool { return v .Kind () == KindEmpty }
func (v Value ) Equal (w Value ) bool {
k1 := v .Kind ()
k2 := w .Kind ()
if k1 != k2 {
return false
}
switch k1 {
case KindInt64 , KindBool :
return v .num == w .num
case KindString :
return v .asString () == w .asString ()
case KindFloat64 :
return v .asFloat64 () == w .asFloat64 ()
case KindSlice :
return slices .EqualFunc (v .asSlice (), w .asSlice (), Value .Equal )
case KindMap :
sv := sortMap (v .asMap ())
sw := sortMap (w .asMap ())
return slices .EqualFunc (sv , sw , KeyValue .Equal )
case KindBytes :
return bytes .Equal (v .asBytes (), w .asBytes ())
case KindEmpty :
return true
default :
global .Error (errKind , "Equal" , "Kind" , k1 )
return false
}
}
func sortMap(m []KeyValue ) []KeyValue {
sm := make ([]KeyValue , len (m ))
copy (sm , m )
slices .SortFunc (sm , func (a , b KeyValue ) int {
return cmp .Compare (a .Key , b .Key )
})
return sm
}
func (v Value ) String () string {
switch v .Kind () {
case KindString :
return v .asString ()
case KindInt64 :
return strconv .FormatInt (int64 (v .num ), 10 )
case KindFloat64 :
return strconv .FormatFloat (v .asFloat64 (), 'g' , -1 , 64 )
case KindBool :
return strconv .FormatBool (v .asBool ())
case KindBytes :
return fmt .Sprint (v .asBytes ())
case KindMap :
return fmt .Sprint (v .asMap ())
case KindSlice :
return fmt .Sprint (v .asSlice ())
case KindEmpty :
return "<nil>"
default :
return fmt .Sprintf ("<unhandled log.Kind: %s>" , v .Kind ())
}
}
type KeyValue struct {
Key string
Value Value
}
func (a KeyValue ) Equal (b KeyValue ) bool {
return a .Key == b .Key && a .Value .Equal (b .Value )
}
func String (key , value string ) KeyValue {
return KeyValue {key , StringValue (value )}
}
func Int64 (key string , value int64 ) KeyValue {
return KeyValue {key , Int64Value (value )}
}
func Int (key string , value int ) KeyValue {
return KeyValue {key , IntValue (value )}
}
func Float64 (key string , value float64 ) KeyValue {
return KeyValue {key , Float64Value (value )}
}
func Bool (key string , value bool ) KeyValue {
return KeyValue {key , BoolValue (value )}
}
func Bytes (key string , value []byte ) KeyValue {
return KeyValue {key , BytesValue (value )}
}
func Slice (key string , value ...Value ) KeyValue {
return KeyValue {key , SliceValue (value ...)}
}
func Map (key string , value ...KeyValue ) KeyValue {
return KeyValue {key , MapValue (value ...)}
}
func Empty (key string ) KeyValue {
return KeyValue {key , Value {}}
}
func (a KeyValue ) String () string {
return fmt .Sprintf ("%s:%s" , a .Key , a .Value )
}
func ValueFromAttribute (value attribute .Value ) Value {
switch value .Type () {
case attribute .INVALID :
return Value {}
case attribute .BOOL :
return BoolValue (value .AsBool ())
case attribute .BOOLSLICE :
val := value .AsBoolSlice ()
res := make ([]Value , 0 , len (val ))
for _ , v := range val {
res = append (res , BoolValue (v ))
}
return SliceValue (res ...)
case attribute .INT64 :
return Int64Value (value .AsInt64 ())
case attribute .INT64SLICE :
val := value .AsInt64Slice ()
res := make ([]Value , 0 , len (val ))
for _ , v := range val {
res = append (res , Int64Value (v ))
}
return SliceValue (res ...)
case attribute .FLOAT64 :
return Float64Value (value .AsFloat64 ())
case attribute .FLOAT64SLICE :
val := value .AsFloat64Slice ()
res := make ([]Value , 0 , len (val ))
for _ , v := range val {
res = append (res , Float64Value (v ))
}
return SliceValue (res ...)
case attribute .STRING :
return StringValue (value .AsString ())
case attribute .STRINGSLICE :
val := value .AsStringSlice ()
res := make ([]Value , 0 , len (val ))
for _ , v := range val {
res = append (res , StringValue (v ))
}
return SliceValue (res ...)
}
panic ("unknown attribute type" )
}
func KeyValueFromAttribute (kv attribute .KeyValue ) KeyValue {
return KeyValue {
Key : string (kv .Key ),
Value : ValueFromAttribute (kv .Value ),
}
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .