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