diff --git a/prometheus/desc.go b/prometheus/desc.go index fcde784..ee02d9b 100644 --- a/prometheus/desc.go +++ b/prometheus/desc.go @@ -1,10 +1,8 @@ package prometheus import ( - "bytes" "errors" "fmt" - "hash/fnv" "regexp" "sort" "strings" @@ -131,31 +129,24 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * d.err = errors.New("duplicate label names") return d } - h := fnv.New64a() - var b bytes.Buffer // To copy string contents into, avoiding []byte allocations. + vh := hashNew() for _, val := range labelValues { - b.Reset() - b.WriteString(val) - b.WriteByte(separatorByte) - h.Write(b.Bytes()) + vh = hashAdd(vh, val) + vh = hashAddByte(vh, separatorByte) } - d.id = h.Sum64() + d.id = vh // Sort labelNames so that order doesn't matter for the hash. sort.Strings(labelNames) // Now hash together (in this order) the help string and the sorted // label names. - h.Reset() - b.Reset() - b.WriteString(help) - b.WriteByte(separatorByte) - h.Write(b.Bytes()) + lh := hashNew() + lh = hashAdd(lh, help) + lh = hashAddByte(lh, separatorByte) for _, labelName := range labelNames { - b.Reset() - b.WriteString(labelName) - b.WriteByte(separatorByte) - h.Write(b.Bytes()) + lh = hashAdd(lh, labelName) + lh = hashAddByte(lh, separatorByte) } - d.dimHash = h.Sum64() + d.dimHash = lh d.constLabelPairs = make([]*dto.LabelPair, 0, len(constLabels)) for n, v := range constLabels { diff --git a/prometheus/fnv.go b/prometheus/fnv.go index 59ecedc..e3b67df 100644 --- a/prometheus/fnv.go +++ b/prometheus/fnv.go @@ -20,3 +20,10 @@ func hashAdd(h uint64, s string) uint64 { } return h } + +// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash. +func hashAddByte(h uint64, b byte) uint64 { + h ^= uint64(b) + h *= prime64 + return h +} diff --git a/prometheus/registry.go b/prometheus/registry.go index 7dbbe84..883be83 100644 --- a/prometheus/registry.go +++ b/prometheus/registry.go @@ -24,7 +24,6 @@ import ( "compress/gzip" "errors" "fmt" - "hash/fnv" "io" "net/http" "net/url" @@ -528,30 +527,25 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d } // Is the metric unique (i.e. no other metric with the same name and the same label values)? - h := fnv.New64a() - var buf bytes.Buffer - buf.WriteString(metricFamily.GetName()) - buf.WriteByte(separatorByte) - h.Write(buf.Bytes()) + h := hashNew() + h = hashAdd(h, metricFamily.GetName()) + h = hashAddByte(h, separatorByte) // Make sure label pairs are sorted. We depend on it for the consistency // check. Label pairs must be sorted by contract. But the point of this // method is to check for contract violations. So we better do the sort // now. sort.Sort(LabelPairSorter(dtoMetric.Label)) for _, lp := range dtoMetric.Label { - buf.Reset() - buf.WriteString(lp.GetValue()) - buf.WriteByte(separatorByte) - h.Write(buf.Bytes()) + h = hashAdd(h, lp.GetValue()) + h = hashAddByte(h, separatorByte) } - metricHash := h.Sum64() - if _, exists := metricHashes[metricHash]; exists { + if _, exists := metricHashes[h]; exists { return fmt.Errorf( "collected metric %s %s was collected before with the same name and label values", metricFamily.GetName(), dtoMetric, ) } - metricHashes[metricHash] = struct{}{} + metricHashes[h] = struct{}{} if desc == nil { return nil // Nothing left to check if we have no desc.