github.com/jd-ly/tools@v0.5.7/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 // OfString creates a new label from a key and a string. 100 // This method is for implementing new key types, label creation should 101 // normally be done with the Of method of the key. 102 func OfString(k Key, v string) Label { 103 hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) 104 return Label{ 105 key: k, 106 packed: uint64(hdr.Len), 107 untyped: unsafe.Pointer(hdr.Data), 108 } 109 } 110 111 // UnpackString assumes the label was built using LabelOfString and returns the 112 // value that was passed to that constructor. 113 // This method is for implementing new key types, for type safety normal 114 // access should be done with the From method of the key. 115 func (t Label) UnpackString() string { 116 var v string 117 hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) 118 hdr.Data = uintptr(t.untyped.(unsafe.Pointer)) 119 hdr.Len = int(t.packed) 120 return *(*string)(unsafe.Pointer(hdr)) 121 } 122 123 // Valid returns true if the Label is a valid one (it has a key). 124 func (t Label) Valid() bool { return t.key != nil } 125 126 // Key returns the key of this Label. 127 func (t Label) Key() Key { return t.key } 128 129 // Format is used for debug printing of labels. 130 func (t Label) Format(f fmt.State, r rune) { 131 if !t.Valid() { 132 io.WriteString(f, `nil`) 133 return 134 } 135 io.WriteString(f, t.Key().Name()) 136 io.WriteString(f, "=") 137 var buf [128]byte 138 t.Key().Format(f, buf[:0], t) 139 } 140 141 func (l *list) Valid(index int) bool { 142 return index >= 0 && index < len(l.labels) 143 } 144 145 func (l *list) Label(index int) Label { 146 return l.labels[index] 147 } 148 149 func (f *filter) Valid(index int) bool { 150 return f.underlying.Valid(index) 151 } 152 153 func (f *filter) Label(index int) Label { 154 l := f.underlying.Label(index) 155 for _, f := range f.keys { 156 if l.Key() == f { 157 return Label{} 158 } 159 } 160 return l 161 } 162 163 func (lm listMap) Find(key Key) Label { 164 for _, l := range lm.labels { 165 if l.Key() == key { 166 return l 167 } 168 } 169 return Label{} 170 } 171 172 func (c mapChain) Find(key Key) Label { 173 for _, src := range c.maps { 174 l := src.Find(key) 175 if l.Valid() { 176 return l 177 } 178 } 179 return Label{} 180 } 181 182 var emptyList = &list{} 183 184 func NewList(labels ...Label) List { 185 if len(labels) == 0 { 186 return emptyList 187 } 188 return &list{labels: labels} 189 } 190 191 func Filter(l List, keys ...Key) List { 192 if len(keys) == 0 { 193 return l 194 } 195 return &filter{keys: keys, underlying: l} 196 } 197 198 func NewMap(labels ...Label) Map { 199 return listMap{labels: labels} 200 } 201 202 func MergeMaps(srcs ...Map) Map { 203 var nonNil []Map 204 for _, src := range srcs { 205 if src != nil { 206 nonNil = append(nonNil, src) 207 } 208 } 209 if len(nonNil) == 1 { 210 return nonNil[0] 211 } 212 return mapChain{maps: nonNil} 213 }