summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Hawkins <[email protected]>2023-07-14 10:59:05 -0400
committerWill Hawkins <[email protected]>2023-07-14 10:59:05 -0400
commitf2b7e719543408650fef7e3290f77962654453a9 (patch)
tree6b9ad557712d19a2362c0055908a8c25b58cafbe
parentdf37c3e0d572ff3b4b4de3e9919402e8e0ccf971 (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.go3
-rw-r--r--series/series_test.go37
-rw-r--r--utilities/math.go9
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
}