--- /dev/null
+/* Copyright (c) 2007-2015 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "timing.h"
+
+#include <stdlib.h>
+
+static int uint64_cmp(const uint64_t *i1, const uint64_t *i2)
+{
+ if (*i1 < *i2)
+ return -1;
+ else if (*i1 > *i2)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+test_timing_verify(const struct timing *t, const int64_t *input,
+ unsigned int input_size)
+{
+ uint64_t min = INT_MAX, max = 0, sum = 0;
+ uint64_t *copy;
+ unsigned int i;
+
+ copy = i_new(uint64_t, input_size);
+ for (i = 0; i < input_size; i++) {
+ uint64_t value = input[i];
+
+ if (min > value)
+ min = value;
+ if (max < value)
+ max = value;
+ sum += value;
+ copy[i] = value;
+ }
+ i_qsort(copy, input_size, sizeof(*copy), uint64_cmp);
+
+ test_assert_idx(timing_get_count(t) == input_size, input_size);
+ test_assert_idx(timing_get_min(t) == min, input_size);
+ test_assert_idx(timing_get_max(t) == max, input_size);
+ test_assert_idx(timing_get_avg(t) == sum/input_size, input_size);
+
+ i_free(copy);
+}
+
+void test_timing(void)
+{
+ static int64_t test_input1[] = {
+ 20, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, -1
+ };
+ static int64_t test_input2[] = {
+ 20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, -1
+ };
+ static int64_t test_input3[] = {
+ 20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 22, -1
+ };
+ static int64_t *test_inputs[] = {
+ test_input1, test_input2, test_input3
+ };
+ struct timing *t;
+ unsigned int i, j;
+
+ for (i = 0; i < N_ELEMENTS(test_inputs); i++) {
+ test_begin(t_strdup_printf("timings %u", i));
+ t = timing_init();
+ for (j = 0; test_inputs[i][j] >= 0; j++) {
+ timing_add_usecs(t, test_inputs[i][j]);
+ test_timing_verify(t, test_inputs[i], j+1);
+ }
+ timing_deinit(&t);
+ test_end();
+ }
+}
--- /dev/null
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "bsearch-insert-pos.h"
+#include "timing.h"
+
+#define TIMING_MAX_BUCKET_COUNT 20
+
+struct timing {
+ unsigned int count;
+ uint64_t min, max, sum;
+};
+
+struct timing *timing_init(void)
+{
+ return i_new(struct timing, 1);
+}
+
+void timing_deinit(struct timing **_timing)
+{
+ i_free_and_null(*_timing);
+}
+
+
+void timing_add_usecs(struct timing *timing, uint64_t usecs)
+{
+ if (timing->count++ == 0) {
+ timing->min = timing->max = timing->sum = usecs;
+ } else {
+ if (timing->min > usecs)
+ timing->min = usecs;
+ if (timing->max < usecs)
+ timing->max = usecs;
+ timing->sum += usecs;
+ }
+}
+
+unsigned int timing_get_count(const struct timing *timing)
+{
+ return timing->count;
+}
+
+uint64_t timing_get_min(const struct timing *timing)
+{
+ return timing->min;
+}
+
+uint64_t timing_get_max(const struct timing *timing)
+{
+ return timing->max;
+}
+
+uint64_t timing_get_avg(const struct timing *timing)
+{
+ if (timing->count == 0)
+ return 0;
+
+ return timing->sum / timing->count;
+}
--- /dev/null
+#ifndef TIMING_H
+#define TIMING_H
+
+struct timing *timing_init(void);
+void timing_deinit(struct timing **timing);
+
+/* Add a new event that took the specified number of usecs. */
+void timing_add_usecs(struct timing *timing, uint64_t usecs);
+
+/* Returns number of events added. */
+unsigned int timing_get_count(const struct timing *timing);
+
+/* Returns events' minimum. */
+uint64_t timing_get_min(const struct timing *timing);
+/* Returns events' maximum. */
+uint64_t timing_get_max(const struct timing *timing);
+/* Returns events' average. */
+uint64_t timing_get_avg(const struct timing *timing);
+
+#endif