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 5package label 6 7import ( 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. 17type 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. 32type 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. 39type 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. 47type 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. 56type list struct { 57 labels []Label 58} 59 60// filter wraps a LabelList filtering out specific labels. 61type filter struct { 62 keys []Key 63 underlying List 64} 65 66// listMap implements LabelMap for a simple list of labels. 67type listMap struct { 68 labels []Label 69} 70 71// mapChain implements LabelMap for a list of underlying LabelMap. 72type 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. 79func 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. 85func (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. 91func 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. 97func (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. 102func 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. 115func (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). 124func (t Label) Valid() bool { return t.key != nil } 125 126// Key returns the key of this Label. 127func (t Label) Key() Key { return t.key } 128 129// Format is used for debug printing of labels. 130func (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 141func (l *list) Valid(index int) bool { 142 return index >= 0 && index < len(l.labels) 143} 144 145func (l *list) Label(index int) Label { 146 return l.labels[index] 147} 148 149func (f *filter) Valid(index int) bool { 150 return f.underlying.Valid(index) 151} 152 153func (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 163func (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 172func (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 182var emptyList = &list{} 183 184func NewList(labels ...Label) List { 185 if len(labels) == 0 { 186 return emptyList 187 } 188 return &list{labels: labels} 189} 190 191func Filter(l List, keys ...Key) List { 192 if len(keys) == 0 { 193 return l 194 } 195 return &filter{keys: keys, underlying: l} 196} 197 198func NewMap(labels ...Label) Map { 199 return listMap{labels: labels} 200} 201 202func 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} 214