--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/statsmulti.h */
+
+#include <inttypes.h>
+
+#include <isc/types.h>
+
+typedef struct isc_statsmulti isc_statsmulti_t; /*%< Statistics Multi */
+
+/*%<
+ * Flag(s) for isc_statsmulti_dump().
+ */
+#define ISC_STATSMULTIDUMP_VERBOSE 0x00000001 /*%< dump 0-value counters */
+
+/*%<
+ * Dump callback type.
+ */
+typedef void (*isc_statsmulti_dumper_t)(isc_statscounter_t, uint64_t, void *);
+
+void
+isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, int ncounters);
+/*%<
+ * Create a statistics counter structure of general type. It counts a general
+ * set of counters indexed by an ID between 0 and ncounters -1.
+ *
+ * Requires:
+ *\li 'mctx' must be a valid memory context.
+ *
+ *\li 'statsp' != NULL && '*statsp' == NULL.
+ */
+
+void
+isc_statsmulti_attach(isc_statsmulti_t *stats, isc_statsmulti_t **statsp);
+/*%<
+ * Attach to a statistics set.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_statsmulti_t.
+ *
+ *\li 'statsp' != NULL && '*statsp' == NULL
+ */
+
+void
+isc_statsmulti_detach(isc_statsmulti_t **statsp);
+/*%<
+ * Detaches from the statistics set.
+ *
+ * Requires:
+ *\li 'statsp' != NULL and '*statsp' is a valid isc_statsmulti_t.
+ */
+
+isc_statscounter_t
+isc_statsmulti_increment(isc_statsmulti_t *stats, isc_statscounter_t counter);
+/*%<
+ * Increment the counter-th counter of stats and return the old value.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_statsmulti_t.
+ *
+ *\li counter is less than the maximum available ID for the stats specified
+ * on creation.
+ */
+
+void
+isc_statsmulti_decrement(isc_statsmulti_t *stats, isc_statscounter_t counter);
+/*%<
+ * Decrement the counter-th counter of stats.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_statsmulti_t.
+ */
+
+void
+isc_statsmulti_dump(isc_statsmulti_t *stats, isc_statsmulti_dumper_t dump_fn, void *arg,
+ unsigned int options);
+/*%<
+ * Dump the current statistics counters in a specified way. For each counter
+ * in stats, dump_fn is called with its current value and the given argument
+ * arg. By default counters that have a value of 0 is skipped; if options has
+ * the ISC_STATSMULTIDUMP_VERBOSE flag, even such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_statsmulti_t.
+ */
+
+isc_statscounter_t
+isc_statsmulti_get_counter(isc_statsmulti_t *stats, isc_statscounter_t counter);
+/*%<
+ * Returns value currently stored in counter.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_statsmulti_t.
+ *
+ *\li counter is less than the maximum available ID for the stats specified
+ * on creation.
+ */
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/atomic.h>
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/refcount.h>
+#include <isc/stats.h>
+#include <isc/statsmulti.h>
+#include <isc/util.h>
+
+#define ISC_STATSMULTI_MAGIC ISC_MAGIC('S', 'M', 'u', 'l')
+#define ISC_STATSMULTI_VALID(x) ISC_MAGIC_VALID(x, ISC_STATSMULTI_MAGIC)
+
+/*
+ * Same constraint as stats.c
+ */
+STATIC_ASSERT(sizeof(isc_statscounter_t) <= sizeof(uint64_t),
+ "Exported statistics must fit into the statistic counter size");
+
+struct isc_statsmulti {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_refcount_t references;
+ int ncounters;
+ isc_atomic_statscounter_t *counters;
+};
+
+void
+isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, int ncounters) {
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ isc_statsmulti_t *stats = isc_mem_get(mctx, sizeof(*stats));
+ size_t counters_alloc_size = sizeof(isc_atomic_statscounter_t) *
+ ncounters;
+ stats->counters = isc_mem_get(mctx, counters_alloc_size);
+ isc_refcount_init(&stats->references, 1);
+ for (int i = 0; i < ncounters; i++) {
+ atomic_init(&stats->counters[i], 0);
+ }
+ stats->mctx = NULL;
+ isc_mem_attach(mctx, &stats->mctx);
+ stats->ncounters = ncounters;
+ stats->magic = ISC_STATSMULTI_MAGIC;
+ *statsp = stats;
+}
+
+void
+isc_statsmulti_attach(isc_statsmulti_t *stats, isc_statsmulti_t **statsp) {
+ REQUIRE(ISC_STATSMULTI_VALID(stats));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ isc_refcount_increment(&stats->references);
+ *statsp = stats;
+}
+
+void
+isc_statsmulti_detach(isc_statsmulti_t **statsp) {
+ isc_statsmulti_t *stats;
+
+ REQUIRE(statsp != NULL && ISC_STATSMULTI_VALID(*statsp));
+
+ stats = *statsp;
+ *statsp = NULL;
+
+ if (isc_refcount_decrement(&stats->references) == 1) {
+ isc_refcount_destroy(&stats->references);
+ isc_mem_cput(stats->mctx, stats->counters, stats->ncounters,
+ sizeof(isc_atomic_statscounter_t));
+ isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
+ }
+}
+
+isc_statscounter_t
+isc_statsmulti_increment(isc_statsmulti_t *stats, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATSMULTI_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+
+ return atomic_fetch_add_relaxed(&stats->counters[counter], 1);
+}
+
+void
+isc_statsmulti_decrement(isc_statsmulti_t *stats, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATSMULTI_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+#if ISC_STATS_CHECKUNDERFLOW
+ REQUIRE(atomic_fetch_sub_release(&stats->counters[counter], 1) > 0);
+#else
+ atomic_fetch_sub_release(&stats->counters[counter], 1);
+#endif
+}
+
+void
+isc_statsmulti_dump(isc_statsmulti_t *stats, isc_statsmulti_dumper_t dump_fn, void *arg,
+ unsigned int options) {
+ int i;
+
+ REQUIRE(ISC_STATSMULTI_VALID(stats));
+
+ for (i = 0; i < stats->ncounters; i++) {
+ isc_statscounter_t counter =
+ atomic_load_acquire(&stats->counters[i]);
+ if ((options & ISC_STATSMULTIDUMP_VERBOSE) == 0 && counter == 0) {
+ continue;
+ }
+ dump_fn((isc_statscounter_t)i, counter, arg);
+ }
+}
+
+isc_statscounter_t
+isc_statsmulti_get_counter(isc_statsmulti_t *stats, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATSMULTI_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+
+ return atomic_load_acquire(&stats->counters[counter]);
+}