Initial commit
This commit is contained in:
25
vendor/github.com/HdrHistogram/hdrhistogram-go/.gitignore
generated
vendored
Normal file
25
vendor/github.com/HdrHistogram/hdrhistogram-go/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
.vscode/
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
coverage.txt
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Test example output
|
||||
example.logV2.hlog
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
21
vendor/github.com/HdrHistogram/hdrhistogram-go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/HdrHistogram/hdrhistogram-go/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Coda Hale
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
45
vendor/github.com/HdrHistogram/hdrhistogram-go/Makefile
generated
vendored
Normal file
45
vendor/github.com/HdrHistogram/hdrhistogram-go/Makefile
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Go parameters
|
||||
GOCMD=GO111MODULE=on go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOINSTALL=$(GOCMD) install
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
GOMOD=$(GOCMD) mod
|
||||
GOFMT=$(GOCMD) fmt
|
||||
GODOC=godoc
|
||||
|
||||
.PHONY: all test coverage
|
||||
all: test
|
||||
|
||||
checkfmt:
|
||||
@echo 'Checking gofmt';\
|
||||
bash -c "diff -u <(echo -n) <(gofmt -d .)";\
|
||||
EXIT_CODE=$$?;\
|
||||
if [ "$$EXIT_CODE" -ne 0 ]; then \
|
||||
echo '$@: Go files must be formatted with gofmt'; \
|
||||
fi && \
|
||||
exit $$EXIT_CODE
|
||||
|
||||
lint:
|
||||
$(GOGET) github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
golangci-lint run
|
||||
|
||||
get:
|
||||
$(GOGET) -v ./...
|
||||
|
||||
fmt:
|
||||
$(GOFMT) ./...
|
||||
|
||||
test: get fmt
|
||||
$(GOTEST) -count=1 ./...
|
||||
|
||||
coverage: get test
|
||||
$(GOTEST) -count=1 -race -coverprofile=coverage.txt -covermode=atomic .
|
||||
|
||||
benchmark: get
|
||||
$(GOTEST) -bench=. -benchmem
|
||||
|
||||
godoc:
|
||||
$(GODOC)
|
||||
|
||||
78
vendor/github.com/HdrHistogram/hdrhistogram-go/README.md
generated
vendored
Normal file
78
vendor/github.com/HdrHistogram/hdrhistogram-go/README.md
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
hdrhistogram-go
|
||||
===============
|
||||
|
||||
<a href="https://pkg.go.dev/github.com/HdrHistogram/hdrhistogram-go"><img src="https://pkg.go.dev/badge/github.com/HdrHistogram/hdrhistogram-go" alt="PkgGoDev"></a>
|
||||
[](https://gitter.im/HdrHistogram/HdrHistogram)
|
||||

|
||||
[](https://github.com/HdrHistogram/hdrhistogram-go/blob/master/LICENSE)
|
||||
[](https://codecov.io/gh/HdrHistogram/hdrhistogram-go)
|
||||
|
||||
|
||||
A pure Go implementation of the [HDR Histogram](https://github.com/HdrHistogram/HdrHistogram).
|
||||
|
||||
> A Histogram that supports recording and analyzing sampled data value counts
|
||||
> across a configurable integer value range with configurable value precision
|
||||
> within the range. Value precision is expressed as the number of significant
|
||||
> digits in the value recording, and provides control over value quantization
|
||||
> behavior across the value range and the subsequent value resolution at any
|
||||
> given level.
|
||||
|
||||
For documentation, check [godoc](https://pkg.go.dev/github.com/HdrHistogram/hdrhistogram-go).
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installing
|
||||
Use `go get` to retrieve the hdrhistogram-go implementation and to add it to your `GOPATH` workspace, or project's Go module dependencies.
|
||||
|
||||
```go
|
||||
go get github.com/HdrHistogram/hdrhistogram-go
|
||||
```
|
||||
|
||||
To update the implementation use `go get -u` to retrieve the latest version of the hdrhistogram.
|
||||
|
||||
```go
|
||||
go get github.com/HdrHistogram/hdrhistogram-go
|
||||
```
|
||||
|
||||
|
||||
### Go Modules
|
||||
|
||||
If you are using Go modules, your `go get` will default to the latest tagged
|
||||
release version of the histogram. To get a specific release version, use
|
||||
`@<tag>` in your `go get` command.
|
||||
|
||||
```go
|
||||
go get github.com/HdrHistogram/hdrhistogram-go@v0.9.0
|
||||
```
|
||||
|
||||
To get the latest HdrHistogram/hdrhistogram-go master repository change use `@latest`.
|
||||
|
||||
```go
|
||||
go get github.com/HdrHistogram/hdrhistogram-go@latest
|
||||
```
|
||||
|
||||
### Repo transfer and impact on go dependencies
|
||||
-------------------------------------------
|
||||
This repository has been transferred under the github HdrHstogram umbrella with the help from the orginal
|
||||
author in Sept 2020. The main reasons are to group all implementations under the same roof and to provide more active contribution
|
||||
from the community as the orginal repository was archived several years ago.
|
||||
|
||||
Unfortunately such URL change will break go applications that depend on this library
|
||||
directly or indirectly, as discussed [here](https://github.com/HdrHistogram/hdrhistogram-go/issues/30#issuecomment-696365251).
|
||||
|
||||
The dependency URL should be modified to point to the new repository URL.
|
||||
The tag "v0.9.0" was applied at the point of transfer and will reflect the exact code that was frozen in the
|
||||
original repository.
|
||||
|
||||
If you are using Go modules, you can update to the exact point of transfter using the `@v0.9.0` tag in your `go get` command.
|
||||
|
||||
```
|
||||
go mod edit -replace github.com/codahale/hdrhistogram=github.com/HdrHistogram/hdrhistogram-go@v0.9.0
|
||||
```
|
||||
|
||||
## Credits
|
||||
-------
|
||||
|
||||
Many thanks for Coda Hale for contributing the initial implementation and transfering the repository here.
|
||||
|
||||
782
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr.go
generated
vendored
Normal file
782
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr.go
generated
vendored
Normal file
@@ -0,0 +1,782 @@
|
||||
// Package hdrhistogram provides an implementation of Gil Tene's HDR Histogram
|
||||
// data structure. The HDR Histogram allows for fast and accurate analysis of
|
||||
// the extreme ranges of data with non-normal distributions, like latency.
|
||||
package hdrhistogram
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/bits"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// A Bracket is a part of a cumulative distribution.
|
||||
type Bracket struct {
|
||||
Quantile float64
|
||||
Count, ValueAt int64
|
||||
}
|
||||
|
||||
// A Snapshot is an exported view of a Histogram, useful for serializing them.
|
||||
// A Histogram can be constructed from it by passing it to Import.
|
||||
type Snapshot struct {
|
||||
LowestTrackableValue int64
|
||||
HighestTrackableValue int64
|
||||
SignificantFigures int64
|
||||
Counts []int64
|
||||
}
|
||||
|
||||
// A Histogram is a lossy data structure used to record the distribution of
|
||||
// non-normally distributed data (like latency) with a high degree of accuracy
|
||||
// and a bounded degree of precision.
|
||||
type Histogram struct {
|
||||
lowestDiscernibleValue int64
|
||||
highestTrackableValue int64
|
||||
unitMagnitude int64
|
||||
significantFigures int64
|
||||
subBucketHalfCountMagnitude int32
|
||||
subBucketHalfCount int32
|
||||
subBucketMask int64
|
||||
subBucketCount int32
|
||||
bucketCount int32
|
||||
countsLen int32
|
||||
totalCount int64
|
||||
counts []int64
|
||||
startTimeMs int64
|
||||
endTimeMs int64
|
||||
tag string
|
||||
}
|
||||
|
||||
func (h *Histogram) Tag() string {
|
||||
return h.tag
|
||||
}
|
||||
|
||||
func (h *Histogram) SetTag(tag string) {
|
||||
h.tag = tag
|
||||
}
|
||||
|
||||
func (h *Histogram) EndTimeMs() int64 {
|
||||
return h.endTimeMs
|
||||
}
|
||||
|
||||
func (h *Histogram) SetEndTimeMs(endTimeMs int64) {
|
||||
h.endTimeMs = endTimeMs
|
||||
}
|
||||
|
||||
func (h *Histogram) StartTimeMs() int64 {
|
||||
return h.startTimeMs
|
||||
}
|
||||
|
||||
func (h *Histogram) SetStartTimeMs(startTimeMs int64) {
|
||||
h.startTimeMs = startTimeMs
|
||||
}
|
||||
|
||||
// Construct a Histogram given the Lowest and Highest values to be tracked and a number of significant decimal digits.
|
||||
//
|
||||
// Providing a lowestDiscernibleValue is useful in situations where the units used for the histogram's values are
|
||||
// much smaller that the minimal accuracy required.
|
||||
// E.g. when tracking time values stated in nanosecond units, where the minimal accuracy required is a microsecond,
|
||||
// the proper value for lowestDiscernibleValue would be 1000.
|
||||
//
|
||||
// Note: the numberOfSignificantValueDigits must be [1,5]. If lower than 1 the numberOfSignificantValueDigits will be
|
||||
// forced to 1, and if higher than 5 the numberOfSignificantValueDigits will be forced to 5.
|
||||
func New(lowestDiscernibleValue, highestTrackableValue int64, numberOfSignificantValueDigits int) *Histogram {
|
||||
if numberOfSignificantValueDigits < 1 {
|
||||
numberOfSignificantValueDigits = 1
|
||||
} else if numberOfSignificantValueDigits > 5 {
|
||||
numberOfSignificantValueDigits = 5
|
||||
}
|
||||
if lowestDiscernibleValue < 1 {
|
||||
lowestDiscernibleValue = 1
|
||||
}
|
||||
|
||||
// Given a 3 decimal point accuracy, the expectation is obviously for "+/- 1 unit at 1000". It also means that
|
||||
// it's "ok to be +/- 2 units at 2000". The "tricky" thing is that it is NOT ok to be +/- 2 units at 1999. Only
|
||||
// starting at 2000. So internally, we need to maintain single unit resolution to 2x 10^decimalPoints.
|
||||
largestValueWithSingleUnitResolution := 2 * math.Pow10(numberOfSignificantValueDigits)
|
||||
|
||||
// We need to maintain power-of-two subBucketCount (for clean direct indexing) that is large enough to
|
||||
// provide unit resolution to at least largestValueWithSingleUnitResolution. So figure out
|
||||
// largestValueWithSingleUnitResolution's nearest power-of-two (rounded up), and use that:
|
||||
subBucketCountMagnitude := int32(math.Ceil(math.Log2(float64(largestValueWithSingleUnitResolution))))
|
||||
subBucketHalfCountMagnitude := subBucketCountMagnitude
|
||||
if subBucketHalfCountMagnitude < 1 {
|
||||
subBucketHalfCountMagnitude = 1
|
||||
}
|
||||
subBucketHalfCountMagnitude--
|
||||
|
||||
unitMagnitude := int32(math.Floor(math.Log2(float64(lowestDiscernibleValue))))
|
||||
if unitMagnitude < 0 {
|
||||
unitMagnitude = 0
|
||||
}
|
||||
|
||||
subBucketCount := int32(math.Pow(2, float64(subBucketHalfCountMagnitude)+1))
|
||||
|
||||
subBucketHalfCount := subBucketCount / 2
|
||||
subBucketMask := int64(subBucketCount-1) << uint(unitMagnitude)
|
||||
|
||||
// determine exponent range needed to support the trackable value with no
|
||||
// overflow:
|
||||
smallestUntrackableValue := int64(subBucketCount) << uint(unitMagnitude)
|
||||
bucketsNeeded := getBucketsNeededToCoverValue(smallestUntrackableValue, highestTrackableValue)
|
||||
|
||||
bucketCount := bucketsNeeded
|
||||
countsLen := (bucketCount + 1) * (subBucketCount / 2)
|
||||
|
||||
return &Histogram{
|
||||
lowestDiscernibleValue: lowestDiscernibleValue,
|
||||
highestTrackableValue: highestTrackableValue,
|
||||
unitMagnitude: int64(unitMagnitude),
|
||||
significantFigures: int64(numberOfSignificantValueDigits),
|
||||
subBucketHalfCountMagnitude: subBucketHalfCountMagnitude,
|
||||
subBucketHalfCount: subBucketHalfCount,
|
||||
subBucketMask: subBucketMask,
|
||||
subBucketCount: subBucketCount,
|
||||
bucketCount: bucketCount,
|
||||
countsLen: countsLen,
|
||||
totalCount: 0,
|
||||
counts: make([]int64, countsLen),
|
||||
startTimeMs: 0,
|
||||
endTimeMs: 0,
|
||||
tag: "",
|
||||
}
|
||||
}
|
||||
|
||||
func getBucketsNeededToCoverValue(smallestUntrackableValue int64, maxValue int64) int32 {
|
||||
// always have at least 1 bucket
|
||||
bucketsNeeded := int32(1)
|
||||
for smallestUntrackableValue < maxValue {
|
||||
if smallestUntrackableValue > (math.MaxInt64 / 2) {
|
||||
// next shift will overflow, meaning that bucket could represent values up to ones greater than
|
||||
// math.MaxInt64, so it's the last bucket
|
||||
return bucketsNeeded + 1
|
||||
}
|
||||
smallestUntrackableValue <<= 1
|
||||
bucketsNeeded++
|
||||
}
|
||||
return bucketsNeeded
|
||||
}
|
||||
|
||||
// ByteSize returns an estimate of the amount of memory allocated to the
|
||||
// histogram in bytes.
|
||||
//
|
||||
// N.B.: This does not take into account the overhead for slices, which are
|
||||
// small, constant, and specific to the compiler version.
|
||||
func (h *Histogram) ByteSize() int {
|
||||
return 6*8 + 5*4 + len(h.counts)*8
|
||||
}
|
||||
|
||||
func (h *Histogram) getNormalizingIndexOffset() int32 {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Merge merges the data stored in the given histogram with the receiver,
|
||||
// returning the number of recorded values which had to be dropped.
|
||||
func (h *Histogram) Merge(from *Histogram) (dropped int64) {
|
||||
i := from.rIterator()
|
||||
for i.next() {
|
||||
v := i.valueFromIdx
|
||||
c := i.countAtIdx
|
||||
|
||||
if h.RecordValues(v, c) != nil {
|
||||
dropped += c
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TotalCount returns total number of values recorded.
|
||||
func (h *Histogram) TotalCount() int64 {
|
||||
return h.totalCount
|
||||
}
|
||||
|
||||
// Max returns the approximate maximum recorded value.
|
||||
func (h *Histogram) Max() int64 {
|
||||
var max int64
|
||||
i := h.iterator()
|
||||
for i.next() {
|
||||
if i.countAtIdx != 0 {
|
||||
max = i.highestEquivalentValue
|
||||
}
|
||||
}
|
||||
return h.highestEquivalentValue(max)
|
||||
}
|
||||
|
||||
// Min returns the approximate minimum recorded value.
|
||||
func (h *Histogram) Min() int64 {
|
||||
var min int64
|
||||
i := h.iterator()
|
||||
for i.next() {
|
||||
if i.countAtIdx != 0 && min == 0 {
|
||||
min = i.highestEquivalentValue
|
||||
break
|
||||
}
|
||||
}
|
||||
return h.lowestEquivalentValue(min)
|
||||
}
|
||||
|
||||
// Mean returns the approximate arithmetic mean of the recorded values.
|
||||
func (h *Histogram) Mean() float64 {
|
||||
if h.totalCount == 0 {
|
||||
return 0
|
||||
}
|
||||
var total int64
|
||||
i := h.iterator()
|
||||
for i.next() {
|
||||
if i.countAtIdx != 0 {
|
||||
total += i.countAtIdx * h.medianEquivalentValue(i.valueFromIdx)
|
||||
}
|
||||
}
|
||||
return float64(total) / float64(h.totalCount)
|
||||
}
|
||||
|
||||
// StdDev returns the approximate standard deviation of the recorded values.
|
||||
func (h *Histogram) StdDev() float64 {
|
||||
if h.totalCount == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
mean := h.Mean()
|
||||
geometricDevTotal := 0.0
|
||||
|
||||
i := h.iterator()
|
||||
for i.next() {
|
||||
if i.countAtIdx != 0 {
|
||||
dev := float64(h.medianEquivalentValue(i.valueFromIdx)) - mean
|
||||
geometricDevTotal += (dev * dev) * float64(i.countAtIdx)
|
||||
}
|
||||
}
|
||||
|
||||
return math.Sqrt(geometricDevTotal / float64(h.totalCount))
|
||||
}
|
||||
|
||||
// Reset deletes all recorded values and restores the histogram to its original
|
||||
// state.
|
||||
func (h *Histogram) Reset() {
|
||||
h.totalCount = 0
|
||||
for i := range h.counts {
|
||||
h.counts[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// RecordValue records the given value, returning an error if the value is out
|
||||
// of range.
|
||||
func (h *Histogram) RecordValue(v int64) error {
|
||||
return h.RecordValues(v, 1)
|
||||
}
|
||||
|
||||
// RecordCorrectedValue records the given value, correcting for stalls in the
|
||||
// recording process. This only works for processes which are recording values
|
||||
// at an expected interval (e.g., doing jitter analysis). Processes which are
|
||||
// recording ad-hoc values (e.g., latency for incoming requests) can't take
|
||||
// advantage of this.
|
||||
func (h *Histogram) RecordCorrectedValue(v, expectedInterval int64) error {
|
||||
if err := h.RecordValue(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if expectedInterval <= 0 || v <= expectedInterval {
|
||||
return nil
|
||||
}
|
||||
|
||||
missingValue := v - expectedInterval
|
||||
for missingValue >= expectedInterval {
|
||||
if err := h.RecordValue(missingValue); err != nil {
|
||||
return err
|
||||
}
|
||||
missingValue -= expectedInterval
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordValues records n occurrences of the given value, returning an error if
|
||||
// the value is out of range.
|
||||
func (h *Histogram) RecordValues(v, n int64) error {
|
||||
idx := h.countsIndexFor(v)
|
||||
if idx < 0 || int(h.countsLen) <= idx {
|
||||
return fmt.Errorf("value %d is too large to be recorded", v)
|
||||
}
|
||||
h.setCountAtIndex(idx, n)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Histogram) setCountAtIndex(idx int, n int64) {
|
||||
h.counts[idx] += n
|
||||
h.totalCount += n
|
||||
}
|
||||
|
||||
// ValueAtQuantile returns the largest value that (100% - percentile) of the overall recorded value entries
|
||||
// in the histogram are either larger than or equivalent to.
|
||||
//
|
||||
// The passed quantile must be a float64 value in [0.0 .. 100.0]
|
||||
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
|
||||
//
|
||||
// Returns 0 if no recorded values exist.
|
||||
func (h *Histogram) ValueAtQuantile(q float64) int64 {
|
||||
return h.ValueAtPercentile(q)
|
||||
}
|
||||
|
||||
// ValueAtPercentile returns the largest value that (100% - percentile) of the overall recorded value entries
|
||||
// in the histogram are either larger than or equivalent to.
|
||||
//
|
||||
// The passed percentile must be a float64 value in [0.0 .. 100.0]
|
||||
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
|
||||
//
|
||||
// Returns 0 if no recorded values exist.
|
||||
func (h *Histogram) ValueAtPercentile(percentile float64) int64 {
|
||||
if percentile > 100 {
|
||||
percentile = 100
|
||||
}
|
||||
|
||||
countAtPercentile := int64(((percentile / 100) * float64(h.totalCount)) + 0.5)
|
||||
valueFromIdx := h.getValueFromIdxUpToCount(countAtPercentile)
|
||||
if percentile == 0.0 {
|
||||
return h.lowestEquivalentValue(valueFromIdx)
|
||||
}
|
||||
return h.highestEquivalentValue(valueFromIdx)
|
||||
}
|
||||
|
||||
func (h *Histogram) getValueFromIdxUpToCount(countAtPercentile int64) int64 {
|
||||
var countToIdx int64
|
||||
var valueFromIdx int64
|
||||
var subBucketIdx int32 = -1
|
||||
var bucketIdx int32
|
||||
bucketBaseIdx := h.getBucketBaseIdx(bucketIdx)
|
||||
|
||||
for {
|
||||
if countToIdx >= countAtPercentile {
|
||||
break
|
||||
}
|
||||
// increment bucket
|
||||
subBucketIdx++
|
||||
if subBucketIdx >= h.subBucketCount {
|
||||
subBucketIdx = h.subBucketHalfCount
|
||||
bucketIdx++
|
||||
bucketBaseIdx = h.getBucketBaseIdx(bucketIdx)
|
||||
}
|
||||
|
||||
countToIdx += h.getCountAtIndexGivenBucketBaseIdx(bucketBaseIdx, subBucketIdx)
|
||||
valueFromIdx = int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude)
|
||||
}
|
||||
return valueFromIdx
|
||||
}
|
||||
|
||||
// ValueAtPercentiles, given an slice of percentiles returns a map containing for each passed percentile,
|
||||
// the largest value that (100% - percentile) of the overall recorded value entries
|
||||
// in the histogram are either larger than or equivalent to.
|
||||
//
|
||||
// Each element in the given an slice of percentiles must be a float64 value in [0.0 .. 100.0]
|
||||
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
|
||||
//
|
||||
// Returns a map of 0's if no recorded values exist.
|
||||
func (h *Histogram) ValueAtPercentiles(percentiles []float64) (values map[float64]int64) {
|
||||
sort.Float64s(percentiles)
|
||||
totalQuantilesToCalculate := len(percentiles)
|
||||
values = make(map[float64]int64, totalQuantilesToCalculate)
|
||||
countAtPercentiles := make([]int64, totalQuantilesToCalculate)
|
||||
for i, percentile := range percentiles {
|
||||
if percentile > 100 {
|
||||
percentile = 100
|
||||
}
|
||||
values[percentile] = 0
|
||||
countAtPercentiles[i] = int64(((percentile / 100) * float64(h.totalCount)) + 0.5)
|
||||
}
|
||||
|
||||
total := int64(0)
|
||||
currentQuantileSlicePos := 0
|
||||
i := h.iterator()
|
||||
for currentQuantileSlicePos < totalQuantilesToCalculate && i.nextCountAtIdx(h.totalCount) {
|
||||
total += i.countAtIdx
|
||||
for currentQuantileSlicePos < totalQuantilesToCalculate && total >= countAtPercentiles[currentQuantileSlicePos] {
|
||||
currentPercentile := percentiles[currentQuantileSlicePos]
|
||||
if currentPercentile == 0.0 {
|
||||
values[currentPercentile] = h.lowestEquivalentValue(i.valueFromIdx)
|
||||
} else {
|
||||
values[currentPercentile] = h.highestEquivalentValue(i.valueFromIdx)
|
||||
}
|
||||
currentQuantileSlicePos++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Determine if two values are equivalent with the histogram's resolution.
|
||||
// Where "equivalent" means that value samples recorded for any two
|
||||
// equivalent values are counted in a common total count.
|
||||
func (h *Histogram) ValuesAreEquivalent(value1, value2 int64) (result bool) {
|
||||
result = h.lowestEquivalentValue(value1) == h.lowestEquivalentValue(value2)
|
||||
return
|
||||
}
|
||||
|
||||
// CumulativeDistribution returns an ordered list of brackets of the
|
||||
// distribution of recorded values.
|
||||
func (h *Histogram) CumulativeDistribution() []Bracket {
|
||||
var result []Bracket
|
||||
|
||||
i := h.pIterator(1)
|
||||
for i.next() {
|
||||
result = append(result, Bracket{
|
||||
Quantile: i.percentile,
|
||||
Count: i.countToIdx,
|
||||
ValueAt: i.highestEquivalentValue,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SignificantFigures returns the significant figures used to create the
|
||||
// histogram
|
||||
func (h *Histogram) SignificantFigures() int64 {
|
||||
return h.significantFigures
|
||||
}
|
||||
|
||||
// LowestTrackableValue returns the lower bound on values that will be added
|
||||
// to the histogram
|
||||
func (h *Histogram) LowestTrackableValue() int64 {
|
||||
return h.lowestDiscernibleValue
|
||||
}
|
||||
|
||||
// HighestTrackableValue returns the upper bound on values that will be added
|
||||
// to the histogram
|
||||
func (h *Histogram) HighestTrackableValue() int64 {
|
||||
return h.highestTrackableValue
|
||||
}
|
||||
|
||||
// Histogram bar for plotting
|
||||
type Bar struct {
|
||||
From, To, Count int64
|
||||
}
|
||||
|
||||
// Pretty print as csv for easy plotting
|
||||
func (b Bar) String() string {
|
||||
return fmt.Sprintf("%v, %v, %v\n", b.From, b.To, b.Count)
|
||||
}
|
||||
|
||||
// Distribution returns an ordered list of bars of the
|
||||
// distribution of recorded values, counts can be normalized to a probability
|
||||
func (h *Histogram) Distribution() (result []Bar) {
|
||||
i := h.iterator()
|
||||
for i.next() {
|
||||
result = append(result, Bar{
|
||||
Count: i.countAtIdx,
|
||||
From: h.lowestEquivalentValue(i.valueFromIdx),
|
||||
To: i.highestEquivalentValue,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Equals returns true if the two Histograms are equivalent, false if not.
|
||||
func (h *Histogram) Equals(other *Histogram) bool {
|
||||
switch {
|
||||
case
|
||||
h.lowestDiscernibleValue != other.lowestDiscernibleValue,
|
||||
h.highestTrackableValue != other.highestTrackableValue,
|
||||
h.unitMagnitude != other.unitMagnitude,
|
||||
h.significantFigures != other.significantFigures,
|
||||
h.subBucketHalfCountMagnitude != other.subBucketHalfCountMagnitude,
|
||||
h.subBucketHalfCount != other.subBucketHalfCount,
|
||||
h.subBucketMask != other.subBucketMask,
|
||||
h.subBucketCount != other.subBucketCount,
|
||||
h.bucketCount != other.bucketCount,
|
||||
h.countsLen != other.countsLen,
|
||||
h.totalCount != other.totalCount:
|
||||
return false
|
||||
default:
|
||||
for i, c := range h.counts {
|
||||
if c != other.counts[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Export returns a snapshot view of the Histogram. This can be later passed to
|
||||
// Import to construct a new Histogram with the same state.
|
||||
func (h *Histogram) Export() *Snapshot {
|
||||
return &Snapshot{
|
||||
LowestTrackableValue: h.lowestDiscernibleValue,
|
||||
HighestTrackableValue: h.highestTrackableValue,
|
||||
SignificantFigures: h.significantFigures,
|
||||
Counts: append([]int64(nil), h.counts...), // copy
|
||||
}
|
||||
}
|
||||
|
||||
// Import returns a new Histogram populated from the Snapshot data (which the
|
||||
// caller must stop accessing).
|
||||
func Import(s *Snapshot) *Histogram {
|
||||
h := New(s.LowestTrackableValue, s.HighestTrackableValue, int(s.SignificantFigures))
|
||||
h.counts = s.Counts
|
||||
totalCount := int64(0)
|
||||
for i := int32(0); i < h.countsLen; i++ {
|
||||
countAtIndex := h.counts[i]
|
||||
if countAtIndex > 0 {
|
||||
totalCount += countAtIndex
|
||||
}
|
||||
}
|
||||
h.totalCount = totalCount
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Histogram) iterator() *iterator {
|
||||
return &iterator{
|
||||
h: h,
|
||||
subBucketIdx: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Histogram) rIterator() *rIterator {
|
||||
return &rIterator{
|
||||
iterator: iterator{
|
||||
h: h,
|
||||
subBucketIdx: -1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Histogram) pIterator(ticksPerHalfDistance int32) *pIterator {
|
||||
return &pIterator{
|
||||
iterator: iterator{
|
||||
h: h,
|
||||
subBucketIdx: -1,
|
||||
},
|
||||
ticksPerHalfDistance: ticksPerHalfDistance,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Histogram) sizeOfEquivalentValueRange(v int64) int64 {
|
||||
bucketIdx := h.getBucketIndex(v)
|
||||
return h.sizeOfEquivalentValueRangeGivenBucketIdx(v, bucketIdx)
|
||||
}
|
||||
|
||||
func (h *Histogram) sizeOfEquivalentValueRangeGivenBucketIdx(v int64, bucketIdx int32) int64 {
|
||||
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
||||
adjustedBucket := bucketIdx
|
||||
if subBucketIdx >= h.subBucketCount {
|
||||
adjustedBucket++
|
||||
}
|
||||
return int64(1) << uint(h.unitMagnitude+int64(adjustedBucket))
|
||||
}
|
||||
|
||||
func (h *Histogram) valueFromIndex(bucketIdx, subBucketIdx int32) int64 {
|
||||
return int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude)
|
||||
}
|
||||
|
||||
func (h *Histogram) lowestEquivalentValue(v int64) int64 {
|
||||
bucketIdx := h.getBucketIndex(v)
|
||||
return h.lowestEquivalentValueGivenBucketIdx(v, bucketIdx)
|
||||
}
|
||||
|
||||
func (h *Histogram) lowestEquivalentValueGivenBucketIdx(v int64, bucketIdx int32) int64 {
|
||||
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
||||
return h.valueFromIndex(bucketIdx, subBucketIdx)
|
||||
}
|
||||
|
||||
func (h *Histogram) nextNonEquivalentValue(v int64) int64 {
|
||||
bucketIdx := h.getBucketIndex(v)
|
||||
return h.lowestEquivalentValueGivenBucketIdx(v, bucketIdx) + h.sizeOfEquivalentValueRangeGivenBucketIdx(v, bucketIdx)
|
||||
}
|
||||
|
||||
func (h *Histogram) highestEquivalentValue(v int64) int64 {
|
||||
return h.nextNonEquivalentValue(v) - 1
|
||||
}
|
||||
|
||||
func (h *Histogram) medianEquivalentValue(v int64) int64 {
|
||||
return h.lowestEquivalentValue(v) + (h.sizeOfEquivalentValueRange(v) >> 1)
|
||||
}
|
||||
|
||||
func (h *Histogram) getCountAtIndex(bucketIdx, subBucketIdx int32) int64 {
|
||||
return h.counts[h.countsIndex(bucketIdx, subBucketIdx)]
|
||||
}
|
||||
|
||||
func (h *Histogram) getCountAtIndexGivenBucketBaseIdx(bucketBaseIdx, subBucketIdx int32) int64 {
|
||||
return h.counts[bucketBaseIdx+subBucketIdx-h.subBucketHalfCount]
|
||||
}
|
||||
|
||||
func (h *Histogram) countsIndex(bucketIdx, subBucketIdx int32) int32 {
|
||||
return h.getBucketBaseIdx(bucketIdx) + subBucketIdx - h.subBucketHalfCount
|
||||
}
|
||||
|
||||
func (h *Histogram) getBucketBaseIdx(bucketIdx int32) int32 {
|
||||
return (bucketIdx + 1) << uint(h.subBucketHalfCountMagnitude)
|
||||
}
|
||||
|
||||
// return the lowest (and therefore highest precision) bucket index that can represent the value
|
||||
// Calculates the number of powers of two by which the value is greater than the biggest value that fits in
|
||||
// bucket 0. This is the bucket index since each successive bucket can hold a value 2x greater.
|
||||
func (h *Histogram) getBucketIndex(v int64) int32 {
|
||||
var pow2Ceiling = int64(64 - bits.LeadingZeros64(uint64(v|h.subBucketMask)))
|
||||
return int32(pow2Ceiling - int64(h.unitMagnitude) -
|
||||
int64(h.subBucketHalfCountMagnitude+1))
|
||||
}
|
||||
|
||||
// For bucketIndex 0, this is just value, so it may be anywhere in 0 to subBucketCount.
|
||||
// For other bucketIndex, this will always end up in the top half of subBucketCount: assume that for some bucket
|
||||
// k > 0, this calculation will yield a value in the bottom half of 0 to subBucketCount. Then, because of how
|
||||
// buckets overlap, it would have also been in the top half of bucket k-1, and therefore would have
|
||||
// returned k-1 in getBucketIndex(). Since we would then shift it one fewer bits here, it would be twice as big,
|
||||
// and therefore in the top half of subBucketCount.
|
||||
func (h *Histogram) getSubBucketIdx(v int64, idx int32) int32 {
|
||||
return int32(v >> uint(int64(idx)+int64(h.unitMagnitude)))
|
||||
}
|
||||
|
||||
func (h *Histogram) countsIndexFor(v int64) int {
|
||||
bucketIdx := h.getBucketIndex(v)
|
||||
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
||||
return int(h.countsIndex(bucketIdx, subBucketIdx))
|
||||
}
|
||||
|
||||
func (h *Histogram) getIntegerToDoubleValueConversionRatio() float64 {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
type iterator struct {
|
||||
h *Histogram
|
||||
bucketIdx, subBucketIdx int32
|
||||
countAtIdx, countToIdx, valueFromIdx int64
|
||||
highestEquivalentValue int64
|
||||
}
|
||||
|
||||
// nextCountAtIdx does not update the iterator highestEquivalentValue in order to optimize cpu usage.
|
||||
func (i *iterator) nextCountAtIdx(limit int64) bool {
|
||||
if i.countToIdx >= limit {
|
||||
return false
|
||||
}
|
||||
// increment bucket
|
||||
i.subBucketIdx++
|
||||
if i.subBucketIdx >= i.h.subBucketCount {
|
||||
i.subBucketIdx = i.h.subBucketHalfCount
|
||||
i.bucketIdx++
|
||||
}
|
||||
|
||||
if i.bucketIdx >= i.h.bucketCount {
|
||||
return false
|
||||
}
|
||||
|
||||
i.countAtIdx = i.h.getCountAtIndex(i.bucketIdx, i.subBucketIdx)
|
||||
i.countToIdx += i.countAtIdx
|
||||
i.valueFromIdx = i.h.valueFromIndex(i.bucketIdx, i.subBucketIdx)
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns the next element in the iteration.
|
||||
func (i *iterator) next() bool {
|
||||
if !i.nextCountAtIdx(i.h.totalCount) {
|
||||
return false
|
||||
}
|
||||
i.highestEquivalentValue = i.h.highestEquivalentValue(i.valueFromIdx)
|
||||
return true
|
||||
}
|
||||
|
||||
type rIterator struct {
|
||||
iterator
|
||||
countAddedThisStep int64
|
||||
}
|
||||
|
||||
func (r *rIterator) next() bool {
|
||||
for r.iterator.next() {
|
||||
if r.countAtIdx != 0 {
|
||||
r.countAddedThisStep = r.countAtIdx
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type pIterator struct {
|
||||
iterator
|
||||
seenLastValue bool
|
||||
ticksPerHalfDistance int32
|
||||
percentileToIteratorTo float64
|
||||
percentile float64
|
||||
}
|
||||
|
||||
func (p *pIterator) next() bool {
|
||||
if !(p.countToIdx < p.h.totalCount) {
|
||||
if p.seenLastValue {
|
||||
return false
|
||||
}
|
||||
|
||||
p.seenLastValue = true
|
||||
p.percentile = 100
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if p.subBucketIdx == -1 && !p.iterator.next() {
|
||||
return false
|
||||
}
|
||||
|
||||
var done = false
|
||||
for !done {
|
||||
currentPercentile := (100.0 * float64(p.countToIdx)) / float64(p.h.totalCount)
|
||||
if p.countAtIdx != 0 && p.percentileToIteratorTo <= currentPercentile {
|
||||
p.percentile = p.percentileToIteratorTo
|
||||
halfDistance := math.Trunc(math.Pow(2, math.Trunc(math.Log2(100.0/(100.0-p.percentileToIteratorTo)))+1))
|
||||
percentileReportingTicks := float64(p.ticksPerHalfDistance) * halfDistance
|
||||
p.percentileToIteratorTo += 100.0 / percentileReportingTicks
|
||||
return true
|
||||
}
|
||||
done = !p.iterator.next()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CumulativeDistribution returns an ordered list of brackets of the
|
||||
// distribution of recorded values.
|
||||
func (h *Histogram) CumulativeDistributionWithTicks(ticksPerHalfDistance int32) []Bracket {
|
||||
var result []Bracket
|
||||
|
||||
i := h.pIterator(ticksPerHalfDistance)
|
||||
for i.next() {
|
||||
result = append(result, Bracket{
|
||||
Quantile: i.percentile,
|
||||
Count: i.countToIdx,
|
||||
ValueAt: int64(i.highestEquivalentValue),
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Output the percentiles distribution in a text format
|
||||
func (h *Histogram) PercentilesPrint(writer io.Writer, ticksPerHalfDistance int32, valueScale float64) (outputWriter io.Writer, err error) {
|
||||
outputWriter = writer
|
||||
dist := h.CumulativeDistributionWithTicks(ticksPerHalfDistance)
|
||||
_, err = outputWriter.Write([]byte(" Value\tPercentile\tTotalCount\t1/(1-Percentile)\n\n"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, slice := range dist {
|
||||
percentile := slice.Quantile / 100.0
|
||||
inverted_percentile := 1.0 / (1.0 - percentile)
|
||||
var inverted_percentile_string = fmt.Sprintf("%12.2f", inverted_percentile)
|
||||
// Given that other language implementations display inf (instead of Go's +Inf)
|
||||
// we want to be as close as possible to them
|
||||
if math.IsInf(inverted_percentile, 1) {
|
||||
inverted_percentile_string = fmt.Sprintf("%12s", "inf")
|
||||
}
|
||||
_, err = outputWriter.Write([]byte(fmt.Sprintf("%12.3f %12f %12d %s\n", float64(slice.ValueAt)/valueScale, percentile, slice.Count, inverted_percentile_string)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
footer := fmt.Sprintf("#[Mean = %12.3f, StdDeviation = %12.3f]\n#[Max = %12.3f, Total count = %12d]\n#[Buckets = %12d, SubBuckets = %12d]\n",
|
||||
h.Mean()/valueScale,
|
||||
h.StdDev()/valueScale,
|
||||
float64(h.Max())/valueScale,
|
||||
h.TotalCount(),
|
||||
h.bucketCount,
|
||||
h.subBucketCount,
|
||||
)
|
||||
_, err = outputWriter.Write([]byte(footer))
|
||||
return
|
||||
}
|
||||
261
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr_encoding.go
generated
vendored
Normal file
261
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr_encoding.go
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
// Histograms are encoded using the HdrHistogram V2 format which is based on an adapted ZigZag LEB128 encoding where:
|
||||
// consecutive zero counters are encoded as a negative number representing the count of consecutive zeros
|
||||
// non zero counter values are encoded as a positive number
|
||||
// A typical histogram (2 digits precision 1 usec to 1 day range) can be encoded in less than the typical MTU size of 1500 bytes.
|
||||
package hdrhistogram
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
const (
|
||||
V2EncodingCookieBase int32 = 0x1c849303
|
||||
V2CompressedEncodingCookieBase int32 = 0x1c849304
|
||||
encodingCookie int32 = V2EncodingCookieBase | 0x10
|
||||
compressedEncodingCookie int32 = V2CompressedEncodingCookieBase | 0x10
|
||||
|
||||
ENCODING_HEADER_SIZE = 40
|
||||
)
|
||||
|
||||
// Encode returns a snapshot view of the Histogram.
|
||||
// The snapshot is compact binary representations of the state of the histogram.
|
||||
// They are intended to be used for archival or transmission to other systems for further analysis.
|
||||
func (h *Histogram) Encode(version int32) (buffer []byte, err error) {
|
||||
switch version {
|
||||
case V2CompressedEncodingCookieBase:
|
||||
buffer, err = h.dumpV2CompressedEncoding()
|
||||
default:
|
||||
err = fmt.Errorf("The provided enconding version %d is not supported.", version)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Decode returns a new Histogram by decoding it from a String containing
|
||||
// a base64 encoded compressed histogram representation.
|
||||
func Decode(encoded []byte) (rh *Histogram, err error) {
|
||||
var decoded []byte
|
||||
decoded, err = base64.StdEncoding.DecodeString(string(encoded))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rbuf := bytes.NewBuffer(decoded[0:8])
|
||||
r32 := make([]int32, 2)
|
||||
err = binary.Read(rbuf, binary.BigEndian, &r32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
Cookie := r32[0] & ^0xf0
|
||||
lengthOfCompressedContents := r32[1]
|
||||
if Cookie != V2CompressedEncodingCookieBase {
|
||||
err = fmt.Errorf("Encoding not supported, only V2 is supported. Got %d want %d", Cookie, V2CompressedEncodingCookieBase)
|
||||
return
|
||||
}
|
||||
decodeLengthOfCompressedContents := int32(len(decoded[8:]))
|
||||
if lengthOfCompressedContents > decodeLengthOfCompressedContents {
|
||||
err = fmt.Errorf("The compressed contents buffer is smaller than the lengthOfCompressedContents. Got %d want %d", decodeLengthOfCompressedContents, lengthOfCompressedContents)
|
||||
return
|
||||
}
|
||||
rh, err = decodeCompressedFormat(decoded[8:8+lengthOfCompressedContents], ENCODING_HEADER_SIZE)
|
||||
return
|
||||
}
|
||||
|
||||
// internal method to encode an histogram in V2 Compressed format
|
||||
func (h *Histogram) dumpV2CompressedEncoding() (outBuffer []byte, err error) {
|
||||
// final buffer
|
||||
buf := new(bytes.Buffer)
|
||||
err = binary.Write(buf, binary.BigEndian, compressedEncodingCookie)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
toCompress, err := h.encodeIntoByteBuffer()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uncompressedBytes := toCompress.Bytes()
|
||||
|
||||
var b bytes.Buffer
|
||||
w, err := zlib.NewWriterLevel(&b, zlib.BestCompression)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(uncompressedBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
w.Close()
|
||||
|
||||
// LengthOfCompressedContents
|
||||
compressedContents := b.Bytes()
|
||||
err = binary.Write(buf, binary.BigEndian, int32(len(compressedContents)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Write(buf, binary.BigEndian, compressedContents)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
outBuffer = []byte(base64.StdEncoding.EncodeToString(buf.Bytes()))
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Histogram) encodeIntoByteBuffer() (*bytes.Buffer, error) {
|
||||
|
||||
countsBytes, err := h.fillBufferFromCountsArray()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toCompress := new(bytes.Buffer)
|
||||
err = binary.Write(toCompress, binary.BigEndian, encodingCookie) // 0-3
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(toCompress, binary.BigEndian, int32(len(countsBytes))) // 3-7
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(toCompress, binary.BigEndian, h.getNormalizingIndexOffset()) // 8-11
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(toCompress, binary.BigEndian, int32(h.significantFigures)) // 12-15
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(toCompress, binary.BigEndian, h.lowestDiscernibleValue) // 16-23
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(toCompress, binary.BigEndian, h.highestTrackableValue) // 24-31
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(toCompress, binary.BigEndian, h.getIntegerToDoubleValueConversionRatio()) // 32-39
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(toCompress, binary.BigEndian, countsBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toCompress, err
|
||||
}
|
||||
|
||||
func decodeCompressedFormat(compressedContents []byte, headerSize int) (rh *Histogram, err error) {
|
||||
b := bytes.NewReader(compressedContents)
|
||||
z, err := zlib.NewReader(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer z.Close()
|
||||
decompressedSlice, err := ioutil.ReadAll(z)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
decompressedSliceLen := int32(len(decompressedSlice))
|
||||
cookie, PayloadLength, _, NumberOfSignificantValueDigits, LowestTrackableValue, HighestTrackableValue, _, err := decodeDeCompressedHeaderFormat(decompressedSlice[0:headerSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cookie != V2EncodingCookieBase {
|
||||
err = fmt.Errorf("Encoding not supported, only V2 is supported. Got %d want %d", cookie, V2EncodingCookieBase)
|
||||
return
|
||||
}
|
||||
actualPayloadLen := decompressedSliceLen - int32(headerSize)
|
||||
if PayloadLength != actualPayloadLen {
|
||||
err = fmt.Errorf("PayloadLength should have the same size of the actual payload. Got %d want %d", actualPayloadLen, PayloadLength)
|
||||
return
|
||||
}
|
||||
rh = New(LowestTrackableValue, HighestTrackableValue, int(NumberOfSignificantValueDigits))
|
||||
payload := decompressedSlice[headerSize:]
|
||||
err = fillCountsArrayFromSourceBuffer(payload, rh)
|
||||
return rh, err
|
||||
}
|
||||
|
||||
func fillCountsArrayFromSourceBuffer(payload []byte, rh *Histogram) (err error) {
|
||||
var payloadSlicePos = 0
|
||||
var dstIndex int64 = 0
|
||||
var n int
|
||||
var count int64
|
||||
var zerosCount int64
|
||||
for payloadSlicePos < len(payload) {
|
||||
count, n, err = zig_zag_decode_i64(payload[payloadSlicePos:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
payloadSlicePos += n
|
||||
if count < 0 {
|
||||
zerosCount = -count
|
||||
dstIndex += zerosCount
|
||||
} else {
|
||||
rh.setCountAtIndex(int(dstIndex), count)
|
||||
dstIndex += 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rh *Histogram) fillBufferFromCountsArray() (buffer []byte, err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// V2 encoding format uses a ZigZag LEB128-64b9B encoded long. Positive values are counts,
|
||||
// while negative values indicate a repeat zero counts.
|
||||
var countsLimit int32 = int32(rh.countsIndexFor(rh.Max()) + 1)
|
||||
var srcIndex int32 = 0
|
||||
for srcIndex < countsLimit {
|
||||
count := rh.counts[srcIndex]
|
||||
srcIndex++
|
||||
|
||||
var zeros int64 = 0
|
||||
// check for contiguous zeros
|
||||
if count == 0 {
|
||||
zeros = 1
|
||||
for srcIndex < countsLimit && 0 == rh.counts[srcIndex] {
|
||||
zeros++
|
||||
srcIndex++
|
||||
}
|
||||
}
|
||||
if zeros > 1 {
|
||||
err = binary.Write(buf, binary.BigEndian, zig_zag_encode_i64(-zeros))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = binary.Write(buf, binary.BigEndian, zig_zag_encode_i64(count))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer = buf.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func decodeDeCompressedHeaderFormat(decoded []byte) (Cookie int32, PayloadLength int32, NormalizingIndexOffSet int32, NumberOfSignificantValueDigits int32, LowestTrackableValue int64, HighestTrackableValue int64, IntegerToDoubleConversionRatio float64, err error) {
|
||||
rbuf := bytes.NewBuffer(decoded[0:40])
|
||||
r32 := make([]int32, 4)
|
||||
r64 := make([]int64, 2)
|
||||
err = binary.Read(rbuf, binary.BigEndian, &r32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Read(rbuf, binary.BigEndian, &r64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Read(rbuf, binary.BigEndian, &IntegerToDoubleConversionRatio)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
Cookie = r32[0] & ^0xf0
|
||||
PayloadLength = r32[1]
|
||||
NormalizingIndexOffSet = r32[2]
|
||||
NumberOfSignificantValueDigits = r32[3]
|
||||
LowestTrackableValue = r64[0]
|
||||
HighestTrackableValue = r64[1]
|
||||
return
|
||||
}
|
||||
209
vendor/github.com/HdrHistogram/hdrhistogram-go/log_reader.go
generated
vendored
Normal file
209
vendor/github.com/HdrHistogram/hdrhistogram-go/log_reader.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
package hdrhistogram
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HistogramLogReader struct {
|
||||
log *bufio.Reader
|
||||
startTimeSec float64
|
||||
observedStartTime bool
|
||||
baseTimeSec float64
|
||||
observedBaseTime bool
|
||||
|
||||
// scanner handling state
|
||||
absolute bool
|
||||
rangeStartTimeSec float64
|
||||
rangeEndTimeSec float64
|
||||
observedMax bool
|
||||
rangeObservedMax int64
|
||||
observedMin bool
|
||||
rangeObservedMin int64
|
||||
reStartTime *regexp.Regexp
|
||||
reBaseTime *regexp.Regexp
|
||||
reHistogramInterval *regexp.Regexp
|
||||
}
|
||||
|
||||
func (hlr *HistogramLogReader) ObservedMin() bool {
|
||||
return hlr.observedMin
|
||||
}
|
||||
|
||||
func (hlr *HistogramLogReader) ObservedMax() bool {
|
||||
return hlr.observedMax
|
||||
}
|
||||
|
||||
// Returns the overall observed max limit ( up to the current point ) of the read histograms
|
||||
func (hlr *HistogramLogReader) RangeObservedMax() int64 {
|
||||
return hlr.rangeObservedMax
|
||||
}
|
||||
|
||||
// Returns the overall observed min limit ( up to the current point ) of the read histograms
|
||||
func (hlr *HistogramLogReader) RangeObservedMin() int64 {
|
||||
return hlr.rangeObservedMin
|
||||
}
|
||||
|
||||
func NewHistogramLogReader(log io.Reader) *HistogramLogReader {
|
||||
//# "#[StartTime: %f (seconds since epoch), %s]\n"
|
||||
reStartTime, _ := regexp.Compile(`#\[StartTime: ([\d\.]*)`)
|
||||
|
||||
//# "#[BaseTime: %f (seconds since epoch)]\n"
|
||||
reBaseTime, _ := regexp.Compile(`#\[BaseTime: ([\d\.]*)`)
|
||||
|
||||
//# 0.127,1.007,2.769,HISTFAAAAEV42pNpmSz...
|
||||
//# Tag=A,0.127,1.007,2.769,HISTFAAAAEV42pNpmSz
|
||||
//# "%f,%f,%f,%s\n"
|
||||
reHistogramInterval, _ := regexp.Compile(`([\d\.]*),([\d\.]*),([\d\.]*),(.*)`)
|
||||
//
|
||||
reader := bufio.NewReader(log)
|
||||
|
||||
return &HistogramLogReader{log: reader,
|
||||
startTimeSec: 0.0,
|
||||
observedStartTime: false,
|
||||
baseTimeSec: 0.0,
|
||||
observedBaseTime: false,
|
||||
reStartTime: reStartTime,
|
||||
reBaseTime: reBaseTime,
|
||||
reHistogramInterval: reHistogramInterval,
|
||||
rangeObservedMin: math.MaxInt64,
|
||||
observedMin: false,
|
||||
rangeObservedMax: math.MinInt64,
|
||||
observedMax: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (hlr *HistogramLogReader) NextIntervalHistogram() (histogram *Histogram, err error) {
|
||||
return hlr.NextIntervalHistogramWithRange(0.0, math.MaxFloat64, true)
|
||||
}
|
||||
|
||||
func (hlr *HistogramLogReader) NextIntervalHistogramWithRange(rangeStartTimeSec, rangeEndTimeSec float64, absolute bool) (histogram *Histogram, err error) {
|
||||
hlr.rangeStartTimeSec = rangeStartTimeSec
|
||||
hlr.rangeEndTimeSec = rangeEndTimeSec
|
||||
hlr.absolute = absolute
|
||||
return hlr.decodeNextIntervalHistogram()
|
||||
}
|
||||
|
||||
func (hlr *HistogramLogReader) decodeNextIntervalHistogram() (histogram *Histogram, err error) {
|
||||
var line string
|
||||
var tag string = ""
|
||||
var logTimeStampInSec float64
|
||||
var intervalLengthSec float64
|
||||
for {
|
||||
line, err = hlr.log.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
if line[0] == '#' {
|
||||
matchRes := hlr.reStartTime.FindStringSubmatch(line)
|
||||
if len(matchRes) > 0 {
|
||||
hlr.startTimeSec, err = strconv.ParseFloat(matchRes[1], 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
hlr.observedStartTime = true
|
||||
continue
|
||||
}
|
||||
matchRes = hlr.reBaseTime.FindStringSubmatch(line)
|
||||
if len(matchRes) > 0 {
|
||||
hlr.baseTimeSec, err = strconv.ParseFloat(matchRes[1], 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
hlr.observedBaseTime = true
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "Tag=") {
|
||||
commaPos := strings.Index(line, ",")
|
||||
tag = line[4:commaPos]
|
||||
line = line[commaPos+1:]
|
||||
}
|
||||
|
||||
matchRes := hlr.reHistogramInterval.FindStringSubmatch(line)
|
||||
if len(matchRes) >= 1 {
|
||||
// Decode: startTimestamp, intervalLength, maxTime, histogramPayload
|
||||
// Timestamp is expected to be in seconds
|
||||
logTimeStampInSec, err = strconv.ParseFloat(matchRes[1], 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
intervalLengthSec, err = strconv.ParseFloat(matchRes[2], 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cpayload := matchRes[4]
|
||||
|
||||
// No explicit start time noted. Use 1st observed time:
|
||||
|
||||
if !hlr.observedStartTime {
|
||||
hlr.startTimeSec = logTimeStampInSec
|
||||
hlr.observedStartTime = true
|
||||
}
|
||||
|
||||
// No explicit base time noted.
|
||||
// Deduce from 1st observed time (compared to start time):
|
||||
if !hlr.observedBaseTime {
|
||||
// Criteria Note: if log timestamp is more than a year in
|
||||
// the past (compared to StartTime),
|
||||
// we assume that timestamps in the log are not absolute
|
||||
if logTimeStampInSec < (hlr.startTimeSec - (365 * 24 * 3600.0)) {
|
||||
hlr.baseTimeSec = hlr.startTimeSec
|
||||
} else {
|
||||
hlr.baseTimeSec = 0.0
|
||||
}
|
||||
hlr.observedBaseTime = true
|
||||
}
|
||||
|
||||
absoluteStartTimeStampSec := logTimeStampInSec + hlr.baseTimeSec
|
||||
offsetStartTimeStampSec := absoluteStartTimeStampSec + hlr.startTimeSec
|
||||
|
||||
// Timestamp length is expect to be in seconds
|
||||
absoluteEndTimeStampSec := absoluteStartTimeStampSec + intervalLengthSec
|
||||
|
||||
var startTimeStampToCheckRangeOn float64
|
||||
if hlr.absolute {
|
||||
startTimeStampToCheckRangeOn = absoluteStartTimeStampSec
|
||||
} else {
|
||||
startTimeStampToCheckRangeOn = offsetStartTimeStampSec
|
||||
}
|
||||
|
||||
if startTimeStampToCheckRangeOn < hlr.rangeStartTimeSec {
|
||||
continue
|
||||
}
|
||||
|
||||
if startTimeStampToCheckRangeOn > hlr.rangeEndTimeSec {
|
||||
return
|
||||
}
|
||||
histogram, err = Decode([]byte(cpayload))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if histogram.Max() > hlr.rangeObservedMax {
|
||||
hlr.rangeObservedMax = histogram.Max()
|
||||
}
|
||||
|
||||
if histogram.Min() < hlr.rangeObservedMin {
|
||||
hlr.rangeObservedMin = histogram.Min()
|
||||
}
|
||||
|
||||
histogram.SetStartTimeMs(int64(absoluteStartTimeStampSec * 1000.0))
|
||||
histogram.SetEndTimeMs(int64(absoluteEndTimeStampSec * 1000.0))
|
||||
if tag != "" {
|
||||
histogram.SetTag(tag)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
141
vendor/github.com/HdrHistogram/hdrhistogram-go/log_writer.go
generated
vendored
Normal file
141
vendor/github.com/HdrHistogram/hdrhistogram-go/log_writer.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
//The log format encodes into a single file, multiple histograms with optional shared meta data.
|
||||
package hdrhistogram
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
const HISTOGRAM_LOG_FORMAT_VERSION = "1.3"
|
||||
const MsToNsRatio float64 = 1000000.0
|
||||
|
||||
type HistogramLogOptions struct {
|
||||
startTimeStampSec float64
|
||||
endTimeStampSec float64
|
||||
maxValueUnitRatio float64
|
||||
}
|
||||
|
||||
func DefaultHistogramLogOptions() *HistogramLogOptions {
|
||||
return &HistogramLogOptions{0, 0, MsToNsRatio}
|
||||
}
|
||||
|
||||
type HistogramLogWriter struct {
|
||||
baseTime int64
|
||||
log io.Writer
|
||||
}
|
||||
|
||||
// Return the current base time offset
|
||||
func (lw *HistogramLogWriter) BaseTime() int64 {
|
||||
return lw.baseTime
|
||||
}
|
||||
|
||||
// Set a base time to subtract from supplied histogram start/end timestamps when
|
||||
// logging based on histogram timestamps.
|
||||
// baseTime is expected to be in msec since the epoch, as histogram start/end times
|
||||
// are typically stamped with absolute times in msec since the epoch.
|
||||
func (lw *HistogramLogWriter) SetBaseTime(baseTime int64) {
|
||||
lw.baseTime = baseTime
|
||||
}
|
||||
|
||||
func NewHistogramLogWriter(log io.Writer) *HistogramLogWriter {
|
||||
return &HistogramLogWriter{baseTime: 0, log: log}
|
||||
}
|
||||
|
||||
// Output an interval histogram, using the start/end timestamp indicated in the histogram, and the [optional] tag associated with the histogram.
|
||||
// The histogram start and end timestamps are assumed to be in msec units
|
||||
//
|
||||
// By convention, histogram start/end time are generally stamped with absolute times in msec
|
||||
// since the epoch. For logging with absolute time stamps, the base time would remain zero ( default ).
|
||||
// For logging with relative time stamps (time since a start point), the base time should be set with SetBaseTime(baseTime int64)
|
||||
//
|
||||
// The max value in the histogram will be reported scaled down by a default maxValueUnitRatio of 1000000.0 (which is the msec : nsec ratio).
|
||||
// If you need to specify a different start/end timestamp or a different maxValueUnitRatio you should use OutputIntervalHistogramWithLogOptions(histogram *Histogram, logOptions *HistogramLogOptions)
|
||||
func (lw *HistogramLogWriter) OutputIntervalHistogram(histogram *Histogram) (err error) {
|
||||
return lw.OutputIntervalHistogramWithLogOptions(histogram, nil)
|
||||
}
|
||||
|
||||
// Output an interval histogram, with the given timestamp information and the [optional] tag associated with the histogram
|
||||
//
|
||||
// If you specify non-nil logOptions, and non-zero start timestamp, the the specified timestamp information will be used, and the start timestamp information in the actual histogram will be ignored.
|
||||
// If you specify non-nil logOptions, and non-zero start timestamp, the the specified timestamp information will be used, and the end timestamp information in the actual histogram will be ignored.
|
||||
// If you specify non-nil logOptions, The max value reported with the interval line will be scaled by the given maxValueUnitRatio,
|
||||
// otherwise a default maxValueUnitRatio of 1,000,000 (which is the msec : nsec ratio) will be used.
|
||||
//
|
||||
// By convention, histogram start/end time are generally stamped with absolute times in msec
|
||||
// since the epoch. For logging with absolute time stamps, the base time would remain zero ( default ).
|
||||
// For logging with relative time stamps (time since a start point), the base time should be set with SetBaseTime(baseTime int64)
|
||||
func (lw *HistogramLogWriter) OutputIntervalHistogramWithLogOptions(histogram *Histogram, logOptions *HistogramLogOptions) (err error) {
|
||||
tag := histogram.Tag()
|
||||
var match bool
|
||||
tagStr := ""
|
||||
if tag != "" {
|
||||
match, err = regexp.MatchString(".[, \\r\\n].", tag)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if match {
|
||||
err = fmt.Errorf("Tag string cannot contain commas, spaces, or line breaks. Used tag: %s", tag)
|
||||
return
|
||||
}
|
||||
tagStr = fmt.Sprintf("Tag=%s,", tag)
|
||||
}
|
||||
var usedStartTime float64 = float64(histogram.StartTimeMs())
|
||||
var usedEndTime float64 = float64(histogram.EndTimeMs())
|
||||
var maxValueUnitRatio float64 = MsToNsRatio
|
||||
if logOptions != nil {
|
||||
if logOptions.startTimeStampSec != 0 {
|
||||
usedStartTime = logOptions.startTimeStampSec
|
||||
}
|
||||
if logOptions.endTimeStampSec != 0 {
|
||||
usedEndTime = logOptions.endTimeStampSec
|
||||
}
|
||||
maxValueUnitRatio = logOptions.maxValueUnitRatio
|
||||
}
|
||||
startTime := usedStartTime - float64(lw.baseTime)/1000.0
|
||||
endTime := usedEndTime - float64(lw.baseTime)/1000.0
|
||||
maxValueAsDouble := float64(histogram.Max()) / maxValueUnitRatio
|
||||
cpayload, err := histogram.Encode(V2CompressedEncodingCookieBase)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = lw.log.Write([]byte(fmt.Sprintf("%s%f,%f,%f,%s\n", tagStr, startTime, endTime, maxValueAsDouble, string(cpayload))))
|
||||
return
|
||||
}
|
||||
|
||||
// Log a start time in the log.
|
||||
// Start time is represented as seconds since epoch with up to 3 decimal places. Line starts with the leading text '#[StartTime:'
|
||||
func (lw *HistogramLogWriter) OutputStartTime(start_time_msec int64) (err error) {
|
||||
secs := start_time_msec / 1000
|
||||
iso_str := time.Unix(secs, start_time_msec%int64(1000)*int64(1000000000)).Format(time.RFC3339)
|
||||
_, err = lw.log.Write([]byte(fmt.Sprintf("#[StartTime: %d (seconds since epoch), %s]\n", secs, iso_str)))
|
||||
return
|
||||
}
|
||||
|
||||
// Log a base time in the log.
|
||||
// Base time is represented as seconds since epoch with up to 3 decimal places. Line starts with the leading text '#[BaseTime:'
|
||||
func (lw *HistogramLogWriter) OutputBaseTime(base_time_msec int64) (err error) {
|
||||
secs := base_time_msec / 1000
|
||||
_, err = lw.log.Write([]byte(fmt.Sprintf("#[Basetime: %d (seconds since epoch)]\n", secs)))
|
||||
return
|
||||
}
|
||||
|
||||
// Log a comment to the log.
|
||||
// A comment is any line that leads with '#' that is not matched by the BaseTime or StartTime formats. Comments are ignored when parsed.
|
||||
func (lw *HistogramLogWriter) OutputComment(comment string) (err error) {
|
||||
_, err = lw.log.Write([]byte(fmt.Sprintf("#%s\n", comment)))
|
||||
return
|
||||
}
|
||||
|
||||
// Output a legend line to the log.
|
||||
// Human readable column headers. Ignored when parsed.
|
||||
func (lw *HistogramLogWriter) OutputLegend() (err error) {
|
||||
_, err = lw.log.Write([]byte("\"StartTimestamp\",\"Interval_Length\",\"Interval_Max\",\"Interval_Compressed_Histogram\"\n"))
|
||||
return
|
||||
}
|
||||
|
||||
// Output a log format version to the log.
|
||||
func (lw *HistogramLogWriter) OutputLogFormatVersion() (err error) {
|
||||
return lw.OutputComment(fmt.Sprintf("[Histogram log format version %s]", HISTOGRAM_LOG_FORMAT_VERSION))
|
||||
}
|
||||
45
vendor/github.com/HdrHistogram/hdrhistogram-go/window.go
generated
vendored
Normal file
45
vendor/github.com/HdrHistogram/hdrhistogram-go/window.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package hdrhistogram
|
||||
|
||||
// A WindowedHistogram combines histograms to provide windowed statistics.
|
||||
type WindowedHistogram struct {
|
||||
idx int
|
||||
h []Histogram
|
||||
m *Histogram
|
||||
|
||||
Current *Histogram
|
||||
}
|
||||
|
||||
// NewWindowed creates a new WindowedHistogram with N underlying histograms with
|
||||
// the given parameters.
|
||||
func NewWindowed(n int, minValue, maxValue int64, sigfigs int) *WindowedHistogram {
|
||||
w := WindowedHistogram{
|
||||
idx: -1,
|
||||
h: make([]Histogram, n),
|
||||
m: New(minValue, maxValue, sigfigs),
|
||||
}
|
||||
|
||||
for i := range w.h {
|
||||
w.h[i] = *New(minValue, maxValue, sigfigs)
|
||||
}
|
||||
w.Rotate()
|
||||
|
||||
return &w
|
||||
}
|
||||
|
||||
// Merge returns a histogram which includes the recorded values from all the
|
||||
// sections of the window.
|
||||
func (w *WindowedHistogram) Merge() *Histogram {
|
||||
w.m.Reset()
|
||||
for _, h := range w.h {
|
||||
w.m.Merge(&h)
|
||||
}
|
||||
return w.m
|
||||
}
|
||||
|
||||
// Rotate resets the oldest histogram and rotates it to be used as the current
|
||||
// histogram.
|
||||
func (w *WindowedHistogram) Rotate() {
|
||||
w.idx++
|
||||
w.Current = &w.h[w.idx%len(w.h)]
|
||||
w.Current.Reset()
|
||||
}
|
||||
133
vendor/github.com/HdrHistogram/hdrhistogram-go/zigzag.go
generated
vendored
Normal file
133
vendor/github.com/HdrHistogram/hdrhistogram-go/zigzag.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
package hdrhistogram
|
||||
|
||||
import "fmt"
|
||||
|
||||
const truncatedErrStr = "Truncated compressed histogram decode. Expected minimum length of %d bytes and got %d."
|
||||
|
||||
// Read an LEB128 ZigZag encoded long value from the given buffer
|
||||
func zig_zag_decode_i64(buf []byte) (signedValue int64, n int, err error) {
|
||||
buflen := len(buf)
|
||||
if buflen < 1 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
var value = uint64(buf[0]) & 0x7f
|
||||
n = 1
|
||||
if (buf[0] & 0x80) != 0 {
|
||||
if buflen < 2 {
|
||||
err = fmt.Errorf(truncatedErrStr, 2, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[1]) & 0x7f << 7
|
||||
n = 2
|
||||
if (buf[1] & 0x80) != 0 {
|
||||
if buflen < 3 {
|
||||
err = fmt.Errorf(truncatedErrStr, 3, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[2]) & 0x7f << 14
|
||||
n = 3
|
||||
if (buf[2] & 0x80) != 0 {
|
||||
if buflen < 4 {
|
||||
err = fmt.Errorf(truncatedErrStr, 4, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[3]) & 0x7f << 21
|
||||
n = 4
|
||||
if (buf[3] & 0x80) != 0 {
|
||||
if buflen < 5 {
|
||||
err = fmt.Errorf(truncatedErrStr, 5, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[4]) & 0x7f << 28
|
||||
n = 5
|
||||
if (buf[4] & 0x80) != 0 {
|
||||
if buflen < 6 {
|
||||
err = fmt.Errorf(truncatedErrStr, 6, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[5]) & 0x7f << 35
|
||||
n = 6
|
||||
if (buf[5] & 0x80) != 0 {
|
||||
if buflen < 7 {
|
||||
err = fmt.Errorf(truncatedErrStr, 7, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[6]) & 0x7f << 42
|
||||
n = 7
|
||||
if (buf[6] & 0x80) != 0 {
|
||||
if buflen < 8 {
|
||||
err = fmt.Errorf(truncatedErrStr, 8, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[7]) & 0x7f << 49
|
||||
n = 8
|
||||
if (buf[7] & 0x80) != 0 {
|
||||
if buflen < 9 {
|
||||
err = fmt.Errorf(truncatedErrStr, 9, buflen)
|
||||
return
|
||||
}
|
||||
value |= uint64(buf[8]) << 56
|
||||
n = 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
signedValue = int64((value >> 1) ^ -(value & 1))
|
||||
return
|
||||
}
|
||||
|
||||
// Writes a int64_t value to the given buffer in LEB128 ZigZag encoded format
|
||||
// ZigZag encoding maps signed integers to unsigned integers so that numbers with a small
|
||||
// absolute value (for instance, -1) have a small varint encoded value too.
|
||||
// It does this in a way that "zig-zags" back and forth through the positive and negative integers,
|
||||
// so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so on.
|
||||
func zig_zag_encode_i64(signedValue int64) (buffer []byte) {
|
||||
buffer = make([]byte, 0)
|
||||
var value = uint64((signedValue << 1) ^ (signedValue >> 63))
|
||||
if value>>7 == 0 {
|
||||
buffer = append(buffer, byte(value))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value&0x7F)|0x80))
|
||||
if value>>14 == 0 {
|
||||
buffer = append(buffer, byte(value>>7))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value>>7)|0x80))
|
||||
if value>>21 == 0 {
|
||||
buffer = append(buffer, byte(value>>14))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value>>14)|0x80))
|
||||
if value>>28 == 0 {
|
||||
buffer = append(buffer, byte(value>>21))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value>>21)|0x80))
|
||||
if value>>35 == 0 {
|
||||
buffer = append(buffer, byte(value>>28))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value>>28)|0x80))
|
||||
if value>>42 == 0 {
|
||||
buffer = append(buffer, byte(value>>35))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value>>35)|0x80))
|
||||
if value>>49 == 0 {
|
||||
buffer = append(buffer, byte(value>>42))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value>>42)|0x80))
|
||||
if value>>56 == 0 {
|
||||
buffer = append(buffer, byte(value>>49))
|
||||
} else {
|
||||
buffer = append(buffer, byte((value>>49)|0x80))
|
||||
buffer = append(buffer, byte(value>>56))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user