| 
									
										
										
										
											2017-04-19 20:43:09 +08:00
										 |  |  | // Copyright 2017 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 01:38:57 +08:00
										 |  |  | //go:build !stringlabels
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | package labels | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 	"github.com/cespare/xxhash/v2" | 
					
						
							| 
									
										
										
										
											2022-12-08 11:09:43 +08:00
										 |  |  | 	"github.com/prometheus/common/model" | 
					
						
							| 
									
										
										
										
											2023-03-07 02:22:49 +08:00
										 |  |  | 	"golang.org/x/exp/slices" | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Well-known label names used by Prometheus components.
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-04-04 20:44:39 +08:00
										 |  |  | 	MetricName   = "__name__" | 
					
						
							|  |  |  | 	AlertName    = "alertname" | 
					
						
							|  |  |  | 	BucketLabel  = "le" | 
					
						
							|  |  |  | 	InstanceName = "instance" | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	labelSep = '\xfe' | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | var seps = []byte{'\xff'} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | // Label is a key/value pair of strings.
 | 
					
						
							|  |  |  | type Label struct { | 
					
						
							|  |  |  | 	Name, Value string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Labels is a sorted set of labels. Order has to be guaranteed upon
 | 
					
						
							|  |  |  | // instantiation.
 | 
					
						
							|  |  |  | type Labels []Label | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ls Labels) Len() int           { return len(ls) } | 
					
						
							|  |  |  | func (ls Labels) Swap(i, j int)      { ls[i], ls[j] = ls[j], ls[i] } | 
					
						
							|  |  |  | func (ls Labels) Less(i, j int) bool { return ls[i].Name < ls[j].Name } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ls Labels) String() string { | 
					
						
							|  |  |  | 	var b bytes.Buffer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b.WriteByte('{') | 
					
						
							|  |  |  | 	for i, l := range ls { | 
					
						
							|  |  |  | 		if i > 0 { | 
					
						
							|  |  |  | 			b.WriteByte(',') | 
					
						
							| 
									
										
										
										
											2016-12-30 00:31:14 +08:00
										 |  |  | 			b.WriteByte(' ') | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		b.WriteString(l.Name) | 
					
						
							|  |  |  | 		b.WriteByte('=') | 
					
						
							|  |  |  | 		b.WriteString(strconv.Quote(l.Value)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	b.WriteByte('}') | 
					
						
							|  |  |  | 	return b.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | // Bytes returns ls as a byte slice.
 | 
					
						
							|  |  |  | // It uses an byte invalid character as a separator and so should not be used for printing.
 | 
					
						
							|  |  |  | func (ls Labels) Bytes(buf []byte) []byte { | 
					
						
							|  |  |  | 	b := bytes.NewBuffer(buf[:0]) | 
					
						
							|  |  |  | 	b.WriteByte(labelSep) | 
					
						
							|  |  |  | 	for i, l := range ls { | 
					
						
							|  |  |  | 		if i > 0 { | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 			b.WriteByte(seps[0]) | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		b.WriteString(l.Name) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 		b.WriteByte(seps[0]) | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 		b.WriteString(l.Value) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b.Bytes() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-23 21:57:30 +08:00
										 |  |  | // MarshalJSON implements json.Marshaler.
 | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | func (ls Labels) MarshalJSON() ([]byte, error) { | 
					
						
							|  |  |  | 	return json.Marshal(ls.Map()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-23 21:57:30 +08:00
										 |  |  | // UnmarshalJSON implements json.Unmarshaler.
 | 
					
						
							| 
									
										
										
										
											2016-12-29 23:53:11 +08:00
										 |  |  | func (ls *Labels) UnmarshalJSON(b []byte) error { | 
					
						
							|  |  |  | 	var m map[string]string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := json.Unmarshal(b, &m); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ls = FromMap(m) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-09 00:29:25 +08:00
										 |  |  | // MarshalYAML implements yaml.Marshaler.
 | 
					
						
							|  |  |  | func (ls Labels) MarshalYAML() (interface{}, error) { | 
					
						
							|  |  |  | 	return ls.Map(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalYAML implements yaml.Unmarshaler.
 | 
					
						
							|  |  |  | func (ls *Labels) UnmarshalYAML(unmarshal func(interface{}) error) error { | 
					
						
							|  |  |  | 	var m map[string]string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := unmarshal(&m); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ls = FromMap(m) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 17:17:52 +08:00
										 |  |  | // MatchLabels returns a subset of Labels that matches/does not match with the provided label names based on the 'on' boolean.
 | 
					
						
							|  |  |  | // If on is set to true, it returns the subset of labels that match with the provided label names and its inverse when 'on' is set to false.
 | 
					
						
							|  |  |  | func (ls Labels) MatchLabels(on bool, names ...string) Labels { | 
					
						
							|  |  |  | 	matchedLabels := Labels{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 16:18:31 +08:00
										 |  |  | 	nameSet := make(map[string]struct{}, len(names)) | 
					
						
							| 
									
										
										
										
											2019-02-09 17:17:52 +08:00
										 |  |  | 	for _, n := range names { | 
					
						
							|  |  |  | 		nameSet[n] = struct{}{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, v := range ls { | 
					
						
							| 
									
										
										
										
											2019-12-27 17:32:19 +08:00
										 |  |  | 		if _, ok := nameSet[v.Name]; on == ok && (on || v.Name != MetricName) { | 
					
						
							| 
									
										
										
										
											2019-02-09 17:17:52 +08:00
										 |  |  | 			matchedLabels = append(matchedLabels, v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return matchedLabels | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | // Hash returns a hash value for the label set.
 | 
					
						
							| 
									
										
										
										
											2022-11-29 00:16:55 +08:00
										 |  |  | // Note: the result is not guaranteed to be consistent across different runs of Prometheus.
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | func (ls Labels) Hash() uint64 { | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 	// Use xxhash.Sum64(b) for fast path as it's faster.
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 	b := make([]byte, 0, 1024) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 	for i, v := range ls { | 
					
						
							|  |  |  | 		if len(b)+len(v.Name)+len(v.Value)+2 >= cap(b) { | 
					
						
							|  |  |  | 			// If labels entry is 1KB+ do not allocate whole entry.
 | 
					
						
							|  |  |  | 			h := xxhash.New() | 
					
						
							|  |  |  | 			_, _ = h.Write(b) | 
					
						
							|  |  |  | 			for _, v := range ls[i:] { | 
					
						
							|  |  |  | 				_, _ = h.WriteString(v.Name) | 
					
						
							|  |  |  | 				_, _ = h.Write(seps) | 
					
						
							|  |  |  | 				_, _ = h.WriteString(v.Value) | 
					
						
							|  |  |  | 				_, _ = h.Write(seps) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return h.Sum64() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		b = append(b, v.Name...) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 		b = append(b, seps[0]) | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 		b = append(b, v.Value...) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 		b = append(b, seps[0]) | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return xxhash.Sum64(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 11:56:27 +08:00
										 |  |  | // HashForLabels returns a hash value for the labels matching the provided names.
 | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | // 'names' have to be sorted in ascending order.
 | 
					
						
							|  |  |  | func (ls Labels) HashForLabels(b []byte, names ...string) (uint64, []byte) { | 
					
						
							|  |  |  | 	b = b[:0] | 
					
						
							|  |  |  | 	i, j := 0, 0 | 
					
						
							|  |  |  | 	for i < len(ls) && j < len(names) { | 
					
						
							| 
									
										
											  
											
												style: Replace `else if` cascades with `switch`
Wiser coders than myself have come to the conclusion that a `switch`
statement is almost always superior to a statement that includes any
`else if`.
The exceptions that I have found in our codebase are just these two:
* The `if else` is followed by an additional statement before the next
  condition (separated by a `;`).
* The whole thing is within a `for` loop and `break` statements are
  used. In this case, using `switch` would require tagging the `for`
  loop, which probably tips the balance.
Why are `switch` statements more readable?
For one, fewer curly braces. But more importantly, the conditions all
have the same alignment, so the whole thing follows the natural flow
of going down a list of conditions. With `else if`, in contrast, all
conditions but the first are "hidden" behind `} else if `, harder to
spot and (for no good reason) presented differently from the first
condition.
I'm sure the aforemention wise coders can list even more reasons.
In any case, I like it so much that I have found myself recommending
it in code reviews. I would like to make it a habit in our code base,
without making it a hard requirement that we would test on the CI. But
for that, there has to be a role model, so this commit eliminates all
`if else` occurrences, unless it is autogenerated code or fits one of
the exceptions above.
Signed-off-by: beorn7 <beorn@grafana.com>
											
										 
											2023-04-12 22:14:31 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case names[j] < ls[i].Name: | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 			j++ | 
					
						
							| 
									
										
											  
											
												style: Replace `else if` cascades with `switch`
Wiser coders than myself have come to the conclusion that a `switch`
statement is almost always superior to a statement that includes any
`else if`.
The exceptions that I have found in our codebase are just these two:
* The `if else` is followed by an additional statement before the next
  condition (separated by a `;`).
* The whole thing is within a `for` loop and `break` statements are
  used. In this case, using `switch` would require tagging the `for`
  loop, which probably tips the balance.
Why are `switch` statements more readable?
For one, fewer curly braces. But more importantly, the conditions all
have the same alignment, so the whole thing follows the natural flow
of going down a list of conditions. With `else if`, in contrast, all
conditions but the first are "hidden" behind `} else if `, harder to
spot and (for no good reason) presented differently from the first
condition.
I'm sure the aforemention wise coders can list even more reasons.
In any case, I like it so much that I have found myself recommending
it in code reviews. I would like to make it a habit in our code base,
without making it a hard requirement that we would test on the CI. But
for that, there has to be a role model, so this commit eliminates all
`if else` occurrences, unless it is autogenerated code or fits one of
the exceptions above.
Signed-off-by: beorn7 <beorn@grafana.com>
											
										 
											2023-04-12 22:14:31 +08:00
										 |  |  | 		case ls[i].Name < names[j]: | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 			i++ | 
					
						
							| 
									
										
											  
											
												style: Replace `else if` cascades with `switch`
Wiser coders than myself have come to the conclusion that a `switch`
statement is almost always superior to a statement that includes any
`else if`.
The exceptions that I have found in our codebase are just these two:
* The `if else` is followed by an additional statement before the next
  condition (separated by a `;`).
* The whole thing is within a `for` loop and `break` statements are
  used. In this case, using `switch` would require tagging the `for`
  loop, which probably tips the balance.
Why are `switch` statements more readable?
For one, fewer curly braces. But more importantly, the conditions all
have the same alignment, so the whole thing follows the natural flow
of going down a list of conditions. With `else if`, in contrast, all
conditions but the first are "hidden" behind `} else if `, harder to
spot and (for no good reason) presented differently from the first
condition.
I'm sure the aforemention wise coders can list even more reasons.
In any case, I like it so much that I have found myself recommending
it in code reviews. I would like to make it a habit in our code base,
without making it a hard requirement that we would test on the CI. But
for that, there has to be a role model, so this commit eliminates all
`if else` occurrences, unless it is autogenerated code or fits one of
the exceptions above.
Signed-off-by: beorn7 <beorn@grafana.com>
											
										 
											2023-04-12 22:14:31 +08:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 			b = append(b, ls[i].Name...) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 			b = append(b, seps[0]) | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 			b = append(b, ls[i].Value...) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 			b = append(b, seps[0]) | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 			i++ | 
					
						
							|  |  |  | 			j++ | 
					
						
							| 
									
										
										
										
											2018-07-18 11:56:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 	return xxhash.Sum64(b), b | 
					
						
							| 
									
										
										
										
											2018-07-18 11:56:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // HashWithoutLabels returns a hash value for all labels except those matching
 | 
					
						
							|  |  |  | // the provided names.
 | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | // 'names' have to be sorted in ascending order.
 | 
					
						
							|  |  |  | func (ls Labels) HashWithoutLabels(b []byte, names ...string) (uint64, []byte) { | 
					
						
							|  |  |  | 	b = b[:0] | 
					
						
							|  |  |  | 	j := 0 | 
					
						
							|  |  |  | 	for i := range ls { | 
					
						
							|  |  |  | 		for j < len(names) && names[j] < ls[i].Name { | 
					
						
							|  |  |  | 			j++ | 
					
						
							| 
									
										
										
										
											2018-07-18 11:56:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 		if ls[i].Name == MetricName || (j < len(names) && ls[i].Name == names[j]) { | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2018-07-18 11:56:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 		b = append(b, ls[i].Name...) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 		b = append(b, seps[0]) | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 		b = append(b, ls[i].Value...) | 
					
						
							| 
									
										
										
										
											2020-10-15 18:31:28 +08:00
										 |  |  | 		b = append(b, seps[0]) | 
					
						
							| 
									
										
										
										
											2018-07-18 11:56:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-28 20:52:51 +08:00
										 |  |  | 	return xxhash.Sum64(b), b | 
					
						
							| 
									
										
										
										
											2018-07-18 11:56:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | // BytesWithLabels is just as Bytes(), but only for labels matching names.
 | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | // 'names' have to be sorted in ascending order.
 | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | func (ls Labels) BytesWithLabels(buf []byte, names ...string) []byte { | 
					
						
							|  |  |  | 	b := bytes.NewBuffer(buf[:0]) | 
					
						
							|  |  |  | 	b.WriteByte(labelSep) | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 	i, j := 0, 0 | 
					
						
							|  |  |  | 	for i < len(ls) && j < len(names) { | 
					
						
							| 
									
										
											  
											
												style: Replace `else if` cascades with `switch`
Wiser coders than myself have come to the conclusion that a `switch`
statement is almost always superior to a statement that includes any
`else if`.
The exceptions that I have found in our codebase are just these two:
* The `if else` is followed by an additional statement before the next
  condition (separated by a `;`).
* The whole thing is within a `for` loop and `break` statements are
  used. In this case, using `switch` would require tagging the `for`
  loop, which probably tips the balance.
Why are `switch` statements more readable?
For one, fewer curly braces. But more importantly, the conditions all
have the same alignment, so the whole thing follows the natural flow
of going down a list of conditions. With `else if`, in contrast, all
conditions but the first are "hidden" behind `} else if `, harder to
spot and (for no good reason) presented differently from the first
condition.
I'm sure the aforemention wise coders can list even more reasons.
In any case, I like it so much that I have found myself recommending
it in code reviews. I would like to make it a habit in our code base,
without making it a hard requirement that we would test on the CI. But
for that, there has to be a role model, so this commit eliminates all
`if else` occurrences, unless it is autogenerated code or fits one of
the exceptions above.
Signed-off-by: beorn7 <beorn@grafana.com>
											
										 
											2023-04-12 22:14:31 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case names[j] < ls[i].Name: | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 			j++ | 
					
						
							| 
									
										
											  
											
												style: Replace `else if` cascades with `switch`
Wiser coders than myself have come to the conclusion that a `switch`
statement is almost always superior to a statement that includes any
`else if`.
The exceptions that I have found in our codebase are just these two:
* The `if else` is followed by an additional statement before the next
  condition (separated by a `;`).
* The whole thing is within a `for` loop and `break` statements are
  used. In this case, using `switch` would require tagging the `for`
  loop, which probably tips the balance.
Why are `switch` statements more readable?
For one, fewer curly braces. But more importantly, the conditions all
have the same alignment, so the whole thing follows the natural flow
of going down a list of conditions. With `else if`, in contrast, all
conditions but the first are "hidden" behind `} else if `, harder to
spot and (for no good reason) presented differently from the first
condition.
I'm sure the aforemention wise coders can list even more reasons.
In any case, I like it so much that I have found myself recommending
it in code reviews. I would like to make it a habit in our code base,
without making it a hard requirement that we would test on the CI. But
for that, there has to be a role model, so this commit eliminates all
`if else` occurrences, unless it is autogenerated code or fits one of
the exceptions above.
Signed-off-by: beorn7 <beorn@grafana.com>
											
										 
											2023-04-12 22:14:31 +08:00
										 |  |  | 		case ls[i].Name < names[j]: | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 			i++ | 
					
						
							| 
									
										
											  
											
												style: Replace `else if` cascades with `switch`
Wiser coders than myself have come to the conclusion that a `switch`
statement is almost always superior to a statement that includes any
`else if`.
The exceptions that I have found in our codebase are just these two:
* The `if else` is followed by an additional statement before the next
  condition (separated by a `;`).
* The whole thing is within a `for` loop and `break` statements are
  used. In this case, using `switch` would require tagging the `for`
  loop, which probably tips the balance.
Why are `switch` statements more readable?
For one, fewer curly braces. But more importantly, the conditions all
have the same alignment, so the whole thing follows the natural flow
of going down a list of conditions. With `else if`, in contrast, all
conditions but the first are "hidden" behind `} else if `, harder to
spot and (for no good reason) presented differently from the first
condition.
I'm sure the aforemention wise coders can list even more reasons.
In any case, I like it so much that I have found myself recommending
it in code reviews. I would like to make it a habit in our code base,
without making it a hard requirement that we would test on the CI. But
for that, there has to be a role model, so this commit eliminates all
`if else` occurrences, unless it is autogenerated code or fits one of
the exceptions above.
Signed-off-by: beorn7 <beorn@grafana.com>
											
										 
											2023-04-12 22:14:31 +08:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | 			if b.Len() > 1 { | 
					
						
							|  |  |  | 				b.WriteByte(seps[0]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			b.WriteString(ls[i].Name) | 
					
						
							|  |  |  | 			b.WriteByte(seps[0]) | 
					
						
							|  |  |  | 			b.WriteString(ls[i].Value) | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 			i++ | 
					
						
							|  |  |  | 			j++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | 	return b.Bytes() | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | // BytesWithoutLabels is just as Bytes(), but only for labels not matching names.
 | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | // 'names' have to be sorted in ascending order.
 | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | func (ls Labels) BytesWithoutLabels(buf []byte, names ...string) []byte { | 
					
						
							|  |  |  | 	b := bytes.NewBuffer(buf[:0]) | 
					
						
							|  |  |  | 	b.WriteByte(labelSep) | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 	j := 0 | 
					
						
							|  |  |  | 	for i := range ls { | 
					
						
							|  |  |  | 		for j < len(names) && names[j] < ls[i].Name { | 
					
						
							|  |  |  | 			j++ | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | 		if j < len(names) && ls[i].Name == names[j] { | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | 		if b.Len() > 1 { | 
					
						
							|  |  |  | 			b.WriteByte(seps[0]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		b.WriteString(ls[i].Name) | 
					
						
							|  |  |  | 		b.WriteByte(seps[0]) | 
					
						
							|  |  |  | 		b.WriteString(ls[i].Value) | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | 	return b.Bytes() | 
					
						
							| 
									
										
										
										
											2020-05-13 05:03:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | // Copy returns a copy of the labels.
 | 
					
						
							|  |  |  | func (ls Labels) Copy() Labels { | 
					
						
							|  |  |  | 	res := make(Labels, len(ls)) | 
					
						
							|  |  |  | 	copy(res, ls) | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | // Get returns the value for the label with the given name.
 | 
					
						
							|  |  |  | // Returns an empty string if the label doesn't exist.
 | 
					
						
							|  |  |  | func (ls Labels) Get(name string) string { | 
					
						
							|  |  |  | 	for _, l := range ls { | 
					
						
							|  |  |  | 		if l.Name == name { | 
					
						
							|  |  |  | 			return l.Value | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 22:26:24 +08:00
										 |  |  | // Has returns true if the label with the given name is present.
 | 
					
						
							| 
									
										
										
										
											2018-02-15 01:03:58 +08:00
										 |  |  | func (ls Labels) Has(name string) bool { | 
					
						
							|  |  |  | 	for _, l := range ls { | 
					
						
							|  |  |  | 		if l.Name == name { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-19 14:25:44 +08:00
										 |  |  | // HasDuplicateLabelNames returns whether ls has duplicate label names.
 | 
					
						
							| 
									
										
										
										
											2020-01-20 19:05:27 +08:00
										 |  |  | // It assumes that the labelset is sorted.
 | 
					
						
							|  |  |  | func (ls Labels) HasDuplicateLabelNames() (string, bool) { | 
					
						
							|  |  |  | 	for i, l := range ls { | 
					
						
							|  |  |  | 		if i == 0 { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if l.Name == ls[i-1].Name { | 
					
						
							|  |  |  | 			return l.Name, true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "", false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 03:53:33 +08:00
										 |  |  | // WithoutEmpty returns the labelset without empty labels.
 | 
					
						
							|  |  |  | // May return the same labelset.
 | 
					
						
							|  |  |  | func (ls Labels) WithoutEmpty() Labels { | 
					
						
							|  |  |  | 	for _, v := range ls { | 
					
						
							|  |  |  | 		if v.Value != "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-10-13 15:57:53 +08:00
										 |  |  | 		// Do not copy the slice until it's necessary.
 | 
					
						
							| 
									
										
										
										
											2019-11-19 03:53:33 +08:00
										 |  |  | 		els := make(Labels, 0, len(ls)-1) | 
					
						
							|  |  |  | 		for _, v := range ls { | 
					
						
							|  |  |  | 			if v.Value != "" { | 
					
						
							|  |  |  | 				els = append(els, v) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return els | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ls | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-08 11:09:43 +08:00
										 |  |  | // IsValid checks if the metric name or label names are valid.
 | 
					
						
							|  |  |  | func (ls Labels) IsValid() bool { | 
					
						
							|  |  |  | 	for _, l := range ls { | 
					
						
							|  |  |  | 		if l.Name == model.MetricNameLabel && !model.IsValidMetricName(model.LabelValue(l.Value)) { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !model.LabelName(l.Name).IsValid() || !model.LabelValue(l.Value).IsValid() { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | // Equal returns whether the two label sets are equal.
 | 
					
						
							|  |  |  | func Equal(ls, o Labels) bool { | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 	if len(ls) != len(o) { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, l := range ls { | 
					
						
							| 
									
										
										
										
											2022-03-15 07:30:04 +08:00
										 |  |  | 		if l != o[i] { | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Map returns a string map of the labels.
 | 
					
						
							|  |  |  | func (ls Labels) Map() map[string]string { | 
					
						
							|  |  |  | 	m := make(map[string]string, len(ls)) | 
					
						
							|  |  |  | 	for _, l := range ls { | 
					
						
							|  |  |  | 		m[l.Name] = l.Value | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return m | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 22:58:06 +08:00
										 |  |  | // EmptyLabels returns n empty Labels value, for convenience.
 | 
					
						
							|  |  |  | func EmptyLabels() Labels { | 
					
						
							|  |  |  | 	return Labels{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | // New returns a sorted Labels from the given labels.
 | 
					
						
							|  |  |  | // The caller has to guarantee that all label names are unique.
 | 
					
						
							|  |  |  | func New(ls ...Label) Labels { | 
					
						
							|  |  |  | 	set := make(Labels, 0, len(ls)) | 
					
						
							| 
									
										
										
										
											2022-12-14 02:14:58 +08:00
										 |  |  | 	set = append(set, ls...) | 
					
						
							| 
									
										
										
										
											2023-03-07 02:22:49 +08:00
										 |  |  | 	slices.SortFunc(set, func(a, b Label) bool { return a.Name < b.Name }) | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return set | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FromMap returns new sorted Labels from the given map.
 | 
					
						
							|  |  |  | func FromMap(m map[string]string) Labels { | 
					
						
							|  |  |  | 	l := make([]Label, 0, len(m)) | 
					
						
							|  |  |  | 	for k, v := range m { | 
					
						
							|  |  |  | 		l = append(l, Label{Name: k, Value: v}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return New(l...) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FromStrings creates new labels from pairs of strings.
 | 
					
						
							|  |  |  | func FromStrings(ss ...string) Labels { | 
					
						
							|  |  |  | 	if len(ss)%2 != 0 { | 
					
						
							|  |  |  | 		panic("invalid number of strings") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-25 22:22:47 +08:00
										 |  |  | 	res := make(Labels, 0, len(ss)/2) | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 	for i := 0; i < len(ss); i += 2 { | 
					
						
							|  |  |  | 		res = append(res, Label{Name: ss[i], Value: ss[i+1]}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 02:22:49 +08:00
										 |  |  | 	slices.SortFunc(res, func(a, b Label) bool { return a.Name < b.Name }) | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Compare compares the two label sets.
 | 
					
						
							|  |  |  | // The result will be 0 if a==b, <0 if a < b, and >0 if a > b.
 | 
					
						
							|  |  |  | func Compare(a, b Labels) int { | 
					
						
							|  |  |  | 	l := len(a) | 
					
						
							|  |  |  | 	if len(b) < l { | 
					
						
							|  |  |  | 		l = len(b) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < l; i++ { | 
					
						
							| 
									
										
										
										
											2020-01-01 23:45:01 +08:00
										 |  |  | 		if a[i].Name != b[i].Name { | 
					
						
							|  |  |  | 			if a[i].Name < b[i].Name { | 
					
						
							|  |  |  | 				return -1 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-03-23 22:47:11 +08:00
										 |  |  | 			return 1 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-01 23:45:01 +08:00
										 |  |  | 		if a[i].Value != b[i].Value { | 
					
						
							|  |  |  | 			if a[i].Value < b[i].Value { | 
					
						
							|  |  |  | 				return -1 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-03-23 22:47:11 +08:00
										 |  |  | 			return 1 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// If all labels so far were in common, the set with fewer labels comes first.
 | 
					
						
							|  |  |  | 	return len(a) - len(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 22:59:13 +08:00
										 |  |  | // Copy labels from b on top of whatever was in ls previously, reusing memory or expanding if needed.
 | 
					
						
							|  |  |  | func (ls *Labels) CopyFrom(b Labels) { | 
					
						
							|  |  |  | 	(*ls) = append((*ls)[:0], b...) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsEmpty returns true if ls represents an empty set of labels.
 | 
					
						
							|  |  |  | func (ls Labels) IsEmpty() bool { | 
					
						
							|  |  |  | 	return len(ls) == 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Range calls f on each label.
 | 
					
						
							|  |  |  | func (ls Labels) Range(f func(l Label)) { | 
					
						
							|  |  |  | 	for _, l := range ls { | 
					
						
							|  |  |  | 		f(l) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Validate calls f on each label. If f returns a non-nil error, then it returns that error cancelling the iteration.
 | 
					
						
							|  |  |  | func (ls Labels) Validate(f func(l Label) error) error { | 
					
						
							|  |  |  | 	for _, l := range ls { | 
					
						
							|  |  |  | 		if err := f(l); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // InternStrings calls intern on every string value inside ls, replacing them with what it returns.
 | 
					
						
							|  |  |  | func (ls *Labels) InternStrings(intern func(string) string) { | 
					
						
							|  |  |  | 	for i, l := range *ls { | 
					
						
							|  |  |  | 		(*ls)[i].Name = intern(l.Name) | 
					
						
							|  |  |  | 		(*ls)[i].Value = intern(l.Value) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReleaseStrings calls release on every string value inside ls.
 | 
					
						
							|  |  |  | func (ls Labels) ReleaseStrings(release func(string)) { | 
					
						
							|  |  |  | 	for _, l := range ls { | 
					
						
							|  |  |  | 		release(l.Name) | 
					
						
							|  |  |  | 		release(l.Value) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-01 22:35:32 +08:00
										 |  |  | // Builder allows modifying Labels.
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | type Builder struct { | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | 	base Labels | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | 	del  []string | 
					
						
							|  |  |  | 	add  []Label | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | // NewBuilder returns a new LabelsBuilder.
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | func NewBuilder(base Labels) *Builder { | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 	b := &Builder{ | 
					
						
							|  |  |  | 		del: make([]string, 0, 5), | 
					
						
							|  |  |  | 		add: make([]Label, 0, 5), | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 	b.Reset(base) | 
					
						
							|  |  |  | 	return b | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | // Reset clears all current state for the builder.
 | 
					
						
							| 
									
										
										
										
											2019-06-11 16:24:50 +08:00
										 |  |  | func (b *Builder) Reset(base Labels) { | 
					
						
							|  |  |  | 	b.base = base | 
					
						
							|  |  |  | 	b.del = b.del[:0] | 
					
						
							|  |  |  | 	b.add = b.add[:0] | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 	for _, l := range b.base { | 
					
						
							|  |  |  | 		if l.Value == "" { | 
					
						
							|  |  |  | 			b.del = append(b.del, l.Name) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-11 16:24:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | // Del deletes the label of the given name.
 | 
					
						
							|  |  |  | func (b *Builder) Del(ns ...string) *Builder { | 
					
						
							|  |  |  | 	for _, n := range ns { | 
					
						
							|  |  |  | 		for i, a := range b.add { | 
					
						
							|  |  |  | 			if a.Name == n { | 
					
						
							|  |  |  | 				b.add = append(b.add[:i], b.add[i+1:]...) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		b.del = append(b.del, n) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | // Keep removes all labels from the base except those with the given names.
 | 
					
						
							|  |  |  | func (b *Builder) Keep(ns ...string) *Builder { | 
					
						
							|  |  |  | Outer: | 
					
						
							|  |  |  | 	for _, l := range b.base { | 
					
						
							|  |  |  | 		for _, n := range ns { | 
					
						
							|  |  |  | 			if l.Name == n { | 
					
						
							|  |  |  | 				continue Outer | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		b.del = append(b.del, l.Name) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-14 03:02:25 +08:00
										 |  |  | // Set the name/value pair as a label. A value of "" means delete that label.
 | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | func (b *Builder) Set(n, v string) *Builder { | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 	if v == "" { | 
					
						
							|  |  |  | 		// Empty labels are the same as missing labels.
 | 
					
						
							|  |  |  | 		return b.Del(n) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | 	for i, a := range b.add { | 
					
						
							|  |  |  | 		if a.Name == n { | 
					
						
							|  |  |  | 			b.add[i].Value = v | 
					
						
							|  |  |  | 			return b | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	b.add = append(b.add, Label{Name: n, Value: v}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return b | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-01 02:10:21 +08:00
										 |  |  | func (b *Builder) Get(n string) string { | 
					
						
							|  |  |  | 	for _, d := range b.del { | 
					
						
							|  |  |  | 		if d == n { | 
					
						
							|  |  |  | 			return "" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, a := range b.add { | 
					
						
							|  |  |  | 		if a.Name == n { | 
					
						
							|  |  |  | 			return a.Value | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b.base.Get(n) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Range calls f on each label in the Builder.
 | 
					
						
							|  |  |  | func (b *Builder) Range(f func(l Label)) { | 
					
						
							| 
									
										
										
										
											2023-03-16 21:28:13 +08:00
										 |  |  | 	// Stack-based arrays to avoid heap allocation in most cases.
 | 
					
						
							| 
									
										
										
										
											2023-03-23 01:11:24 +08:00
										 |  |  | 	var addStack [128]Label | 
					
						
							|  |  |  | 	var delStack [128]string | 
					
						
							| 
									
										
										
										
											2023-03-16 21:28:13 +08:00
										 |  |  | 	// Take a copy of add and del, so they are unaffected by calls to Set() or Del().
 | 
					
						
							|  |  |  | 	origAdd, origDel := append(addStack[:0], b.add...), append(delStack[:0], b.del...) | 
					
						
							| 
									
										
										
										
											2023-03-01 02:10:21 +08:00
										 |  |  | 	b.base.Range(func(l Label) { | 
					
						
							|  |  |  | 		if !slices.Contains(origDel, l.Name) && !contains(origAdd, l.Name) { | 
					
						
							|  |  |  | 			f(l) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	for _, a := range origAdd { | 
					
						
							|  |  |  | 		f(a) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func contains(s []Label, n string) bool { | 
					
						
							|  |  |  | 	for _, a := range s { | 
					
						
							|  |  |  | 		if a.Name == n { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-22 23:46:02 +08:00
										 |  |  | // Labels returns the labels from the builder.
 | 
					
						
							| 
									
										
										
										
											2022-08-19 17:57:52 +08:00
										 |  |  | // If no modifications were made, the original labels are returned.
 | 
					
						
							| 
									
										
										
										
											2023-03-22 23:46:02 +08:00
										 |  |  | func (b *Builder) Labels() Labels { | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | 	if len(b.del) == 0 && len(b.add) == 0 { | 
					
						
							|  |  |  | 		return b.base | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-02 18:17:05 +08:00
										 |  |  | 	expectedSize := len(b.base) + len(b.add) - len(b.del) | 
					
						
							|  |  |  | 	if expectedSize < 1 { | 
					
						
							|  |  |  | 		expectedSize = 1 | 
					
						
							| 
									
										
										
										
											2022-08-19 17:57:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-02 18:17:05 +08:00
										 |  |  | 	res := make(Labels, 0, expectedSize) | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | 	for _, l := range b.base { | 
					
						
							| 
									
										
										
										
											2023-03-01 02:10:21 +08:00
										 |  |  | 		if slices.Contains(b.del, l.Name) || contains(b.add, l.Name) { | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		res = append(res, l) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | 	if len(b.add) > 0 { // Base is already in order, so we only need to sort if we add to it.
 | 
					
						
							|  |  |  | 		res = append(res, b.add...) | 
					
						
							| 
									
										
										
										
											2023-03-07 02:22:49 +08:00
										 |  |  | 		slices.SortFunc(res, func(a, b Label) bool { return a.Name < b.Name }) | 
					
						
							| 
									
										
										
										
											2022-06-07 12:38:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-24 21:35:24 +08:00
										 |  |  | 	return res | 
					
						
							| 
									
										
										
										
											2016-12-24 21:01:10 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-05-26 22:59:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ScratchBuilder allows efficient construction of a Labels from scratch.
 | 
					
						
							|  |  |  | type ScratchBuilder struct { | 
					
						
							|  |  |  | 	add Labels | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewScratchBuilder creates a ScratchBuilder initialized for Labels with n entries.
 | 
					
						
							|  |  |  | func NewScratchBuilder(n int) ScratchBuilder { | 
					
						
							|  |  |  | 	return ScratchBuilder{add: make([]Label, 0, n)} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *ScratchBuilder) Reset() { | 
					
						
							|  |  |  | 	b.add = b.add[:0] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Add a name/value pair.
 | 
					
						
							|  |  |  | // Note if you Add the same name twice you will get a duplicate label, which is invalid.
 | 
					
						
							|  |  |  | func (b *ScratchBuilder) Add(name, value string) { | 
					
						
							|  |  |  | 	b.add = append(b.add, Label{Name: name, Value: value}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Sort the labels added so far by name.
 | 
					
						
							|  |  |  | func (b *ScratchBuilder) Sort() { | 
					
						
							| 
									
										
										
										
											2023-03-07 02:22:49 +08:00
										 |  |  | 	slices.SortFunc(b.add, func(a, b Label) bool { return a.Name < b.Name }) | 
					
						
							| 
									
										
										
										
											2022-05-26 22:59:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-16 02:19:15 +08:00
										 |  |  | // Asssign is for when you already have a Labels which you want this ScratchBuilder to return.
 | 
					
						
							|  |  |  | func (b *ScratchBuilder) Assign(ls Labels) { | 
					
						
							|  |  |  | 	b.add = append(b.add[:0], ls...) // Copy on top of our slice, so we don't retain the input slice.
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 22:59:13 +08:00
										 |  |  | // Return the name/value pairs added so far as a Labels object.
 | 
					
						
							|  |  |  | // Note: if you want them sorted, call Sort() first.
 | 
					
						
							|  |  |  | func (b *ScratchBuilder) Labels() Labels { | 
					
						
							|  |  |  | 	// Copy the slice, so the next use of ScratchBuilder doesn't overwrite.
 | 
					
						
							|  |  |  | 	return append([]Label{}, b.add...) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-04-13 19:07:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Write the newly-built Labels out to ls.
 | 
					
						
							|  |  |  | // Callers must ensure that there are no other references to ls, or any strings fetched from it.
 | 
					
						
							|  |  |  | func (b *ScratchBuilder) Overwrite(ls *Labels) { | 
					
						
							|  |  |  | 	*ls = append((*ls)[:0], b.add...) | 
					
						
							|  |  |  | } |