label.go (5431B)
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 "slices" 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 // Append appends the formatted value of the label to the supplied buffer. 23 Append(buf []byte, l Label) []byte 24 } 25 26 // Label holds a key and value pair. 27 // It is normally used when passing around lists of labels. 28 type Label struct { 29 key Key 30 packed uint64 31 untyped any 32 } 33 34 // Map is the interface to a collection of Labels indexed by key. 35 type Map interface { 36 // Find returns the label that matches the supplied key. 37 Find(key Key) Label 38 } 39 40 // List is the interface to something that provides an iterable 41 // list of labels. 42 // Iteration should start from 0 and continue until Valid returns false. 43 type List interface { 44 // Valid returns true if the index is within range for the list. 45 // It does not imply the label at that index will itself be valid. 46 Valid(index int) bool 47 // Label returns the label at the given index. 48 Label(index int) Label 49 } 50 51 // list implements LabelList for a list of Labels. 52 type list struct { 53 labels []Label 54 } 55 56 // filter wraps a LabelList filtering out specific labels. 57 type filter struct { 58 keys []Key 59 underlying List 60 } 61 62 // listMap implements LabelMap for a simple list of labels. 63 type listMap struct { 64 labels []Label 65 } 66 67 // mapChain implements LabelMap for a list of underlying LabelMap. 68 type mapChain struct { 69 maps []Map 70 } 71 72 // OfValue creates a new label from the key and value. 73 // This method is for implementing new key types, label creation should 74 // normally be done with the Of method of the key. 75 func OfValue(k Key, value any) Label { return Label{key: k, untyped: value} } 76 77 // UnpackValue assumes the label was built using LabelOfValue and returns the value 78 // that was passed to that constructor. 79 // This method is for implementing new key types, for type safety normal 80 // access should be done with the From method of the key. 81 func (t Label) UnpackValue() any { return t.untyped } 82 83 // Of64 creates a new label from a key and a uint64. This is often 84 // used for non uint64 values that can be packed into a uint64. 85 // This method is for implementing new key types, label creation should 86 // normally be done with the Of method of the key. 87 func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} } 88 89 // Unpack64 assumes the label was built using LabelOf64 and returns the value that 90 // was passed to that constructor. 91 // This method is for implementing new key types, for type safety normal 92 // access should be done with the From method of the key. 93 func (t Label) Unpack64() uint64 { return t.packed } 94 95 type stringptr unsafe.Pointer 96 97 // OfString creates a new label from a key and a string. 98 // This method is for implementing new key types, label creation should 99 // normally be done with the Of method of the key. 100 func OfString(k Key, v string) Label { 101 return Label{ 102 key: k, 103 packed: uint64(len(v)), 104 untyped: stringptr(unsafe.StringData(v)), 105 } 106 } 107 108 // UnpackString assumes the label was built using LabelOfString and returns the 109 // value that was passed to that constructor. 110 // This method is for implementing new key types, for type safety normal 111 // access should be done with the From method of the key. 112 func (t Label) UnpackString() string { 113 return unsafe.String((*byte)(t.untyped.(stringptr)), int(t.packed)) 114 } 115 116 // Valid returns true if the Label is a valid one (it has a key). 117 func (t Label) Valid() bool { return t.key != nil } 118 119 // Key returns the key of this Label. 120 func (t Label) Key() Key { return t.key } 121 122 // Format is used for debug printing of labels. 123 func (t Label) Format(f fmt.State, r rune) { 124 if !t.Valid() { 125 io.WriteString(f, `nil`) 126 return 127 } 128 io.WriteString(f, t.Key().Name()) 129 io.WriteString(f, "=") 130 f.Write(t.Key().Append(nil, t)) // ignore error 131 } 132 133 func (l *list) Valid(index int) bool { 134 return index >= 0 && index < len(l.labels) 135 } 136 137 func (l *list) Label(index int) Label { 138 return l.labels[index] 139 } 140 141 func (f *filter) Valid(index int) bool { 142 return f.underlying.Valid(index) 143 } 144 145 func (f *filter) Label(index int) Label { 146 l := f.underlying.Label(index) 147 if slices.Contains(f.keys, l.Key()) { 148 return Label{} 149 } 150 return l 151 } 152 153 func (lm listMap) Find(key Key) Label { 154 for _, l := range lm.labels { 155 if l.Key() == key { 156 return l 157 } 158 } 159 return Label{} 160 } 161 162 func (c mapChain) Find(key Key) Label { 163 for _, src := range c.maps { 164 l := src.Find(key) 165 if l.Valid() { 166 return l 167 } 168 } 169 return Label{} 170 } 171 172 var emptyList = &list{} 173 174 func NewList(labels ...Label) List { 175 if len(labels) == 0 { 176 return emptyList 177 } 178 return &list{labels: labels} 179 } 180 181 func Filter(l List, keys ...Key) List { 182 if len(keys) == 0 { 183 return l 184 } 185 return &filter{keys: keys, underlying: l} 186 } 187 188 func NewMap(labels ...Label) Map { 189 return listMap{labels: labels} 190 } 191 192 func MergeMaps(srcs ...Map) Map { 193 var nonNil []Map 194 for _, src := range srcs { 195 if src != nil { 196 nonNil = append(nonNil, src) 197 } 198 } 199 if len(nonNil) == 1 { 200 return nonNil[0] 201 } 202 return mapChain{maps: nonNil} 203 }