github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/event/label/label.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package label 6 7 import ( 8 "fmt" 9 "io" 10 "reflect" 11 "unsafe" 12 ) 13 14 // Key is used as the identity of a Label. 15 // Keys are intended to be compared by pointer only, the name should be unique 16 // for communicating with external systems, but it is not required or enforced. 17 type Key interface { 18 // Name returns the key name. 19 Name() string 20 // Description returns a string that can be used to describe the value. 21 Description() string 22 23 // Format is used in formatting to append the value of the label to the 24 // supplied buffer. 25 // The formatter may use the supplied buf as a scratch area to avoid 26 // allocations. 27 Format(w io.Writer, buf []byte, l Label) 28 } 29 30 // Label holds a key and value pair. 31 // It is normally used when passing around lists of labels. 32 type Label struct { 33 key Key 34 packed uint64 35 untyped interface{} 36 } 37 38 // Map is the interface to a collection of Labels indexed by key. 39 type Map interface { 40 // Find returns the label that matches the supplied key. 41 Find(key Key) Label 42 } 43 44 // List is the interface to something that provides an iterable 45 // list of labels. 46 // Iteration should start from 0 and continue until Valid returns false. 47 type List interface { 48 // Valid returns true if the index is within range for the list. 49 // It does not imply the label at that index will itself be valid. 50 Valid(index int) bool 51 // Label returns the label at the given index. 52 Label(index int) Label 53 } 54 55 // list implements LabelList for a list of Labels. 56 type list struct { 57 labels []Label 58 } 59 60 // filter wraps a LabelList filtering out specific labels. 61 type filter struct { 62 keys []Key 63 underlying List 64 } 65 66 // listMap implements LabelMap for a simple list of labels. 67 type listMap struct { 68 labels []Label 69 } 70 71 // mapChain implements LabelMap for a list of underlying LabelMap. 72 type mapChain struct { 73 maps []Map 74 } 75 76 // OfValue creates a new label from the key and value. 77 // This method is for implementing new key types, label creation should 78 // normally be done with the Of method of the key. 79 func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} } 80 81 // UnpackValue assumes the label was built using LabelOfValue and returns the value 82 // that was passed to that constructor. 83 // This method is for implementing new key types, for type safety normal 84 // access should be done with the From method of the key. 85 func (t Label) UnpackValue() interface{} { return t.untyped } 86 87 // Of64 creates a new label from a key and a uint64. This is often 88 // used for non uint64 values that can be packed into a uint64. 89 // This method is for implementing new key types, label creation should 90 // normally be done with the Of method of the key. 91 func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} } 92 93 // Unpack64 assumes the label was built using LabelOf64 and returns the value that 94 // was passed to that constructor. 95 // This method is for implementing new key types, for type safety normal 96 // access should be done with the From method of the key. 97 func (t Label) Unpack64() uint64 { return t.packed } 98 99 type stringptr unsafe.Pointer 100 101 // OfString creates a new label from a key and a string. 102 // This method is for implementing new key types, label creation should 103 // normally be done with the Of method of the key. 104 func OfString(k Key, v string) Label { 105 hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) 106 return Label{ 107 key: k, 108 packed: uint64(hdr.Len), 109 untyped: stringptr(hdr.Data), 110 } 111 } 112 113 // UnpackString assumes the label was built using LabelOfString and returns the 114 // value that was passed to that constructor. 115 // This method is for implementing new key types, for type safety normal 116 // access should be done with the From method of the key. 117 func (t Label) UnpackString() string { 118 var v string 119 hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) 120 hdr.Data = uintptr(t.untyped.(stringptr)) 121 hdr.Len = int(t.packed) 122 return v 123 } 124 125 // Valid returns true if the Label is a valid one (it has a key). 126 func (t Label) Valid() bool { return t.key != nil } 127 128 // Key returns the key of this Label. 129 func (t Label) Key() Key { return t.key } 130 131 // Format is used for debug printing of labels. 132 func (t Label) Format(f fmt.State, r rune) { 133 if !t.Valid() { 134 io.WriteString(f, `nil`) 135 return 136 } 137 io.WriteString(f, t.Key().Name()) 138 io.WriteString(f, "=") 139 var buf [128]byte 140 t.Key().Format(f, buf[:0], t) 141 } 142 143 func (l *list) Valid(index int) bool { 144 return index >= 0 && index < len(l.labels) 145 } 146 147 func (l *list) Label(index int) Label { 148 return l.labels[index] 149 } 150 151 func (f *filter) Valid(index int) bool { 152 return f.underlying.Valid(index) 153 } 154 155 func (f *filter) Label(index int) Label { 156 l := f.underlying.Label(index) 157 for _, f := range f.keys { 158 if l.Key() == f { 159 return Label{} 160 } 161 } 162 return l 163 } 164 165 func (lm listMap) Find(key Key) Label { 166 for _, l := range lm.labels { 167 if l.Key() == key { 168 return l 169 } 170 } 171 return Label{} 172 } 173 174 func (c mapChain) Find(key Key) Label { 175 for _, src := range c.maps { 176 l := src.Find(key) 177 if l.Valid() { 178 return l 179 } 180 } 181 return Label{} 182 } 183 184 var emptyList = &list{} 185 186 func NewList(labels ...Label) List { 187 if len(labels) == 0 { 188 return emptyList 189 } 190 return &list{labels: labels} 191 } 192 193 func Filter(l List, keys ...Key) List { 194 if len(keys) == 0 { 195 return l 196 } 197 return &filter{keys: keys, underlying: l} 198 } 199 200 func NewMap(labels ...Label) Map { 201 return listMap{labels: labels} 202 } 203 204 func MergeMaps(srcs ...Map) Map { 205 var nonNil []Map 206 for _, src := range srcs { 207 if src != nil { 208 nonNil = append(nonNil, src) 209 } 210 } 211 if len(nonNil) == 1 { 212 return nonNil[0] 213 } 214 return mapChain{maps: nonNil} 215 }