summaryrefslogtreecommitdiffstats
path: root/src/lib-old-stats/stats.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-old-stats/stats.c')
-rw-r--r--src/lib-old-stats/stats.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/src/lib-old-stats/stats.c b/src/lib-old-stats/stats.c
new file mode 100644
index 0000000..284de26
--- /dev/null
+++ b/src/lib-old-stats/stats.c
@@ -0,0 +1,229 @@
+/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "stats.h"
+
+struct stats_item {
+ struct stats_vfuncs v;
+ size_t pos;
+};
+
+static ARRAY(struct stats_item *) stats_items = ARRAY_INIT;
+static unsigned int stats_total_size = 0;
+static bool stats_allocated = FALSE;
+
+struct stats_item *stats_register(const struct stats_vfuncs *vfuncs)
+{
+ struct stats_item *item;
+
+ if (stats_allocated)
+ i_panic("stats_register() called after stats_alloc_size() was already called - this will break existing allocations");
+
+ if (!array_is_created(&stats_items))
+ i_array_init(&stats_items, 8);
+
+ item = i_new(struct stats_item, 1);
+ item->v = *vfuncs;
+ item->pos = stats_total_size;
+ array_push_back(&stats_items, &item);
+
+ stats_total_size += vfuncs->alloc_size();
+ return item;
+}
+
+static bool stats_item_find(struct stats_item *item, unsigned int *idx_r)
+{
+ struct stats_item *const *itemp;
+
+ array_foreach(&stats_items, itemp) {
+ if (*itemp == item) {
+ *idx_r = array_foreach_idx(&stats_items, itemp);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static struct stats_item *stats_item_find_by_name(const char *name)
+{
+ struct stats_item *item;
+
+ array_foreach_elem(&stats_items, item) {
+ if (strcmp(item->v.short_name, name) == 0)
+ return item;
+ }
+ return NULL;
+}
+
+void stats_unregister(struct stats_item **_item)
+{
+ struct stats_item *item = *_item;
+ unsigned int idx;
+
+ *_item = NULL;
+
+ if (!stats_item_find(item, &idx))
+ i_unreached();
+ array_delete(&stats_items, idx, 1);
+
+ i_free(item);
+ if (array_count(&stats_items) == 0) {
+ array_free(&stats_items);
+ /* all stats should have been freed by now. allow
+ re-registering and using stats. */
+ stats_allocated = FALSE;
+ }
+}
+
+struct stats *stats_alloc(pool_t pool)
+{
+ return p_malloc(pool, stats_alloc_size());
+}
+
+size_t stats_alloc_size(void)
+{
+ stats_allocated = TRUE;
+ return stats_total_size;
+}
+
+void stats_copy(struct stats *dest, const struct stats *src)
+{
+ memcpy(dest, src, stats_total_size);
+}
+
+unsigned int stats_field_count(void)
+{
+ struct stats_item *item;
+ unsigned int count = 0;
+
+ array_foreach_elem(&stats_items, item)
+ count += item->v.field_count();
+ return count;
+}
+
+const char *stats_field_name(unsigned int n)
+{
+ struct stats_item *item;
+ unsigned int i = 0, count;
+
+ array_foreach_elem(&stats_items, item) {
+ count = item->v.field_count();
+ if (i + count > n)
+ return item->v.field_name(n - i);
+ i += count;
+ }
+ i_unreached();
+}
+
+void stats_field_value(string_t *str, const struct stats *stats,
+ unsigned int n)
+{
+ struct stats_item *item;
+ unsigned int i = 0, count;
+
+ array_foreach_elem(&stats_items, item) {
+ count = item->v.field_count();
+ if (i + count > n) {
+ const void *item_stats =
+ CONST_PTR_OFFSET(stats, item->pos);
+ item->v.field_value(str, item_stats, n - i);
+ return;
+ }
+ i += count;
+ }
+ i_unreached();
+}
+
+bool stats_diff(const struct stats *stats1, const struct stats *stats2,
+ struct stats *diff_stats_r, const char **error_r)
+{
+ struct stats_item *item;
+ bool ret = TRUE;
+
+ array_foreach_elem(&stats_items, item) {
+ if (!item->v.diff(CONST_PTR_OFFSET(stats1, item->pos),
+ CONST_PTR_OFFSET(stats2, item->pos),
+ PTR_OFFSET(diff_stats_r, item->pos),
+ error_r))
+ ret = FALSE;
+ }
+ return ret;
+}
+
+void stats_add(struct stats *dest, const struct stats *src)
+{
+ struct stats_item *item;
+
+ array_foreach_elem(&stats_items, item) {
+ item->v.add(PTR_OFFSET(dest, item->pos),
+ CONST_PTR_OFFSET(src, item->pos));
+ }
+}
+
+bool stats_have_changed(const struct stats *prev, const struct stats *cur)
+{
+ struct stats_item *item;
+
+ array_foreach_elem(&stats_items, item) {
+ if (item->v.have_changed(CONST_PTR_OFFSET(prev, item->pos),
+ CONST_PTR_OFFSET(cur, item->pos)))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void stats_export(buffer_t *buf, const struct stats *stats)
+{
+ struct stats_item *item;
+
+ array_foreach_elem(&stats_items, item) {
+ buffer_append(buf, item->v.short_name,
+ strlen(item->v.short_name)+1);
+ item->v.export(buf, CONST_PTR_OFFSET(stats, item->pos));
+ }
+}
+
+bool stats_import(const unsigned char *data, size_t size,
+ const struct stats *old_stats, struct stats *stats,
+ const char **error_r)
+{
+ struct stats_item *item;
+ const unsigned char *p;
+ size_t pos;
+
+ memcpy(stats, old_stats, stats_total_size);
+ while (size > 0) {
+ const char *next_name = (const void *)data;
+
+ p = memchr(data, '\0', size);
+ if (p == NULL) {
+ *error_r = "Expected name, but NUL is missing";
+ return FALSE;
+ }
+ item = stats_item_find_by_name(next_name);
+ if (item == NULL) {
+ *error_r = t_strdup_printf("Unknown stats name: '%s'", next_name);
+ return FALSE;
+ }
+ size -= (p+1) - data;
+ data = p+1;
+ if (!item->v.import(data, size, &pos,
+ PTR_OFFSET(stats, item->pos), error_r))
+ return FALSE;
+ i_assert(pos <= size);
+ data += pos;
+ size -= pos;
+ }
+ return TRUE;
+}
+
+void *stats_fill_ptr(struct stats *stats, struct stats_item *item)
+{
+ return PTR_OFFSET(stats, item->pos);
+}
+
+void stats_reset(struct stats *stats)
+{
+ memset(stats, 0, stats_total_size);
+}