blob: 7d7a34c0f650ecec69c95cf41798b1be97597920 [file] [log] [blame]
// 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}
}