summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/tools/internal/event/label/label.go
blob: b55c12eb250754d8aea45a1ea822cb7893342a30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package label

import (
	"fmt"
	"io"
	"reflect"
	"unsafe"
)

// Key is used as the identity of a Label.
// Keys are intended to be compared by pointer only, the name should be unique
// for communicating with external systems, but it is not required or enforced.
type Key interface {
	// Name returns the key name.
	Name() string
	// Description returns a string that can be used to describe the value.
	Description() string

	// Format is used in formatting to append the value of the label to the
	// supplied buffer.
	// The formatter may use the supplied buf as a scratch area to avoid
	// allocations.
	Format(w io.Writer, buf []byte, l Label)
}

// Label holds a key and value pair.
// It is normally used when passing around lists of labels.
type Label struct {
	key     Key
	packed  uint64
	untyped interface{}
}

// Map is the interface to a collection of Labels indexed by key.
type Map interface {
	// Find returns the label that matches the supplied key.
	Find(key Key) Label
}

// List is the interface to something that provides an iterable
// list of labels.
// Iteration should start from 0 and continue until Valid returns false.
type List interface {
	// Valid returns true if the index is within range for the list.
	// It does not imply the label at that index will itself be valid.
	Valid(index int) bool
	// Label returns the label at the given index.
	Label(index int) Label
}

// list implements LabelList for a list of Labels.
type list struct {
	labels []Label
}

// filter wraps a LabelList filtering out specific labels.
type filter struct {
	keys       []Key
	underlying List
}

// listMap implements LabelMap for a simple list of labels.
type listMap struct {
	labels []Label
}

// mapChain implements LabelMap for a list of underlying LabelMap.
type mapChain struct {
	maps []Map
}

// OfValue creates a new label from the key and value.
// This method is for implementing new key types, label creation should
// normally be done with the Of method of the key.
func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} }

// UnpackValue assumes the label was built using LabelOfValue and returns the value
// that was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Label) UnpackValue() interface{} { return t.untyped }

// Of64 creates a new label from a key and a uint64. This is often
// used for non uint64 values that can be packed into a uint64.
// This method is for implementing new key types, label creation should
// normally be done with the Of method of the key.
func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} }

// Unpack64 assumes the label was built using LabelOf64 and returns the value that
// was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Label) Unpack64() uint64 { return t.packed }

// OfString creates a new label from a key and a string.
// This method is for implementing new key types, label creation should
// normally be done with the Of method of the key.
func OfString(k Key, v string) Label {
	hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
	return Label{
		key:     k,
		packed:  uint64(hdr.Len),
		untyped: unsafe.Pointer(hdr.Data),
	}
}

// UnpackString assumes the label was built using LabelOfString and returns the
// value that was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Label) UnpackString() string {
	var v string
	hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
	hdr.Data = uintptr(t.untyped.(unsafe.Pointer))
	hdr.Len = int(t.packed)
	return *(*string)(unsafe.Pointer(hdr))
}

// Valid returns true if the Label is a valid one (it has a key).
func (t Label) Valid() bool { return t.key != nil }

// Key returns the key of this Label.
func (t Label) Key() Key { return t.key }

// Format is used for debug printing of labels.
func (t Label) Format(f fmt.State, r rune) {
	if !t.Valid() {
		io.WriteString(f, `nil`)
		return
	}
	io.WriteString(f, t.Key().Name())
	io.WriteString(f, "=")
	var buf [128]byte
	t.Key().Format(f, buf[:0], t)
}

func (l *list) Valid(index int) bool {
	return index >= 0 && index < len(l.labels)
}

func (l *list) Label(index int) Label {
	return l.labels[index]
}

func (f *filter) Valid(index int) bool {
	return f.underlying.Valid(index)
}

func (f *filter) Label(index int) Label {
	l := f.underlying.Label(index)
	for _, f := range f.keys {
		if l.Key() == f {
			return Label{}
		}
	}
	return l
}

func (lm listMap) Find(key Key) Label {
	for _, l := range lm.labels {
		if l.Key() == key {
			return l
		}
	}
	return Label{}
}

func (c mapChain) Find(key Key) Label {
	for _, src := range c.maps {
		l := src.Find(key)
		if l.Valid() {
			return l
		}
	}
	return Label{}
}

var emptyList = &list{}

func NewList(labels ...Label) List {
	if len(labels) == 0 {
		return emptyList
	}
	return &list{labels: labels}
}

func Filter(l List, keys ...Key) List {
	if len(keys) == 0 {
		return l
	}
	return &filter{keys: keys, underlying: l}
}

func NewMap(labels ...Label) Map {
	return listMap{labels: labels}
}

func MergeMaps(srcs ...Map) Map {
	var nonNil []Map
	for _, src := range srcs {
		if src != nil {
			nonNil = append(nonNil, src)
		}
	}
	if len(nonNil) == 1 {
		return nonNil[0]
	}
	return mapChain{maps: nonNil}
}