| // 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" |
| ) |
| |
| // Tag holds a key and value pair. |
| // It is normally used when passing around lists of tags. |
| type Tag struct { |
| Key Key |
| packed uint64 |
| str string |
| 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 interface{}) Tag |
| } |
| |
| // TagPointer is the interface to something that provides an iterable |
| // list of tags. |
| type TagPointer interface { |
| // Next advances to the next entry in the list and return a TagIterator for it. |
| // It will return nil if there are no more entries. |
| Next() TagPointer |
| // Tag returns the tag the pointer is for. |
| Tag() Tag |
| } |
| |
| // TagIterator is used to iterate through tags using TagPointer. |
| // It is a small helper that will normally fully inline to make it easier to |
| // manage the fact that pointer advance returns a new pointer rather than |
| // moving the existing one. |
| type TagIterator struct { |
| ptr TagPointer |
| } |
| |
| // tagPointer implements TagPointer over a simple list of tags. |
| type tagPointer struct { |
| tags []Tag |
| } |
| |
| // tagPointer wraps a TagPointer filtering out specific tags. |
| type tagFilter struct { |
| filter []Key |
| underlying TagPointer |
| } |
| |
| // tagPointerChain implements TagMap for a list of underlying TagMap. |
| type tagPointerChain struct { |
| ptrs []TagPointer |
| } |
| |
| // 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 |
| } |
| |
| // Valid returns true if the Tag is a valid one (it has a key). |
| func (t Tag) Valid() bool { return t.Key != nil } |
| |
| // Format is used for debug printing of tags. |
| func (t Tag) Format(f fmt.State, r rune) { |
| if !t.Valid() { |
| fmt.Fprintf(f, `nil`) |
| return |
| } |
| switch key := t.Key.(type) { |
| case *IntKey: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *Int8Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *Int16Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *Int32Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *Int64Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *UIntKey: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *UInt8Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *UInt16Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *UInt32Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *UInt64Key: |
| fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t)) |
| case *Float32Key: |
| fmt.Fprintf(f, "%s=%g", key.Name(), key.From(t)) |
| case *Float64Key: |
| fmt.Fprintf(f, "%s=%g", key.Name(), key.From(t)) |
| case *BooleanKey: |
| fmt.Fprintf(f, "%s=%t", key.Name(), key.From(t)) |
| case *StringKey: |
| fmt.Fprintf(f, "%s=%q", key.Name(), key.From(t)) |
| case *ErrorKey: |
| fmt.Fprintf(f, "%s=%q", key.Name(), key.From(t)) |
| case *ValueKey: |
| fmt.Fprintf(f, "%s=%q", key.Name(), key.From(t)) |
| default: |
| fmt.Fprintf(f, `%s="invalid type %T"`, key.Name(), key) |
| } |
| } |
| |
| func (i *TagIterator) Valid() bool { |
| return i.ptr != nil |
| } |
| |
| func (i *TagIterator) Advance() { |
| i.ptr = i.ptr.Next() |
| } |
| |
| func (i *TagIterator) Tag() Tag { |
| return i.ptr.Tag() |
| } |
| |
| func (i tagPointer) Next() TagPointer { |
| // loop until we are on a valid tag |
| for { |
| // move on one tag |
| i.tags = i.tags[1:] |
| // check if we have exhausted the current list |
| if len(i.tags) == 0 { |
| // no more tags, so no more iterator |
| return nil |
| } |
| // if the tag is valid, we are done |
| if i.tags[0].Valid() { |
| return i |
| } |
| } |
| } |
| |
| func (i tagPointer) Tag() Tag { |
| return i.tags[0] |
| } |
| |
| func (i tagFilter) Next() TagPointer { |
| // loop until we are on a valid tag |
| for { |
| i.underlying = i.underlying.Next() |
| if i.underlying == nil { |
| return nil |
| } |
| if !i.filtered() { |
| return i |
| } |
| } |
| } |
| |
| func (i tagFilter) filtered() bool { |
| tag := i.underlying.Tag() |
| for _, f := range i.filter { |
| if tag.Key == f { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (i tagFilter) Tag() Tag { |
| return i.underlying.Tag() |
| } |
| |
| func (i tagPointerChain) Next() TagPointer { |
| i.ptrs[0] = i.ptrs[0].Next() |
| if i.ptrs[0] == nil { |
| i.ptrs = i.ptrs[1:] |
| } |
| if len(i.ptrs) == 0 { |
| return nil |
| } |
| return i |
| } |
| |
| func (i tagPointerChain) Tag() Tag { |
| return i.ptrs[0].Tag() |
| } |
| |
| func (l tagMap) Find(key interface{}) Tag { |
| for _, tag := range l.tags { |
| if tag.Key == key { |
| return tag |
| } |
| } |
| return Tag{} |
| } |
| |
| func (c tagMapChain) Find(key interface{}) Tag { |
| for _, src := range c.maps { |
| tag := src.Find(key) |
| if tag.Valid() { |
| return tag |
| } |
| } |
| return Tag{} |
| } |
| |
| func NewTagIterator(tags ...Tag) TagIterator { |
| if len(tags) == 0 { |
| return TagIterator{} |
| } |
| result := TagIterator{ptr: tagPointer{tags: tags}} |
| if !result.Tag().Valid() { |
| result.Advance() |
| } |
| return result |
| } |
| |
| func Filter(it TagIterator, keys ...Key) TagIterator { |
| if !it.Valid() || len(keys) == 0 { |
| return it |
| } |
| ptr := tagFilter{filter: keys, underlying: it.ptr} |
| result := TagIterator{ptr: ptr} |
| if ptr.filtered() { |
| result.Advance() |
| } |
| return result |
| } |
| |
| func ChainTagIterators(iterators ...TagIterator) TagIterator { |
| if len(iterators) == 0 { |
| return TagIterator{} |
| } |
| ptrs := make([]TagPointer, 0, len(iterators)) |
| for _, it := range iterators { |
| if it.Valid() { |
| ptrs = append(ptrs, it.ptr) |
| } |
| } |
| if len(ptrs) == 0 { |
| return TagIterator{} |
| } |
| return TagIterator{ptr: tagPointerChain{ptrs: ptrs}} |
| } |
| |
| func NewTagMap(tags ...Tag) TagMap { |
| return tagMap{tags: tags} |
| } |
| |
| func MergeTagMaps(srcs ...TagMap) TagMap { |
| return tagMapChain{maps: srcs} |
| } |