diff options
| author | Will Hawkins <[email protected]> | 2023-07-14 10:59:05 -0400 | 
|---|---|---|
| committer | Will Hawkins <[email protected]> | 2023-07-14 10:59:05 -0400 | 
| commit | f2b7e719543408650fef7e3290f77962654453a9 (patch) | |
| tree | 6b9ad557712d19a2362c0055908a8c25b58cafbe | |
| parent | df37c3e0d572ff3b4b4de3e9919402e8e0ccf971 (diff) | |
[Bugfix]: Out of range percentile calculations
It was possible for a user of percentile-calculation functions to
request a percentile that caused the underlying array of values to be
accessed out of bounds. This patch fixes that error.
Signed-off-by: Will Hawkins <[email protected]>
| -rw-r--r-- | series/series.go | 3 | ||||
| -rw-r--r-- | series/series_test.go | 37 | ||||
| -rw-r--r-- | utilities/math.go | 9 | 
3 files changed, 44 insertions, 5 deletions
diff --git a/series/series.go b/series/series.go index 43d9809..6b9af1f 100644 --- a/series/series.go +++ b/series/series.go @@ -127,6 +127,9 @@ func (wsi *windowSeriesWindowOnlyImpl[Data, Bucket]) previousIndex(currentIndex  func (wsi *windowSeriesWindowOnlyImpl[Data, Bucket]) toArray() []utilities.Optional[Data] {  	result := make([]utilities.Optional[Data], wsi.windowSize) +	if wsi.empty { +		return result +	}  	iterator := wsi.latestIndex  	parallelIterator := 0  	for { diff --git a/series/series_test.go b/series/series_test.go index 5d2ab57..3ed752d 100644 --- a/series/series_test.go +++ b/series/series_test.go @@ -95,6 +95,25 @@ func Test_ForeverValues(test *testing.T) {  	}  } +func Test_WindowOnly_no_values_getvalues(test *testing.T) { +	expectedLen := 5 +	series := newWindowSeriesWindowOnlyImpl[float64, int](5) +	result := series.GetValues() +	allZeros := true +	for _, v := range result { +		if utilities.IsSome(v) { +			allZeros = false +			break +		} +	} +	if len(result) != expectedLen { +		test.Fatalf("GetValues of empty window-only series returned list with incorrect size.") +	} +	if !allZeros { +		test.Fatalf("GetValues of empty window-only series returned list with some values.") +	} +} +  func Test_WindowOnlySequentialIncreasesAlwaysLessThan(test *testing.T) {  	series := newWindowSeriesWindowOnlyImpl[float64, int](10)  	previous := float64(1.0) @@ -154,11 +173,18 @@ func Test_Forever_degenerate_percentile_too_high(test *testing.T) {  func Test_Forever_degenerate_percentile_too_low(test *testing.T) {  	series := newWindowSeriesForeverImpl[int, int]() -	if complete, result := Percentile[int, int](series, -1); !complete || result != 0.0 { +	if complete, result := Percentile[int, int](series, 0); !complete || result != 0.0 {  		test.Fatalf("(infinite) Series percentile of -1 failed.")  	}  } +func Test_Forever_degenerate_percentile_no_values(test *testing.T) { +	series := newWindowSeriesForeverImpl[int, int]() +	if complete, p := Percentile[int, int](series, 50); !complete || p != 0 { +		test.Fatalf("empty series percentile of 50 failed.") +	} +} +  ///////////  func Test_Forever90_percentile(test *testing.T) { @@ -557,11 +583,18 @@ func Test_WindowOnly_degenerate_percentile_too_high(test *testing.T) {  func Test_WindowOnly_degenerate_percentile_too_low(test *testing.T) {  	series := newWindowSeriesWindowOnlyImpl[int, int](21) -	if complete, p := Percentile[int, int](series, -1); complete != false || p != 0 { +	if complete, p := Percentile[int, int](series, 0); complete != false || p != 0 {  		test.Fatalf("Series percentile of -1 failed.")  	}  } +func Test_WindowOnly_degenerate_percentile_no_values(test *testing.T) { +	series := newWindowSeriesWindowOnlyImpl[int, int](0) +	if complete, p := Percentile[int, int](series, 50); !complete || p != 0 { +		test.Fatalf("empty series percentile of 50 failed.") +	} +} +  func Test_WindowOnly90_percentile(test *testing.T) {  	var expected int = 10  	series := newWindowSeriesWindowOnlyImpl[int, int](10) diff --git a/utilities/math.go b/utilities/math.go index 1ecca61..96b9f10 100644 --- a/utilities/math.go +++ b/utilities/math.go @@ -36,15 +36,18 @@ func CalculateAverage[T Number](elements []T) float64 {  func CalculatePercentile[T Number](  	elements []T, -	p int, +	p uint,  ) (result T) {  	result = T(0) -	if p < 0 || p > 100 { +	if p < 1 || p > 100 {  		return  	}  	sort.Slice(elements, func(l int, r int) bool { return elements[l] < elements[r] }) -	pindex := int64((float64(p) / float64(100)) * float64(len(elements))) +	pindex := int((float64(p) / float64(100)) * float64(len(elements))) +	if pindex >= len(elements) { +		return +	}  	result = elements[pindex]  	return  }  | 
