label.go raw

   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