| // 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 { |
| return tagMapChain{maps: srcs} |
| } |