// 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" "io" "reflect" "unsafe" ) // Tag holds a key and value pair. // It is normally used when passing around lists of tags. type Tag struct { key Key packed uint64 untyped interface{} } // TagMap is the interface to a collection of Tags indexed by key. type TagMap interface { // Find returns the tag that matches the supplied key. Find(key Key) Tag } // TagList is the interface to something that provides an iterable // list of tags. // Iteration should start from 0 and continue until Valid returns false. type TagList interface { // Valid returns true if the index is within range for the list. // It does not imply the tag at that index will itself be valid. Valid(index int) bool // Tag returns the tag at the given index. Tag(index int) Tag } // tagList implements TagList for a list of Tags. type tagList struct { tags []Tag } // tagFilter wraps a TagList filtering out specific tags. type tagFilter struct { keys []Key underlying TagList } // tagMap implements TagMap for a simple list of tags. type tagMap struct { tags []Tag } // tagMapChain implements TagMap for a list of underlying TagMap. type tagMapChain struct { maps []TagMap } // TagOfValue creates a new tag from the key and value. // This method is for implementing new key types, tag creation should // normally be done with the Of method of the key. func TagOfValue(k Key, value interface{}) Tag { return Tag{key: k, untyped: value} } // UnpackValue assumes the tag was built using TagOfValue and returns the value // that was passed to that constructor. // This method is for implementing new key types, for type safety normal // access should be done with the From method of the key. func (t Tag) UnpackValue() interface{} { return t.untyped } // TagOf64 creates a new tag from a key and a uint64. This is often // used for non uint64 values that can be packed into a uint64. // This method is for implementing new key types, tag creation should // normally be done with the Of method of the key. func TagOf64(k Key, v uint64) Tag { return Tag{key: k, packed: v} } // Unpack64 assumes the tag was built using TagOf64 and returns the value that // was passed to that constructor. // This method is for implementing new key types, for type safety normal // access should be done with the From method of the key. func (t Tag) Unpack64() uint64 { return t.packed } // TagOfString creates a new tag from a key and a string. // This method is for implementing new key types, tag creation should // normally be done with the Of method of the key. func TagOfString(k Key, v string) Tag { hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) return Tag{ key: k, packed: uint64(hdr.Len), untyped: unsafe.Pointer(hdr.Data), } } // UnpackString assumes the tag was built using TagOfString and returns the // value that was passed to that constructor. // This method is for implementing new key types, for type safety normal // access should be done with the From method of the key. func (t Tag) UnpackString() string { var v string hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) hdr.Data = uintptr(t.untyped.(unsafe.Pointer)) hdr.Len = int(t.packed) return *(*string)(unsafe.Pointer(hdr)) } // Valid returns true if the Tag is a valid one (it has a key). func (t Tag) Valid() bool { return t.key != nil } // Key returns the key of this Tag. func (t Tag) Key() Key { return t.key } // Format is used for debug printing of tags. func (t Tag) Format(f fmt.State, r rune) { if !t.Valid() { io.WriteString(f, `nil`) return } io.WriteString(f, t.Key().Name()) io.WriteString(f, "=") var buf [128]byte t.Key().Format(f, buf[:0], t) } func (l *tagList) Valid(index int) bool { return index >= 0 && index < len(l.tags) } func (l *tagList) Tag(index int) Tag { return l.tags[index] } func (f *tagFilter) Valid(index int) bool { return f.underlying.Valid(index) } func (f *tagFilter) Tag(index int) Tag { tag := f.underlying.Tag(index) for _, f := range f.keys { if tag.Key() == f { return Tag{} } } return tag } func (l tagMap) Find(key Key) Tag { for _, tag := range l.tags { if tag.Key() == key { return tag } } return Tag{} } func (c tagMapChain) Find(key Key) Tag { for _, src := range c.maps { tag := src.Find(key) if tag.Valid() { return tag } } return Tag{} } var emptyList = &tagList{} func NewTagList(tags ...Tag) TagList { if len(tags) == 0 { return emptyList } return &tagList{tags: tags} } func Filter(l TagList, keys ...Key) TagList { if len(keys) == 0 { return l } return &tagFilter{keys: keys, underlying: l} } func NewTagMap(tags ...Tag) TagMap { return tagMap{tags: tags} } func MergeTagMaps(srcs ...TagMap) TagMap { var nonNil []TagMap for _, src := range srcs { if src != nil { nonNil = append(nonNil, src) } } if len(nonNil) == 1 { return nonNil[0] } return tagMapChain{maps: nonNil} }