summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/librdkafka-2.1.0/src/rdhdrhistogram.c
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/librdkafka-2.1.0/src/rdhdrhistogram.c')
-rw-r--r--fluent-bit/lib/librdkafka-2.1.0/src/rdhdrhistogram.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/fluent-bit/lib/librdkafka-2.1.0/src/rdhdrhistogram.c b/fluent-bit/lib/librdkafka-2.1.0/src/rdhdrhistogram.c
new file mode 100644
index 000000000..3f2b6758b
--- /dev/null
+++ b/fluent-bit/lib/librdkafka-2.1.0/src/rdhdrhistogram.c
@@ -0,0 +1,721 @@
+/*
+ * This license covers this C port of
+ * Coda Hale's Golang HdrHistogram https://github.com/codahale/hdrhistogram
+ * at revision 3a0bb77429bd3a61596f5e8a3172445844342120
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * 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.
+ */
+
+/*
+ * librdkafka - Apache Kafka C library
+ *
+ * Copyright (c) 2018, Magnus Edenhill
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Minimal C Hdr_Histogram based on Coda Hale's Golang implementation.
+ * https://github.com/codahale/hdr_histogram
+ *
+ *
+ * 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.
+ *
+ *
+ */
+
+#include "rd.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "rdhdrhistogram.h"
+#include "rdunittest.h"
+#include "rdfloat.h"
+
+void rd_hdr_histogram_destroy(rd_hdr_histogram_t *hdr) {
+ rd_free(hdr);
+}
+
+rd_hdr_histogram_t *rd_hdr_histogram_new(int64_t minValue,
+ int64_t maxValue,
+ int significantFigures) {
+ rd_hdr_histogram_t *hdr;
+ int64_t largestValueWithSingleUnitResolution;
+ int32_t subBucketCountMagnitude;
+ int32_t subBucketHalfCountMagnitude;
+ int32_t unitMagnitude;
+ int32_t subBucketCount;
+ int32_t subBucketHalfCount;
+ int64_t subBucketMask;
+ int64_t smallestUntrackableValue;
+ int32_t bucketsNeeded = 1;
+ int32_t bucketCount;
+ int32_t countsLen;
+
+ if (significantFigures < 1 || significantFigures > 5)
+ return NULL;
+
+ largestValueWithSingleUnitResolution =
+ (int64_t)(2.0 * pow(10.0, (double)significantFigures));
+
+ subBucketCountMagnitude =
+ (int32_t)ceil(log2((double)largestValueWithSingleUnitResolution));
+
+ subBucketHalfCountMagnitude = RD_MAX(subBucketCountMagnitude, 1) - 1;
+
+ unitMagnitude = (int32_t)RD_MAX(floor(log2((double)minValue)), 0);
+
+ subBucketCount =
+ (int32_t)pow(2, (double)subBucketHalfCountMagnitude + 1.0);
+
+ subBucketHalfCount = subBucketCount / 2;
+
+ subBucketMask = (int64_t)(subBucketCount - 1) << unitMagnitude;
+
+ /* Determine exponent range needed to support the trackable
+ * value with no overflow: */
+ smallestUntrackableValue = (int64_t)subBucketCount << unitMagnitude;
+ while (smallestUntrackableValue < maxValue) {
+ smallestUntrackableValue <<= 1;
+ bucketsNeeded++;
+ }
+
+ bucketCount = bucketsNeeded;
+ countsLen = (bucketCount + 1) * (subBucketCount / 2);
+ hdr = rd_calloc(1, sizeof(*hdr) + (sizeof(*hdr->counts) * countsLen));
+ hdr->counts = (int64_t *)(hdr + 1);
+ hdr->allocatedSize = sizeof(*hdr) + (sizeof(*hdr->counts) * countsLen);
+
+ hdr->lowestTrackableValue = minValue;
+ hdr->highestTrackableValue = maxValue;
+ hdr->unitMagnitude = unitMagnitude;
+ hdr->significantFigures = significantFigures;
+ hdr->subBucketHalfCountMagnitude = subBucketHalfCountMagnitude;
+ hdr->subBucketHalfCount = subBucketHalfCount;
+ hdr->subBucketMask = subBucketMask;
+ hdr->subBucketCount = subBucketCount;
+ hdr->bucketCount = bucketCount;
+ hdr->countsLen = countsLen;
+ hdr->totalCount = 0;
+ hdr->lowestOutOfRange = minValue;
+ hdr->highestOutOfRange = maxValue;
+
+ return hdr;
+}
+
+/**
+ * @brief Deletes all recorded values and resets histogram.
+ */
+void rd_hdr_histogram_reset(rd_hdr_histogram_t *hdr) {
+ int32_t i;
+ hdr->totalCount = 0;
+ for (i = 0; i < hdr->countsLen; i++)
+ hdr->counts[i] = 0;
+}
+
+
+
+static RD_INLINE int32_t rd_hdr_countsIndex(const rd_hdr_histogram_t *hdr,
+ int32_t bucketIdx,
+ int32_t subBucketIdx) {
+ int32_t bucketBaseIdx = (bucketIdx + 1)
+ << hdr->subBucketHalfCountMagnitude;
+ int32_t offsetInBucket = subBucketIdx - hdr->subBucketHalfCount;
+ return bucketBaseIdx + offsetInBucket;
+}
+
+static RD_INLINE int64_t rd_hdr_getCountAtIndex(const rd_hdr_histogram_t *hdr,
+ int32_t bucketIdx,
+ int32_t subBucketIdx) {
+ return hdr->counts[rd_hdr_countsIndex(hdr, bucketIdx, subBucketIdx)];
+}
+
+
+static RD_INLINE int64_t bitLen(int64_t x) {
+ int64_t n = 0;
+ for (; x >= 0x8000; x >>= 16)
+ n += 16;
+ if (x >= 0x80) {
+ x >>= 8;
+ n += 8;
+ }
+ if (x >= 0x8) {
+ x >>= 4;
+ n += 4;
+ }
+ if (x >= 0x2) {
+ x >>= 2;
+ n += 2;
+ }
+ if (x >= 0x1)
+ n++;
+ return n;
+}
+
+
+static RD_INLINE int32_t rd_hdr_getBucketIndex(const rd_hdr_histogram_t *hdr,
+ int64_t v) {
+ int64_t pow2Ceiling = bitLen(v | hdr->subBucketMask);
+ return (int32_t)(pow2Ceiling - (int64_t)hdr->unitMagnitude -
+ (int64_t)(hdr->subBucketHalfCountMagnitude + 1));
+}
+
+static RD_INLINE int32_t rd_hdr_getSubBucketIdx(const rd_hdr_histogram_t *hdr,
+ int64_t v,
+ int32_t idx) {
+ return (int32_t)(v >> ((int64_t)idx + (int64_t)hdr->unitMagnitude));
+}
+
+static RD_INLINE int64_t rd_hdr_valueFromIndex(const rd_hdr_histogram_t *hdr,
+ int32_t bucketIdx,
+ int32_t subBucketIdx) {
+ return (int64_t)subBucketIdx
+ << ((int64_t)bucketIdx + hdr->unitMagnitude);
+}
+
+static RD_INLINE int64_t
+rd_hdr_sizeOfEquivalentValueRange(const rd_hdr_histogram_t *hdr, int64_t v) {
+ int32_t bucketIdx = rd_hdr_getBucketIndex(hdr, v);
+ int32_t subBucketIdx = rd_hdr_getSubBucketIdx(hdr, v, bucketIdx);
+ int32_t adjustedBucket = bucketIdx;
+ if (unlikely(subBucketIdx >= hdr->subBucketCount))
+ adjustedBucket++;
+ return (int64_t)1 << (hdr->unitMagnitude + (int64_t)adjustedBucket);
+}
+
+static RD_INLINE int64_t
+rd_hdr_lowestEquivalentValue(const rd_hdr_histogram_t *hdr, int64_t v) {
+ int32_t bucketIdx = rd_hdr_getBucketIndex(hdr, v);
+ int32_t subBucketIdx = rd_hdr_getSubBucketIdx(hdr, v, bucketIdx);
+ return rd_hdr_valueFromIndex(hdr, bucketIdx, subBucketIdx);
+}
+
+
+static RD_INLINE int64_t
+rd_hdr_nextNonEquivalentValue(const rd_hdr_histogram_t *hdr, int64_t v) {
+ return rd_hdr_lowestEquivalentValue(hdr, v) +
+ rd_hdr_sizeOfEquivalentValueRange(hdr, v);
+}
+
+
+static RD_INLINE int64_t
+rd_hdr_highestEquivalentValue(const rd_hdr_histogram_t *hdr, int64_t v) {
+ return rd_hdr_nextNonEquivalentValue(hdr, v) - 1;
+}
+
+static RD_INLINE int64_t
+rd_hdr_medianEquivalentValue(const rd_hdr_histogram_t *hdr, int64_t v) {
+ return rd_hdr_lowestEquivalentValue(hdr, v) +
+ (rd_hdr_sizeOfEquivalentValueRange(hdr, v) >> 1);
+}
+
+
+static RD_INLINE int32_t rd_hdr_countsIndexFor(const rd_hdr_histogram_t *hdr,
+ int64_t v) {
+ int32_t bucketIdx = rd_hdr_getBucketIndex(hdr, v);
+ int32_t subBucketIdx = rd_hdr_getSubBucketIdx(hdr, v, bucketIdx);
+ return rd_hdr_countsIndex(hdr, bucketIdx, subBucketIdx);
+}
+
+
+
+typedef struct rd_hdr_iter_s {
+ const rd_hdr_histogram_t *hdr;
+ int bucketIdx;
+ int subBucketIdx;
+ int64_t countAtIdx;
+ int64_t countToIdx;
+ int64_t valueFromIdx;
+ int64_t highestEquivalentValue;
+} rd_hdr_iter_t;
+
+#define RD_HDR_ITER_INIT(hdr) \
+ { .hdr = hdr, .subBucketIdx = -1 }
+
+static int rd_hdr_iter_next(rd_hdr_iter_t *it) {
+ const rd_hdr_histogram_t *hdr = it->hdr;
+
+ if (unlikely(it->countToIdx >= hdr->totalCount))
+ return 0;
+
+ it->subBucketIdx++;
+ if (unlikely(it->subBucketIdx >= hdr->subBucketCount)) {
+ it->subBucketIdx = hdr->subBucketHalfCount;
+ it->bucketIdx++;
+ }
+
+ if (unlikely(it->bucketIdx >= hdr->bucketCount))
+ return 0;
+
+ it->countAtIdx =
+ rd_hdr_getCountAtIndex(hdr, it->bucketIdx, it->subBucketIdx);
+ it->countToIdx += it->countAtIdx;
+ it->valueFromIdx =
+ rd_hdr_valueFromIndex(hdr, it->bucketIdx, it->subBucketIdx);
+ it->highestEquivalentValue =
+ rd_hdr_highestEquivalentValue(hdr, it->valueFromIdx);
+
+ return 1;
+}
+
+
+double rd_hdr_histogram_stddev(rd_hdr_histogram_t *hdr) {
+ double mean;
+ double geometricDevTotal = 0.0;
+ rd_hdr_iter_t it = RD_HDR_ITER_INIT(hdr);
+
+ if (hdr->totalCount == 0)
+ return 0;
+
+ mean = rd_hdr_histogram_mean(hdr);
+
+
+ while (rd_hdr_iter_next(&it)) {
+ double dev;
+
+ if (it.countAtIdx == 0)
+ continue;
+
+ dev =
+ (double)rd_hdr_medianEquivalentValue(hdr, it.valueFromIdx) -
+ mean;
+ geometricDevTotal += (dev * dev) * (double)it.countAtIdx;
+ }
+
+ return sqrt(geometricDevTotal / (double)hdr->totalCount);
+}
+
+
+/**
+ * @returns the approximate maximum recorded value.
+ */
+int64_t rd_hdr_histogram_max(const rd_hdr_histogram_t *hdr) {
+ int64_t vmax = 0;
+ rd_hdr_iter_t it = RD_HDR_ITER_INIT(hdr);
+
+ while (rd_hdr_iter_next(&it)) {
+ if (it.countAtIdx != 0)
+ vmax = it.highestEquivalentValue;
+ }
+ return rd_hdr_highestEquivalentValue(hdr, vmax);
+}
+
+/**
+ * @returns the approximate minimum recorded value.
+ */
+int64_t rd_hdr_histogram_min(const rd_hdr_histogram_t *hdr) {
+ int64_t vmin = 0;
+ rd_hdr_iter_t it = RD_HDR_ITER_INIT(hdr);
+
+ while (rd_hdr_iter_next(&it)) {
+ if (it.countAtIdx != 0 && vmin == 0) {
+ vmin = it.highestEquivalentValue;
+ break;
+ }
+ }
+ return rd_hdr_lowestEquivalentValue(hdr, vmin);
+}
+
+/**
+ * @returns the approximate arithmetic mean of the recorded values.
+ */
+double rd_hdr_histogram_mean(const rd_hdr_histogram_t *hdr) {
+ int64_t total = 0;
+ rd_hdr_iter_t it = RD_HDR_ITER_INIT(hdr);
+
+ if (hdr->totalCount == 0)
+ return 0.0;
+
+ while (rd_hdr_iter_next(&it)) {
+ if (it.countAtIdx != 0)
+ total += it.countAtIdx * rd_hdr_medianEquivalentValue(
+ hdr, it.valueFromIdx);
+ }
+ return (double)total / (double)hdr->totalCount;
+}
+
+
+
+/**
+ * @brief Records the given value.
+ *
+ * @returns 1 if value was recorded or 0 if value is out of range.
+ */
+
+int rd_hdr_histogram_record(rd_hdr_histogram_t *hdr, int64_t v) {
+ int32_t idx = rd_hdr_countsIndexFor(hdr, v);
+
+ if (idx < 0 || hdr->countsLen <= idx) {
+ hdr->outOfRangeCount++;
+ if (v > hdr->highestOutOfRange)
+ hdr->highestOutOfRange = v;
+ if (v < hdr->lowestOutOfRange)
+ hdr->lowestOutOfRange = v;
+ return 0;
+ }
+
+ hdr->counts[idx]++;
+ hdr->totalCount++;
+
+ return 1;
+}
+
+
+/**
+ * @returns the recorded value at the given quantile (0..100).
+ */
+int64_t rd_hdr_histogram_quantile(const rd_hdr_histogram_t *hdr, double q) {
+ int64_t total = 0;
+ int64_t countAtPercentile;
+ rd_hdr_iter_t it = RD_HDR_ITER_INIT(hdr);
+
+ if (q > 100.0)
+ q = 100.0;
+
+ countAtPercentile =
+ (int64_t)(((q / 100.0) * (double)hdr->totalCount) + 0.5);
+
+ while (rd_hdr_iter_next(&it)) {
+ total += it.countAtIdx;
+ if (total >= countAtPercentile)
+ return rd_hdr_highestEquivalentValue(hdr,
+ it.valueFromIdx);
+ }
+
+ return 0;
+}
+
+
+
+/**
+ * @name Unit tests
+ * @{
+ *
+ *
+ *
+ */
+
+/**
+ * @returns 0 on success or 1 on failure.
+ */
+static int ut_high_sigfig(void) {
+ rd_hdr_histogram_t *hdr;
+ const int64_t input[] = {
+ 459876, 669187, 711612, 816326, 931423,
+ 1033197, 1131895, 2477317, 3964974, 12718782,
+ };
+ size_t i;
+ int64_t v;
+ const int64_t exp = 1048575;
+
+ hdr = rd_hdr_histogram_new(459876, 12718782, 5);
+ for (i = 0; i < RD_ARRAYSIZE(input); i++) {
+ /* Ignore errors (some should fail) */
+ rd_hdr_histogram_record(hdr, input[i]);
+ }
+
+ v = rd_hdr_histogram_quantile(hdr, 50);
+ RD_UT_ASSERT(v == exp, "Median is %" PRId64 ", expected %" PRId64, v,
+ exp);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+static int ut_quantile(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10000000, 3);
+ size_t i;
+ const struct {
+ double q;
+ int64_t v;
+ } exp[] = {
+ {50, 500223}, {75, 750079}, {90, 900095}, {95, 950271},
+ {99, 990207}, {99.9, 999423}, {99.99, 999935},
+ };
+
+ for (i = 0; i < 1000000; i++) {
+ int r = rd_hdr_histogram_record(hdr, (int64_t)i);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", (int64_t)i);
+ }
+
+ for (i = 0; i < RD_ARRAYSIZE(exp); i++) {
+ int64_t v = rd_hdr_histogram_quantile(hdr, exp[i].q);
+ RD_UT_ASSERT(v == exp[i].v,
+ "P%.2f is %" PRId64 ", expected %" PRId64,
+ exp[i].q, v, exp[i].v);
+ }
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+static int ut_mean(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10000000, 3);
+ size_t i;
+ const double exp = 500000.013312;
+ double v;
+
+ for (i = 0; i < 1000000; i++) {
+ int r = rd_hdr_histogram_record(hdr, (int64_t)i);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", (int64_t)i);
+ }
+
+ v = rd_hdr_histogram_mean(hdr);
+ RD_UT_ASSERT(rd_dbl_eq0(v, exp, 0.0000001), "Mean is %f, expected %f",
+ v, exp);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+
+static int ut_stddev(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10000000, 3);
+ size_t i;
+ const double exp = 288675.140368;
+ const double epsilon = 0.000001;
+ double v;
+
+ for (i = 0; i < 1000000; i++) {
+ int r = rd_hdr_histogram_record(hdr, (int64_t)i);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", (int64_t)i);
+ }
+
+ v = rd_hdr_histogram_stddev(hdr);
+ RD_UT_ASSERT(rd_dbl_eq0(v, exp, epsilon),
+ "StdDev is %.6f, expected %.6f: diff %.6f vs epsilon %.6f",
+ v, exp, fabs(v - exp), epsilon);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+static int ut_totalcount(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10000000, 3);
+ int64_t i;
+
+ for (i = 0; i < 1000000; i++) {
+ int64_t v;
+ int r = rd_hdr_histogram_record(hdr, i);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", i);
+
+ v = hdr->totalCount;
+ RD_UT_ASSERT(v == i + 1,
+ "total_count is %" PRId64 ", expected %" PRId64, v,
+ i + 1);
+ }
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+
+static int ut_max(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10000000, 3);
+ int64_t i, v;
+ const int64_t exp = 1000447;
+
+ for (i = 0; i < 1000000; i++) {
+ int r = rd_hdr_histogram_record(hdr, i);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", i);
+ }
+
+ v = rd_hdr_histogram_max(hdr);
+ RD_UT_ASSERT(v == exp, "Max is %" PRId64 ", expected %" PRId64, v, exp);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+static int ut_min(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10000000, 3);
+ int64_t i, v;
+ const int64_t exp = 0;
+
+ for (i = 0; i < 1000000; i++) {
+ int r = rd_hdr_histogram_record(hdr, i);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", i);
+ }
+
+ v = rd_hdr_histogram_min(hdr);
+ RD_UT_ASSERT(v == exp, "Min is %" PRId64 ", expected %" PRId64, v, exp);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+static int ut_reset(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10000000, 3);
+ int64_t i, v;
+ const int64_t exp = 0;
+
+ for (i = 0; i < 1000000; i++) {
+ int r = rd_hdr_histogram_record(hdr, i);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", i);
+ }
+
+ rd_hdr_histogram_reset(hdr);
+
+ v = rd_hdr_histogram_max(hdr);
+ RD_UT_ASSERT(v == exp, "Max is %" PRId64 ", expected %" PRId64, v, exp);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+
+static int ut_nan(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 100000, 3);
+ double v;
+
+ v = rd_hdr_histogram_mean(hdr);
+ RD_UT_ASSERT(!isnan(v), "Mean is %f, expected NaN", v);
+ v = rd_hdr_histogram_stddev(hdr);
+ RD_UT_ASSERT(!isnan(v), "StdDev is %f, expected NaN", v);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+
+static int ut_sigfigs(void) {
+ int sigfigs;
+
+ for (sigfigs = 1; sigfigs <= 5; sigfigs++) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(1, 10, sigfigs);
+ RD_UT_ASSERT(hdr->significantFigures == sigfigs,
+ "Significant figures is %" PRId64 ", expected %d",
+ hdr->significantFigures, sigfigs);
+ rd_hdr_histogram_destroy(hdr);
+ }
+
+ RD_UT_PASS();
+}
+
+static int ut_minmax_trackable(void) {
+ const int64_t minval = 2;
+ const int64_t maxval = 11;
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(minval, maxval, 3);
+
+ RD_UT_ASSERT(hdr->lowestTrackableValue == minval,
+ "lowestTrackableValue is %" PRId64 ", expected %" PRId64,
+ hdr->lowestTrackableValue, minval);
+ RD_UT_ASSERT(hdr->highestTrackableValue == maxval,
+ "highestTrackableValue is %" PRId64 ", expected %" PRId64,
+ hdr->highestTrackableValue, maxval);
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+
+static int ut_unitmagnitude_overflow(void) {
+ rd_hdr_histogram_t *hdr = rd_hdr_histogram_new(0, 200, 4);
+ int r = rd_hdr_histogram_record(hdr, 11);
+ RD_UT_ASSERT(r, "record(11) failed\n");
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+static int ut_subbucketmask_overflow(void) {
+ rd_hdr_histogram_t *hdr;
+ const int64_t input[] = {(int64_t)1e8, (int64_t)2e7, (int64_t)3e7};
+ const struct {
+ double q;
+ int64_t v;
+ } exp[] = {
+ {50, 33554431},
+ {83.33, 33554431},
+ {83.34, 100663295},
+ {99, 100663295},
+ };
+ size_t i;
+
+ hdr = rd_hdr_histogram_new((int64_t)2e7, (int64_t)1e8, 5);
+
+ for (i = 0; i < RD_ARRAYSIZE(input); i++) {
+ /* Ignore errors (some should fail) */
+ int r = rd_hdr_histogram_record(hdr, input[i]);
+ RD_UT_ASSERT(r, "record(%" PRId64 ") failed\n", input[i]);
+ }
+
+ for (i = 0; i < RD_ARRAYSIZE(exp); i++) {
+ int64_t v = rd_hdr_histogram_quantile(hdr, exp[i].q);
+ RD_UT_ASSERT(v == exp[i].v,
+ "P%.2f is %" PRId64 ", expected %" PRId64,
+ exp[i].q, v, exp[i].v);
+ }
+
+ rd_hdr_histogram_destroy(hdr);
+ RD_UT_PASS();
+}
+
+
+int unittest_rdhdrhistogram(void) {
+ int fails = 0;
+
+ fails += ut_high_sigfig();
+ fails += ut_quantile();
+ fails += ut_mean();
+ fails += ut_stddev();
+ fails += ut_totalcount();
+ fails += ut_max();
+ fails += ut_min();
+ fails += ut_reset();
+ fails += ut_nan();
+ fails += ut_sigfigs();
+ fails += ut_minmax_trackable();
+ fails += ut_unitmagnitude_overflow();
+ fails += ut_subbucketmask_overflow();
+
+ return fails;
+}
+
+/**@}*/