Skip to content

Commit 81ecd4b

Browse files
authored
Merge pull request #34 from raintank/eat-dots
add function to eat dots
2 parents ce78547 + 63b0064 commit 81ecd4b

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

metric.go

+59
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,65 @@ func SanitizeNameAsTagValue(name string) string {
244244
return ""
245245
}
246246

247+
// EatDots removes multiple consecutive, leading, and trailing dots
248+
// from name. If the provided name is only dots, it will return an
249+
// empty string
250+
// The vast majority of names will not need to be modified,
251+
// so we optimize for that case. This function only requires
252+
// allocations if the name does need to be modified.
253+
func EatDots(name string) string {
254+
if len(name) == 0 {
255+
return ""
256+
}
257+
258+
dotsToRemove := 0
259+
if name[0] == '.' {
260+
dotsToRemove++
261+
}
262+
for i := 1; i < len(name); i++ {
263+
if name[i] == '.' {
264+
if name[i-1] == '.' {
265+
dotsToRemove++
266+
}
267+
if i == len(name)-1 {
268+
dotsToRemove++
269+
}
270+
}
271+
}
272+
273+
// the majority of cases will return here
274+
if dotsToRemove == 0 {
275+
return name
276+
}
277+
278+
if dotsToRemove >= len(name) {
279+
return ""
280+
}
281+
282+
newName := make([]byte, len(name)-dotsToRemove)
283+
j := 0
284+
sawDot := false
285+
for i := 0; i < len(name); i++ {
286+
if name[i] == '.' {
287+
if j > 0 {
288+
sawDot = true
289+
}
290+
continue
291+
}
292+
293+
if sawDot {
294+
newName[j] = '.'
295+
sawDot = false
296+
j++
297+
}
298+
299+
newName[j] = name[i]
300+
j++
301+
}
302+
303+
return string(newName)
304+
}
305+
247306
// ValidateTags returns whether all tags are in a valid format.
248307
// a valid format is anything that looks like key=value,
249308
// the length of key and value must be >0 and both cannot contain

metricpoint_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@ import (
66
"testing"
77
)
88

9+
var testMetrics = []string{
10+
"some.id.of.a.metric",
11+
".some.id.of.a.metric.",
12+
"..some.id.of.a.metric",
13+
"some.id....of.a.metric",
14+
"some.id.of....a..metric",
15+
"some...id.of.a.metric..",
16+
".",
17+
"...",
18+
"..a.",
19+
"...a",
20+
"a...",
21+
}
22+
23+
var expectedResults = []string{
24+
"some.id.of.a.metric",
25+
"some.id.of.a.metric",
26+
"some.id.of.a.metric",
27+
"some.id.of.a.metric",
28+
"some.id.of.a.metric",
29+
"some.id.of.a.metric",
30+
"",
31+
"",
32+
"a",
33+
"a",
34+
"a",
35+
}
36+
937
func TestMetricPointMarshal(t *testing.T) {
1038
tests := []MetricPoint{
1139
{
@@ -169,3 +197,28 @@ func TestMetricPointMarshalMultiple(t *testing.T) {
169197
}
170198
}
171199
}
200+
201+
func TestEatDots(t *testing.T) {
202+
results := make([]string, 0, len(testMetrics))
203+
204+
for _, s := range testMetrics {
205+
results = append(results, EatDots(s))
206+
}
207+
208+
for i := range results {
209+
if results[i] != expectedResults[i] {
210+
t.Errorf("Result '%s' does not match expected '%s'", results[i], expectedResults[i])
211+
}
212+
}
213+
}
214+
215+
var globalString string
216+
217+
func BenchmarkEatDots(b *testing.B) {
218+
b.ReportAllocs()
219+
for i := 0; i < b.N; i++ {
220+
for _, s := range testMetrics {
221+
globalString = EatDots(s)
222+
}
223+
}
224+
}

0 commit comments

Comments
 (0)